import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import random
from collections import namedtuple
%matplotlib inline
NUM_GAMES = 10000
NUM_ROUNDS = 100
results = []
NUM_PROPERTIES = 40
GO = 0
GO_TO_JAIL = 30
JAIL = 10
BOARDWALK = 39
READING = 5
ILLINOIS = 25
ST_CHARLES = 11
CHANCE = (7, 22, 36)
COMMUNITY_CHEST = (2, 17, 33)
UTILITIES = (12, 28)
RAILROADS = (5, 15, 25, 35)
Property = namedtuple('Property', ['name', 'color', 'low_rent', 'high_rent', 'cost', 'full_cost'])
properties = [
Property('Go', 'None', 0, 0, 1, 1), # Non-purchasable have cost of 1 to prevent div-zero errors later.
Property('Mediterranean Avenue', 'Dark Purple', 2, 250, 60, 310),
Property('Community Chest', 'None', 0, 0, 1, 1),
Property('Baltic Avenue', 'Dark Purple', 4, 320, 60, 310),
Property('Income Tax', 'None', 0, 0, 1, 1),
Property('Reading Railroad', 'Railroad', 25, 200, 200, 200),
Property('Oriental Avenue', 'Light Blue', 6, 550, 100, 350),
Property('Chance', 'None', 0, 0, 1, 1),
Property('Vermont Avenue', 'Light Blue', 6, 550, 100, 350),
Property('Connecticut Avenue', 'Light Blue', 8, 600, 120, 370),
Property('Jail', 'None', 0, 0, 1, 1),
Property('St. Charles Place', 'Pink', 10, 750, 140, 640),
Property('Electric Company', 'Utility', 28, 70, 150, 150), # Rent based on mean roll of 7.
Property('States Avenue', 'Pink', 10, 750, 140, 640),
Property('Virginia Avenue', 'Pink', 12, 900, 160, 660),
Property('Pennsylvania Railroad', 'Railroad', 25, 200, 200, 200),
Property('St. James Place', 'Orange', 14, 950, 180, 680),
Property('Community Chest', 'None', 0, 0, 1, 1),
Property('Tennessee Avenue', 'Orange', 14, 950, 180, 680),
Property('New York Avenue', 'Orange', 16, 1000, 200, 700),
Property('Free Parking', 'None', 0, 0, 1, 1),
Property('Kentucky Avenue', 'Red', 18, 1050, 220, 970),
Property('Chance', 'None', 0, 0, 1, 1),
Property('Indiana Avenue', 'Red', 18, 1050, 220, 970),
Property('Illinois Avenue', 'Red', 20, 1100, 240, 990),
Property('B&O Railroad', 'Railroad', 25, 200, 200, 200),
Property('Atlantic Avenue', 'Yellow', 22, 1150, 260, 1010),
Property('Ventnor Avenue', 'Yellow', 22, 1150, 260, 1010),
Property('Water Works', 'Utility', 28, 70, 150, 150), # Rent based on mean roll of 7.
Property('Marvin Garden', 'Yellow', 24, 1200, 280, 1030),
Property('Go To Jail', 'None', 0, 0, 1, 1),
Property('Pacific Avenue', 'Green', 26, 1275, 300, 1300),
Property('North Carolina Avenue', 'Green', 26, 1275, 300, 1300),
Property('Community Chest', 'None', 0, 0, 1, 1),
Property('Pennsylvania Avenue', 'Green', 28, 1400, 320, 1320),
Property('Short Line', 'Railroad', 25, 200, 200, 200),
Property('Chance', 'None', 0, 0, 1, 1),
Property('Park Place', 'Blue', 35, 1500, 350, 1350),
Property('Luxury Tax', 'None', 0, 0, 1, 1),
Property('Boardwalk', 'Blue', 50, 2000, 400, 1400)
]
class Player():
def __init__(self, number, game):
self.number = number
self.game = game
self.position = 0
self.turn = 0
self.round = 1
def roll_dice(self):
dice_one = random.randint(1, 6)
dice_two = random.randint(1, 6)
return dice_one + dice_two, dice_one == dice_two
def go_to_property(self, number, pass_go=True):
if pass_go and self.position > number:
self.round += 1
self.position = number
results.append([self.game.number,
self.number,
self.turn,
0,
False,
self.position,
properties[self.position].name])
self.resolve_property()
def take_turn(self):
global df
self.turn += 1
doubles_count = 0
while True:
roll, doubles = self.roll_dice()
if doubles:
doubles_count += 1
if doubles_count >= 3:
self.go_to_property(JAIL, pass_go=False)
break
self.position += roll
if self.position >= NUM_PROPERTIES:
self.position = self.position % NUM_PROPERTIES
self.round += 1
results.append([self.game.number,
self.number,
self.turn,
roll,
doubles,
self.position,
properties[self.position].name])
end_turn = self.resolve_property()
if end_turn:
break
if not doubles:
break
def resolve_property(self):
if self.position == GO_TO_JAIL:
self.go_to_property(JAIL, pass_go=False)
return True
elif self.position in CHANCE:
card = self.game.draw_chance()
if card == 0: # Advance to Go
self.go_to_property(GO)
elif card == 1: # Go to Jail
self.go_to_property(JAIL, pass_go=False)
return True
elif card == 2: # Go Back 3 Spaces
new_position = self.position - 3
if new_position < 0:
new_position += NUM_PROPERTIES
self.go_to_property(new_position)
elif card == 3: # Advance to Boardwalk
self.go_to_property(BOARDWALK)
elif card == 4: # Advance to Reading Railroad
self.go_to_property(READING)
elif card == 5: # Advance to Illinois Avenue
self.go_to_property(ILLINOIS)
elif card == 6: # Advance to St. Charles Place
self.go_to_property(ST_CHARLES)
elif card == 7: # Advance to nearest Utility
found = False
for util in UTILITIES:
if self.position < util:
found = True
self.go_to_property(util)
if not found:
self.go_to_property(UTILITIES[0])
elif card == 8 or card == 9:
found = False
for rr in RAILROADS:
if self.position < rr:
found = True
self.go_to_property(rr)
if not found:
self.go_to_property(RAILROADS[0])
elif self.position in COMMUNITY_CHEST:
card = self.game.draw_community_chest()
if card == 0: # Advance to Go
self.go_to_property(GO)
elif card == 1: # Go to Jail
self.go_to_property(JAIL, pass_go=False)
return True
return False
class Game():
def __init__(self, number, num_players):
self.number = number
self.players = [Player(i + 1, self) for i in range(num_players)]
self.chance = [i for i in range(16)]
random.shuffle(self.chance)
self.community_chest = [i for i in range(16)]
random.shuffle(self.community_chest)
def play_game(self, num_turns):
for i in range(num_turns):
for p in self.players:
p.take_turn()
def draw_card(self, deck):
card = deck.pop(0)
deck.append(card)
return card
def draw_community_chest(self):
return self.draw_card(self.community_chest)
def draw_chance(self):
return self.draw_card(self.chance)
investments = pd.DataFrame(properties, columns=['Name', 'Color', 'Low Rent', 'High Rent', 'Low Cost', 'High Cost'])
investments.head()
investments['Low ROI'] = investments['Low Cost'] / investments['Low Rent']
investments['High ROI'] = investments['High Cost'] / investments['High Rent']
columns = ['Name', 'Low Cost', 'Low Rent', 'Low ROI']
investments.sort('Low ROI')[columns]
columns = ['Name', 'High Cost', 'High Rent', 'High ROI']
investments.sort('High ROI')[columns]
for i in range(NUM_GAMES):
g = Game(i, 4)
g.play_game(NUM_ROUNDS)
game_results = pd.DataFrame(results, columns=['Game', 'Player', 'Turn', 'Roll', 'Doubles', 'Position', 'Property'])
game_results.head()
prop_revenue = pd.DataFrame({'Count' : game_results.groupby( ['Position', 'Property'] ).size()}).reset_index()
prop_revenue['Color'] = prop_revenue['Position'].apply(lambda x: properties[x].color)
prop_revenue['Percent Total Count'] = prop_revenue['Count'].apply(lambda x: x / prop_revenue['Count'].sum())
prop_revenue.head()
prop_revenue[['Property', 'Count', 'Percent Total Count']].sort('Count', ascending=False)
prop_revenue['Low Rent'] = prop_revenue['Position'].apply(lambda x: properties[x].low_rent)
prop_revenue['High Rent'] = prop_revenue['Position'].apply(lambda x: properties[x].high_rent)
prop_revenue['Low Revenue'] = prop_revenue['Count'] * prop_revenue['Low Rent']
prop_revenue['Percent Total Low Revenue'] = prop_revenue['Low Revenue'].apply(
lambda x: x / prop_revenue['Low Revenue'].sum()
)
prop_revenue['High Revenue'] = prop_revenue['Count'] * prop_revenue['High Rent']
prop_revenue['Percent Total High Revenue'] = prop_revenue['High Revenue'].apply(
lambda x: x / prop_revenue['High Revenue'].sum()
)
columns = ['Property', 'Count', 'Low Rent', 'Low Revenue', 'Percent Total Low Revenue']
prop_revenue.sort('Low Revenue', ascending=False)[columns]
columns = ['Property', 'Count', 'High Rent', 'High Revenue', 'Percent Total High Revenue']
prop_revenue.sort('High Revenue', ascending=False)[columns]
color_revenue = prop_revenue.groupby( ['Color'] ).sum().reset_index()
color_revenue.head()
columns = ['Color', 'Count', 'Percent Total Count']
color_revenue.sort('Count', ascending=False)[columns]
columns = ['Color', 'Count', 'Low Revenue', 'Percent Total Low Revenue']
color_revenue.sort('Low Revenue', ascending=False)[columns]
columns = ['Color', 'Count', 'High Revenue', 'Percent Total High Revenue']
color_revenue.sort('High Revenue', ascending=False)[columns]