-
Notifications
You must be signed in to change notification settings - Fork 32
/
coffee_shop_simulator.py
216 lines (166 loc) · 6.79 KB
/
coffee_shop_simulator.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
import pickle
import random
import re
import numpy
class CoffeeShopSimulator:
# Minimum and maximum temperatures
TEMP_MIN = 20
TEMP_MAX = 90
# Length of temperature list
# (higher produces more realistic curve)
SERIES_DENSITY = 300
# Save game file
SAVE_FILE = "savegame.dat"
# Faux temp distributions. We'll do this better
# later with a bell curve, but for now a quick hack
def __init__(self):
# Get name and store name
print("Let's collect some information before we start the game.\n")
self.player_name = self.prompt("What is your name?", True)
self.shop_name = self.prompt("What do you want to name your coffee shop?", True)
# Current day number
self.day = 1
# Cash on hand at start
self.cash = 100.00
# Inventory at start
self.coffee_inventory = 100
# Sales list
self.sales = []
# Possible temperatures
self.temps = self.make_temp_distribution()
def run(self):
print("\nOk, let's get started. Have fun!")
# The main game loop
running = True
while running:
# Display the day and add a "fancy" text effect
self.day_header()
# Get the weather
temperature = self.weather
# Display the cash and weather
self.daily_stats(temperature)
# Get price of a cup of coffee (but provide an escape hatch)
response = self.prompt("What do you want to charge per cup of coffee? (type exit to quit)")
if re.search("^exit", response, re.IGNORECASE):
running = False
continue
else:
cup_price = int(response)
# Do they want to buy more coffee inventory?
print("\nIt costs $1 for the necessary inventory to make a cup of coffee.")
response = self.prompt("Want to buy more so you can make more coffee? (ENTER for none or enter number)", False)
if response:
if not self.buy_coffee(response):
print("Could not buy additional coffee.")
# Get price of a cup of coffee
print("\nYou can buy advertising to help promote sales.")
advertising = self.prompt("How much do you want to spend on advertising (0 for none)?", False)
# Convert advertising into a float
advertising = self.convert_to_float(advertising)
# Deduct advertising from cash on hand
self.cash -= advertising
# Simulate today's sales
cups_sold = self.simulate(temperature, advertising, cup_price)
gross_profit = cups_sold * cup_price
# Display the results
print("\nYou sold " + str(cups_sold) + " cups of coffee today.")
print("You made $" + str(gross_profit) + ".")
# Add the profit to our coffers
self.cash += gross_profit
# Subtract inventory
self.coffee_inventory -= cups_sold
if self.cash < 0:
print("\n:( GAME OVER! You ran out of cash.")
running = False
continue
# Before we loop around, add a day
self.increment_day()
# Save the game
with open(self.SAVE_FILE, mode="wb") as f:
pickle.dump(self, f)
def simulate(self, temperature, advertising, cup_price):
# Find out how many cups were sold
cups_sold = self.daily_sales(temperature, advertising, cup_price)
# Save the sales data for today
self.sales.append({
"day": self.day,
"coffee_inv": self.coffee_inventory,
"advertising": advertising,
"temp": temperature,
"cup_price": cup_price,
"cups_sold": cups_sold
})
# We technically don't need this, but why make the next step
# read from the sales list when we have the data right here
return cups_sold
def buy_coffee(self, amount):
try:
i_amount = int(amount)
except ValueError:
return False
if i_amount <= self.cash:
self.coffee_inventory += i_amount
self.cash -= i_amount
return True
else:
return False
def make_temp_distribution(self):
# Create series of numbers between TEMP_MIN and TEMP_MAX
series = numpy.linspace(self.TEMP_MIN, self.TEMP_MAX, self.SERIES_DENSITY)
# Obtain mean and standard deviation from the series
mean = numpy.mean(series)
std_dev = numpy.std(series)
# Calculate probability density and return the list it creates
return (numpy.pi * std_dev) * numpy.exp(-0.5 * ((series - mean) / std_dev) ** 2)
def increment_day(self):
self.day += 1
def daily_stats(self, temperature):
print("You have $" + str(self.cash) + " cash on hand and the temperature is " + str(temperature) + ".")
print("You have enough coffee on hand to make " + str(self.coffee_inventory) + " cups.\n")
def day_header(self):
print("\n-----| Day " + str(self.day) + " @ " + self.shop_name + " |-----")
def daily_sales(self, temperature, advertising, cup_price):
# Randomize advertising effectiveness
adv_coefficient = random.randint(20, 80) / 100
# Higher priced coffee doesn't sell as well
price_coefficient = int((cup_price * (random.randint(50, 250) / 100)))
# Run the sales figures!
sales = int((self.TEMP_MAX - temperature) * (advertising * adv_coefficient))
# If price is too high, we don't sell anything
if price_coefficient > sales:
sales = 0
else:
sales -= price_coefficient
if sales > self.coffee_inventory:
sales = self.coffee_inventory
print("You would have sold more coffee but you ran out. Be sure to buy additional inventory.")
return sales
@property
def weather(self):
# Generate a random temperature between 20 and 90
# We'll consider seasons later on, but this is good enough for now
return int(random.choice(self.temps))
@staticmethod
def prompt(display="Please input a string", require=True):
if require:
s = False
while not s:
s = input(display + " ")
else:
s = input(display + " ")
return s
@staticmethod
def convert_to_float(s):
# If conversion fails, assign it to 0
try:
f = float(s)
except ValueError:
f = 0
return f
@staticmethod
def x_of_y(x, y):
num_list = []
# Return a list of x numbers of y
for i in range(x):
num_list.append(y)
return num_list