Python OOP Monty Hall not giving the expected results - python

Trying out some OOP in python, I tried to create a Monty Hall Problem simulation that is giving odd results. I implement three different strategies that a player can choose from, either to stay with the first door selected, switch to the second closed door, or randomly choose between them.
import random
class Door():
behind = None
is_open = False
is_chosen = False
def __init__(self,name=None):
self.name = name
def open(self):
self.is_open = True
def choose(self):
self.is_chosen = True
class Goat():
is_a = 'goat'
class Car():
is_a = 'car'
class Player():
door = None
def choose(self,door):
self.door = door
self.door.choose()
def open(self):
self.door.open()
if self.door.behind.is_a == 'car':
return True
return False
def play(strategy):
player = Player()
items = [Goat(),Goat(),Car()]
doors = [Door(name='a'),Door(name='b'),Door(name='c')]
for door in doors:
item = items.pop()
door.behind = item
random.shuffle(doors)
player.choose(random.choice(doors))
if strategy == 'random':
if random.choice([True,False]):
for door in doors:
if not door.is_open and not door.is_chosen:
final = door
break
else:
final = player.door
elif strategy == 'switch':
for door in doors:
if not door.is_open and not door.is_chosen:
final = door
break
elif strategy == 'stay':
final = player.door
player.choose(final)
if player.open():
return True
else:
return False
## Play some games
for strategy in ['random','switch','stay']:
results = []
for game in range(0,10000):
if play(strategy):
results.append(True)
else:
results.append(False)
## Gather the results
wins = 0
loses = 0
for game in results:
if game:
wins += 1
else:
loses += 1
print 'results:\tstrategy={}\twins={}\tloses={}'.format(strategy,str(wins),str(loses))
But every time I run it, I get something like:
results: strategy=random wins=3369 loses=6631
results: strategy=switch wins=3369 loses=6631
results: strategy=stay wins=3320 loses=6680
Why is this giving nearly the same results for each strategy? Shouldn't the 'switch' strategy give a ratio of ~66% wins and 'stay' give ~33%?

You're not playing the game correctly. After the contestant chooses a door, the host reveals a goat behind one of the other two doors, and then offers the contestant the opportunity to switch -- you're allowing a choice between three doors instead of two. Here's a revised play() function:
def play(strategy):
player = Player()
items = [Goat(), Goat(), Car()]
doors = [Door(name='a'), Door(name='b'), Door(name='c')]
random.shuffle(items)
for door in doors:
item = items.pop()
door.behind = item
player.choose(random.choice(doors))
# player has chosen a door, now show a goat behind one of the other two
show = None
for door in doors:
if not (door.is_open or door.is_chosen) and door.behind.is_a == 'goat':
show = door
show.open()
break
# The player has now been shown a goat behind one of the two doors not chosen
if strategy == 'random':
if random.choice([True, False]):
for door in doors:
if not (door.is_open or door.is_chosen):
final = door
break
else:
final = player.door
elif strategy == 'switch':
for door in doors:
if not (door.is_open or door.is_chosen):
final = door
break
elif strategy == 'stay':
final = player.door
player.choose(final)
return player.open()
That produces results like:
results: strategy=random wins=4977 loses=5023
results: strategy=switch wins=6592 loses=3408
results: strategy=stay wins=3368 loses=6632

Related

how to avoid infinite loop when using a constructor function to create an object

# Import Modules
from Dice import dice
d10 = dice(10,1)
d4 = dice(4,1)
# Assign Classes
class Player_Character:
def __init__(self, hp, maxhp, ac, THAC0, Surprise_Adjustment, Initiative_Adjustment):
self.hp = int(hp)
self.maxhp = int(maxhp)
self.ac = int(ac)
self.THAC0 = int(THAC0)
self.Surprise_Adjustment = int(Surprise_Adjustment)
self.Initiative_Adjustment = int(Initiative_Adjustment)
def attack(self, goblin):
Player_Character_Damage = d10.die_roll()
goblin.hp -= Player_Character_Damage
if (goblin.hp <= 0):
print("congratulations you killed the goblin")
def flee(self):
print("you run away ")
quit()
def heal(self, Player_Character):
Player_Character.hp += d10.die_roll()
if Player_Character.hp >= Player_Character.maxhp:
Player_Character.hp = Player_Character.maxhp
class goblin:
def __init__(self, hp, maxhp, ac, THAC0, Surprise_Adjustment, Initiative_Adjustment):
self.hp = int(hp)
self.maxhp = int(maxhp)
self.ac = int(ac)
self.THAC0 = int(THAC0)
self.Surprise_Adjustment = int(Surprise_Adjustment)
self.Initiative_Adjustment = int(Initiative_Adjustment)
def attack(self, Player_Character):
goblin_damage = d4.die_roll()
Player_Character.hp -= goblin_damage
if (Player_Character.hp <= 0):
print("oh dear you have died")
del Player_Character
MrHezy = Player_Character(10, 20, 10, 15, 0, 2)
def spawn_goblin(goblin):
G1 = goblin(5, 10, 8, 18, 0, 0)
return G1
goblin1 = spawn_goblin(goblin)
def battle(goblin1):
# user input
player_initiative_adjustment = MrHezy.Initiative_Adjustment
monster_initiative_adjustment = goblin1.Initiative_Adjustment
#define while loop for the battle
battle_not_over = 'yes'
while battle_not_over == 'yes':
#use random.randint(a,b) to generate player and monster base initiative
player_base_initiative = d10.die_roll()
monster_base_initiative = d10.die_roll()
#subtract the adjustment to get player and monster initiative
player_initiative = player_base_initiative - player_initiative_adjustment
monster_initiative = monster_base_initiative - monster_initiative_adjustment
#compare the initiatives and display the results
if (player_initiative < monster_initiative):
attack_flee_heal = input("congratulations you go first. Would you like to attack, flee, or heal?")
while attack_flee_heal != 'attack' or 'flee' or 'heal':
if attack_flee_heal == 'attack':
MrHezy.attack(goblin1)
elif attack_flee_heal == 'heal':
MrHezy.heal(MrHezy)
print("the goblin attacks")
goblin1.attack(MrHezy)
break
elif attack_flee_heal == 'flee':
MrHezy.flee()
break
else:
print("uhoh, the monsters go first, they attack!")
goblin1.attack(MrHezy)
attack_flee_heal = input("Would you like to attack, flee, or heal? ")
while attack_flee_heal != 'attack' or 'flee' or 'heal':
if attack_flee_heal == 'attack':
MrHezy.attack(goblin1)
elif attack_flee_heal == 'heal':
MrHezy.heal(MrHezy)
print("the goblin attacks")
goblin1.attack(MrHezy)
break
elif attack_flee_heal == 'flee':
MrHezy.flee()
break
#main game loop
while True:
spawn_goblin(goblin)
battle(goblin1)
This is a code for a battle simulator that ive been working on. it starts by importing a module that i made which consists of a class called 'dice' which I use for randomly generating numbers. Defining classes is next, with attributes including hp, maxhp, armor class, 'to hit armor class 0', surprise adjustment, and initiative adjustment, and methods including attack which allows you to attack the monsters, flee which exits the battle, and heal which gives your character more hit points. The program moves on to define the spawn_goblin() function which spawns a goblin (this works just fine for the first iteration of the loop). Then it moves on to the battle part which is pretty simple; all it does is check who goes first and then allows you to attack, or flee, or heal yourself. Please ignore the "heal" and "flee" methods, these are working just fine. The problem occurs when I attack the goblin. Instead of killing the goblin and spawning another it creates an infinite loop saying "congratulations you killed the goblin"
while (goblin.hp <= 0):
print("congratulations you killed the goblin")
I think this part of your code is wrong. The while should be if, since you only want to check if the goblin has died, and run some code once. Once the goblin's HP becomes lesser than or equal to 0, the print statement will loop forever since the expression goblin.hp <= 0 will always return True.
EDIT: Now where I think your code is wrong is here:
while attack_flee_heal != 'attack' or 'flee' or 'heal':
if attack_flee_heal == 'attack':
MrHezy.attack(goblin1)
elif attack_flee_heal == 'heal':
MrHezy.heal(MrHezy)
...
I think you have missed the break in the first if (if attack_flee_heal == 'attack'
Also, it would be more appropriate to use an if here rather than a while, since, as I mentioned above, you only want to check once.

Developing a Turn-Based Battle Game [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
I am trying to create a turn based rpg game in python. Currently i am using the list method to display and create individual values for each role. For example, the game allows player to setup a team of units for battle (minimum of 1 unit, default is 3). Each unit has a unique name and attributes like health point (HP), attack point (ATK), defence point (DEF), experience point (EXP) and a rank (default is level 1).
A unit can be either a Warrior, a Tanker or a Wizard whom have different strength in ATK and DEF point. In addition, a Wizard has special ability to cast spells that can impact friendly and enemy units (Heal, Poison, Cure, Freeze).
The suggested initial values for each unit’s attribute point are described in requirement details under Part A – Game Setup section.
The game will automatically setup either (1) player vs Artificial Intelligence (AI) or (2) two-player mode, of which both teams are made up of same number of units.
For AI team, the type of units will be assigned randomly or by specific AI algorithm. The name of units will be specified by the player, whereas AI unit names can be defined with prefix + random numbers (eg AI87). For two-player mode, each player will be able to go through the same unit selection process either through console or GUI.
For each turn, player can select an active friendly unit (non-frozen or dead) to perform an action on a target unit. Units which are severely damaged (i.e. HP equals to or less than 0) will be considered killed (or flagged as “dead”).
How do I use Object Oriented Programming method to create the 3 characters with input name for each team (can display the name keyed and the information of each character), instead of use list method.
#Menu page
print('Welcome to PSB Battle Game! \n(N)ew game\n(S)ave game\n(Q)uit')
def main():
selection = input('Choose your option then hit <ENTER> ==> ')
if selection.upper() == 'N':
new_game()
elif selection.upper() == 'S':
print('Loading save game...')
pass
elif selection.upper() == 'Q':
print('Exit game...')
pass
else:
print("I don't understand what are you typing.")
return main()
def new_game():
print('\nSetting up Player 1 team...\n')
name_list = []
for unit_name in range(1,4):
print(f'Enter a unique name for unit #{unit_name}-> ', end='')
name = input('')
repeated = False
while repeated:
if name == "":
continue
repeated = True
if name in name_list:
print('\nUnit name must be unique.\n')
return new_game()
if not name.strip():
print('\nUnit name could not be blank.\n')
return new_game()
else:
print('Name looks good!')
name_list.append(name)
print(f'Select unit #{unit_name}, type: (W)arrior, (T)anker, or Wi(Z)ard ==> ', end='')
role = input('')
if role.upper() == 'W':
print('Added ' + str(name_list))
warrior()
elif role.upper() == 'T':
print('Added ' + str(name_list))
tanker()
elif role.upper() == 'Z':
print('Added ' + str(name_list))
wizard()
else:
print("I don't understand what are you typing.")
return role()
def warrior ():
charac = [1,50,8,3,0,'True','False','False']
print ('\nWarrior Level 1: ','HP =',charac[1],',''ATK =',charac[2],',''DEF =',charac[3],',''EXP =',charac[4],',''ALIVE =',charac[5],',''POISONED =',charac[6],',''FROZEN =',charac[7])
print ()
def tanker ():
charac = [1,60,5,5,0,'True','False','False']
print ('\nTanker Level 1: ','HP =',charac[1],',''ATK =',charac[2],',''DEF =',charac[3],',''EXP =',charac[4],',''ALIVE =',charac[5],',''POISONED =',charac[6],',''FROZEN =',charac[7])
print ()
def wizard ():
charac = [1,40,3,2,0,'True','False','False']
print ('\nWizard Level 1: ','HP =',charac[1],',''ATK =',charac[2],',''DEF =',charac[3],',''EXP =',charac[4],',''ALIVE =',charac[5],',''POISONED =',charac[6],',''FROZEN =',charac[7])
print ()
main()
Your game is far from functional. I took the liberty of setting up a small sketch of a game after your design with battle functionality and character classes. From this code you can work forward to include other functionalities, such as chance, moving, changing skill points, and other stuff.
class Char:
def __init__(self, name = '', cclass = "Warrior", stats = [1,50,5,5,0,'True','False','False']):
self.c = cclass
self.name = name
self.stats = {'LVL':stats[0],
'HP':stats[1],
'ATK':stats[2],
'DEF':stats[3],
'EXP':stats[4],
'ALIVE':stats[5],
'POISONED':stats[6],
'FROZEN':stats[7]}
self.calc_level()
def __repr__(self):
outs = ''
outs+="Character Name: {0} of class {1}:\n---------------".format(self.name,self.c)
for k,v in self.stats.items():
outs+='\n {0}: {1}'.format(k,v)
return outs
def calc_level(self):
self.stats['LVL'] = int(self.stats['EXP']**.5)+1
def attack(self,other):
print("\n{0} furiously attacks {1} with {2} attack. {1} has {3} defense.".format(self.name,other.name,self.stats['ATK'],other.stats['DEF']))
if self.stats['ATK']>=other.stats['DEF']:
other.stats['HP'] -= self.stats['ATK']
print("\nThat was a hit! The HP of {0} is now {1}".format(other.name,other.stats['HP']))
else:
print("\nYou missed and only made him angrier!")
def new_char(existing):
cc = ''
accept = False
while not accept:
n = input("\nPlease input a new name: ")
accept = True
for c in existing:
if n == c.name:
accept = False
print("This name is taken, already")
while not cc in ['w','t','z']:
cc = input("\nPlease input a class, noble {0}. (W)arrior, (T)ank, Wi(z)ard: ".format(n)).lower()
cclasses = {'w':'Warrior','t':'Tank','z':'Wizard'}
newc = Char(n,cclasses[cc])
print('\nCharacter successfully created:')
print(newc)
return newc
def play(chars):
print("May the games begin. The following characters are present:\n")
for c in chars:
print(c)
print('')
game_over = False
turn = 0
while not game_over:
print("It's the turn of noble {0} {1}. Please select a player to attack:".format(chars[turn].c,chars[turn].name))
possible = []
for i in range(len(chars)):
if not i==turn:
possible.append(i)
print(" - ({0}): {1} named {2}".format(i,chars[i].c,chars[i].name))
sel = -1
while not sel in possible:
s = input('Selection: ')
try:
sel = int(s)
except:
print("That's not a valid choice")
chars[turn].attack(chars[sel])
if chars[sel].stats['HP']<=0:
game_over=True
print("That was it! {0} has died and the game is over.".format(chars[sel].name))
turn +=1
if turn==len(chars):turn=0
def main():
chars = []
entry = ''
print("Welcome to PSB Battle Game!")
while not entry.lower() in ['q','p']:
entry = input('\n(N)ew character\n(P)lay game\n(Q)uit\nSelection: ').lower()
if entry == 'p' and len(chars)<2:
print("\nYou can't play with only one character. Create characters first")
entry = '' ## You can't play with only one char
elif entry == 'n':
chars.append(new_char(chars))
entry = ''
elif entry == 'p':
play(chars)
elif entry == 'q':
print("\nOK, good bye")
main()

breaking out of while loops

So this is a simplified version of my bigger code (there is some unnecessary bits that makes more sense in my main code), and I cannot seem to understand why it neither breaks out of the playing nor the fight loops:
fight = True
a = "enemy"
t = ""
ccd = False,False
playing = True
def lvl():
playing = True
fight = True
while fight:
if a == "enemy":
t = "enemy"
else:
if a == "enemy":
t = "enemy"
else:
t = "player"
while t == "enemy":
print ("enemy attacks player")
t = "player"
while t == "player":
if ccd == (False , False):
fight = False
playing = False
turn = "no one"
t = "enemy"
fight = True
while playing:
fight = True
lvl()
while not playing:
print ("no")
Your program actually is breaking out of the "fight" loop, but it is sent right back in because the playing loop is still running.
The reason why you are not breaking out of the playing loop is that your "playing" variable is local, not global. As such, when you change the variable in the function it doesn't change the one in main (even though they have the same name), which you are using to control the while loop. A possible solution for this would be to return "playing" at the end of your function. Here is some modified code that should suit your purposes:
'''
fight = True
a = "enemy"
t = ""
ccd = False,False
playing = True
def lvl():
playing = True
fight = True
while fight:
if a == "enemy":
t = "enemy"
else:
t = "player"
while t == "enemy":
print ("enemy attacks player")
t = "player"
while t == "player":
if ccd == (False , False):
fight = False
playing = False
t = "enemy"
return playing
fight = True
while playing:
fight = True
playing = lvl()
while not playing:
print ("no")
'''
To have lvl() affect the global variable, you must do the following:
def lvl():
global playing
playing = True

montyhall simulator gone wrong(python)

#
# Monty Hall Problem Simulation
# Author: Ryan Sturmer
#
import random
def play_round(doors, switch):
# Choose the location of the car
car = random.randint(1, doors)
# Contestant chooses a door
initial_choice = random.randint(1, doors)
# Monty opens ALL the other doors except one
if initial_choice != car:
monty_leaves = car # If the car wasn't chosen, Monty is forced to reveal its location
else:
while True:
monty_leaves = random.randint(1, doors)
if monty_leaves != initial_choice:
break
# monty_leaves is now the door that Monty DIDN'T open
if switch:
final_choice = monty_leaves
else:
final_choice = initial_choice
victory = (final_choice == car)
return victory, initial_choice, final_choice, car
def simulation(iterations, doors=3):
games_won_switch = 0
games_won_noswitch = 0
for i in range(iterations):
won_game, intial_choice, final_choice, car = play_round(doors, False)
if(won_game):
games_won_noswitch += 1
won_game, intial_choice, final_choice, car = play_round(doors, True)
if(won_game):
games_won_switch += 1
print ""
print " Monty Hall Simulation"
print "---------------------------------------------"
print " Iterations: %d" % iterations
print " Games won when switching doors: %d (%g%%)" % (games_won_switch, 100*float(games_won_switch)/float(iterations))
print "Games won when NOT switching doors: %d (%g%%)" % (games_won_noswitch, 100*float(games_won_noswitch)/float(iterations))
===========================================================================
I found this code from github.
When I run this code, the sum of games_won_switch and games_won_noswitch is not equals to iteration.
(For example, if I set iteration 1,000 -> it appears 996, 1,001, 1,008 not exact 1,000)
How can i fix problem this up?
There is no mistake in the code; it runs the simulations twice:
Once when the player chooses to switch doors every time, and another time when the player chooses to never switch doors. It then prints the results of both simulations.
The results are from independent simulations.
for i in range(iterations):
# Sim with player choose to open the door each time
won_game, intial_choice, final_choice, car = play_round(doors, False)
if(won_game):
games_won_noswitch += 1
# Sim with player choose NEVER to open the door
won_game, intial_choice, final_choice, car = play_round(doors, True)
if(won_game):
games_won_switch += 1
In consequence, because sim(open=True) is not (1 - sim(open=False)), but result from two sets of simulations, the addition of results may not add up precisely to the number of trials.

How to check if user has been through a method

I am making a simple text-based RPG in Python. Currently I have two methods for most rooms, one for when they first enter and one if they return. Is there a way that I can make sure that they haven't been in that room before without another method?
For example, if I had a method named tomb() i create another method called tombAlready() that contains the same code except for the introduction text to the room.
So if I had
slow_type("\n\nTomb\n\n")
slow_type("There is an altar in the middle of the room, with passages leading down and west.")
choice = None
while choice == None:
userInput = input("\n>")
if checkDirection(userInput) == False:
while checkDirection == False:
userInput = input("\n>")
checkDirection(userInput)
userInput = userInput.lower().strip()
if userInput == "d":
catacombs()
elif userInput == "n":
altar()
elif userInput == "w":
throneroom()
else:
slow_type("You cannot perform this action.")
Then tombAlready() would have the same code except for slow_type("There is an altar in the middle of the room, with passages leading down and west.")
What you want is state associated with a function. Use an object with a method:
class Room:
def __init__(self, description):
self._description = description
self._visited = False
def visit(self):
if not self._visited:
print(self._description)
self._visited = True
Then you can have a Room object for each room:
catacombs = Room('There is a low, arched passageway. You have to stoop.')
tomb = Room('There is an altar in the middle of the room, with passages leading down and west.')
throneroom = Room('There is a large chair. It looks inviting.')
You can visit a room twice but it only prints its description once:
>>> catacombs.visit()
There is a low, arched passageway. You have to stoop.
>>> catacombs.visit()

Categories

Resources