I just started to learn python(self-taught) and I'm trying to do a simulation of racquetball game.
I can run the simulation with: a==15 or b==15, but when I add the extra condition: abs(a-b)>=2 IT GOES CRAZY(the simOneGame turns to an infinite loop). Here is what I tried so far:
return a == 15 or b == 15 ------> this one works perfectly
return a == 15 or b == 15 and abs(a-b) >= 2 --> this doesn't freeze but the logic is wrong(I think)
return (a == 15 or b == 15) and abs(a-b) >= 2 --> freezing
return (a == 15 or b == 15) and (abs(a-b) >= 2) --> freezing
return ((a == 15 or b == 15) and (abs(a-b) >= 2)) --> freezing
Thanks a lot and sorry if this seems too long(maybe I should've typed just the function gameOver()?...)
Here is my code so far:
from random import random
def main():
printIntro()
n, probA, probB = getInputs()
winsA, winsB = simNGames(n, probA, probB)
displayResults(winsA, winsB)
def printIntro():
print("\nVOLLEYBALL SIMULATION\n")
print("The inputs will be the number of simulations(n),")
print(" the probability player A wins his serve(probA)")
print(" and the probability player B wins his serve(probB).")
print("The program will display how many games won player A(winsA),")
print(" how many games won player B(winsB),")
print(" and the percentages of winnings for both players.\n")
def getInputs():
n = int(input("Enter the number of simulations: "))
probA = float(input("Enter the probability player A wins his serve: "))
probB = float(input("Enter the probability player B wins his serve: "))
return n, probA, probB
def simNGames(n, probA, probB):
winsA, winsB = 0, 0
for i in range(n):
# player A serves on odd games
if i % 2 == 0:
serving = "A"
# player B serves on even games
else:
serving = "B"
scoreA, scoreB = simOneGame(probA, probB, serving)
if scoreA > scoreB:
winsA = winsA + 1
else:
winsB = winsB + 1
return winsA, winsB
def simOneGame(probA, probB, serving):
scoreA, scoreB = 0, 0
while not gameOver(scoreA, scoreB):
if serving == "A":
if random() < probA:
scoreA = scoreA + 1
else:
serving = "B"
else:
if random() < probB:
scoreB = scoreB + 1
else:
serving = "A"
return scoreA, scoreB
def gameOver(a, b):
# the game is over if a player gets to 15 AND the difference is at least 2.
# this shoud work... BUT IT DOESN'T... simOneGame turns to an infinete loop with a large number of simulations...
return (a == 15 or b == 15) and abs(a-b) >= 2
def displayResults(winsA, winsB):
print("Player A won {0} games({1:1.0%})".format(winsA, winsA/(winsA+winsB)))
print("Player B won {0} games({1:0.0%})".format(winsB, winsB/(winsA+winsB)))
if __name__ == "__main__":
main()
Related
I'm trying to create a game called Connect Four with AI which uses the alpha-beta pruning algorithm in python. Here is the code I have managed to write:
# -*- coding: utf-8 -*-
import sys
class ConnectFour:
def __init__(self):
self.board = [[],[],[],[],[],[]]
for i in range(7):
for j in range(6):
self.board[j].append(" ")
self.moves = 0
self.colstack = [0,0,0,0,0,0,0]
self.node = 0
self.move = 0
def PrintGameBoard(self):
print(' 0 1 2 3 4 5 6')
for i in range(5, -1, -1):
print('|---|---|---|---|---|---|---|')
print("| ",end="")
for j in range(7):
print(self.board[i][j],end="")
if j != 6:
print(" | ",end="")
else:
print(" |")
print('`---------------------------´')
def CanPlay(self, col):
return self.colstack[col] < 6
def Play(self, col, board):
board[self.colstack[col]][col] = 2
self.colstack[col] += 1
self.moves += 1
return board
def IsWinning(self, currentplayer):
for i in range(6):
for j in range(4):
if self.board[i][j] == currentplayer and self.board[i][j+1] == currentplayer and self.board[i][j+2] == currentplayer and self.board[i][j+3] == currentplayer:
return True
for i in range(3):
for j in range(7):
if self.board[i][j] == currentplayer and self.board[i+1][j] == currentplayer and self.board[i+2][j] == currentplayer and self.board[i+3][j] == currentplayer:
return True
for i in range(3):
for j in range(4):
if self.board[i][j] == currentplayer and self.board[i+1][j+1] == currentplayer and self.board[i+2][j+2] == currentplayer and self.board[i+3][j+3] == currentplayer:
return True
for i in range(3,6):
for j in range(4):
if self.board[i][j] == currentplayer and self.board[i-1][j+1] == currentplayer and self.board[i-2][j+2] == currentplayer and self.board[i-3][j+3] == currentplayer:
return True
return False
def AlphaBeta(self, alpha, beta):
self.node += 1
if self.moves == 42:
return 0
for col in range(7):
if self.CanPlay(col) and self.IsWinning(2):
return (43 - self.moves)/2
max = (41 - self.moves)/2
if beta > max:
beta = max
if alpha >= beta:
return beta
for col in range(7):
if self.CanPlay(col):
self.board[self.colstack[col]][col] = 2
self.move = col
score = -self.AlphaBeta(-alpha, -beta)
if score >= beta:
return score
elif score > alpha:
alpha = score
self.board[self.colstack[col]][col] = " "
def Solve(self, table, week=False):
self.node = 0
self.board = table
if week:
self.AlphaBeta(-1, 1)
self.board = self.Play(self.move, table)
return self.board
else:
self.AlphaBeta(-21, 21)
self.board = self.Play(self.move, table)
return self.board
def PlayerMove(self, table):
self.board = table
try:
allowedmove = False
while not allowedmove:
print("Choose a column where you want to make your move (0-6): ",end="")
col =input()
if self.CanPlay(int(col)):
self.board[self.colstack[int(col)]][int(col)] = 1
self.moves += 1
self.colstack[int(col)] += 1
allowedmove = True
else:
print("This column is full")
except (NameError, ValueError, IndexError, TypeError, SyntaxError) as e:
print("Give a number as an integer between 0-6!")
else:
return self.board
def PlayerMark():
print("Player 1 starts the game")
mark = ''
while not (mark == "1" or mark == "2"):
print('Do you want to be 1 or 2: ',end="")
mark = input()
if mark == "1":
return 1
else:
return 2
def PlayAgain():
print('Do you want to play again? (yes or no) :',end="")
return input().lower().startswith('y')
def main():
sys.setrecursionlimit(2000)
print("Connect4")
while True:
mark = PlayerMark()
connectfour = ConnectFour()
if mark==1:
print("You are going to start the game\r\n\r\n")
else:
print("Computer (negamax) starts the game")
gameisgoing = True
table = [[],[],[],[],[],[]]
for i in range(7):
for j in range(6):
table[j].append(" ")
while gameisgoing:
connectfour.PrintGameBoard()
if mark == 1:
table = connectfour.PlayerMove(table)
if connectfour.IsWinning(1):
connectfour.PrintGameBoard()
print('You won the game!')
gameisgoing = False
else:
if connectfour.moves==42:
connectfour.PrintGameBoard()
print('Game is tie')
break
else:
mark = 2
else:
move = connectfour.Solve(table)
if connectfour.IsWinning(2):
connectfour.PrintGameBoard()
print('Computer won the game')
gameisgoing = False
else:
if connectfour.moves==42:
connectfour.PrintGameBoard()
print('Game is tie')
break
else:
mark = 1
if not PlayAgain():
print("Game ended")
break
if __name__ == '__main__':
main()
I'm not sure if the algorithm is working properly, but the problem is that I get RecursionError: maximum recursion depth exceeded in comparison. If I increase recursionlimit I get in around 10000 MemoryError: Stack Overflow. I think that the problem is that there is too many states to handle for my computer. That's why I changed the negamax algorithm to alpha-beta pruning, but there seem to be still to many states. Is there a effective technique that does algorithmic search, for example max depth 10 and still the computer is almost invincible? I'm waiting your solutions.
This question already has answers here:
How to test multiple variables for equality against a single value?
(31 answers)
Closed 6 years ago.
Currently I'm working in Python to try and simulate a set in a match of tennis based on player probabilities. There's a while loop that's never ending in my "ftsix" (firsttosixpoints) function, but I can't figure out what the bug is because my logic isn't sound. I've been working on the problem for a few hours but I can't seem to find the solution.
P.S - please play nice
#One set in tennis = 1st to 6 games
#One game = 1st to four points
#Serve switches after each game.
def main():
printIntro()
probA, probB = getInputs()
gamesA, gamesB = ftsix(probA, probB)
if gamesA > gamesB:
print"Player A has won with", gamesA, "to player B's", gamesB, "."
else:
print "Player B has won with", gamesB,"to player A's", gamesA, "."
def printIntro():
print "This is a simulation of 1 tennis set based on player probabilities!"
def getInputs():
probA = input("What is the probability that player A will win?: ")
probB = input("What is the probability that player B will win?: ")
return probA, probB
def ftsix(probA, probB):
#First one to six games = one set
x = 0
gamesA = 0
gamesB = 0
while gamesA or gamesB != 6:
if x % 2 == 0 or x == 0:
serving = "A"
else:
serving = "B"
#This is one game, not in a seperate function because I couldn't figure out how to change serve inside the function!
pointsA = 0
pointsB = 0
while pointsA or pointsB != 4:
if serving == "A":
if probA >= random():
pointsA = pointsA + 1
else:
pointsB = pointsB + 1
if serving == "B":
if probB >= random():
pointsB = pointsB + 1
else:
pointsA = pointsA + 1
if pointsA > pointsB:
gamesA = gamesA + 1
else:
gamesB = gamesB + 1
x = x + 1
return gamesA, gamesB
I think your syntax in the while loops is incorrect. You need a full conditional on each side of the or, for example:
while gamesA != 6 and gamesB != 6:
You need to change your loop to terminate when either gamesA or gamesB reaches 6:
while gamesA != 6 and games != 6:
I am trying to implement a function that takes an integer n and simulates n rounds of Rock, Paper, Scissors between players Player 1 and Player 2. The player who wins the most rounds wins the n-round game, with ties possible. The function should print the result of the game as shown.
>>> simul(1)
Player 1
>>> simul(1)
Tie
>>>simul(100)
Player 2
I think I need to approach this in a modular fashion. In other words, I need to combine at least 2 functions, my problem is that I can't seem to figure out how to do that. How can I activate the result from an embedded function when calling the simul() function?
So I have created a function that simulates the game Rock, Paper, Scissors by execution the function rps(p1, p2). The code is the following:
def rps(p1,p2):
#tie
if (p1==p2):
return 0
# player 1 wins
elif p1+p2 in ['PR','RS','SP']:
return -1
else:
return 1
# player 2 wins
This is where I'm a bit stuck. I need to activate this function when executing the simul() function—how can I do that? What I have so far is the following:
def rps(p1,p2):
#tie
if (p1==p2):
return 0
# player 1 wins
elif p1+p2 in ['PR','RS','SP']:
return -1
else:
return 1
# player 2 wins
def choose_rps():
import random
random.choice('RPS')
def simul(n):
p1_wins, p2_wins = 0, 0
for i in range(n):
p1 = choose_rps()
p2 = choose_rps()
result = rps(p1, p2)
if result == -1:
p1_wins += 1
elif result == 1:
p2_wins += 1
if p1_wins > p2_wins:
return 'Player 1'
elif p1_wins == p2_wins:
return 'Tie'
else:
return 'Player 2'
To "activate" a function, you just call it. For example:
def simul(n):
score = 0
for i in range(n):
p1 = choose_rps()
p2 = choose_rps()
result = rps(p1, p2)
score += result
if score < 0:
print('Player 1')
elif score == 0:
print('Tie')
else:
print('Player 2')
Of course you need to write the choose_rps function (which randomly chooses and returns one of R, P, or S)—but, as you can see, you just call it the same way as the rps function.
To put it all together into a script:
def rps(p1, p2):
# ... your code here
def choose_rps():
# implement this here
def simul(n):
# code from above
And then you'll probably want something to drive it, such as this:
if __name__ == '__main__':
import sys
n = int(sys.argv[1]) if len(sys.argv) > 1 else 5
simul(n)
… or…
while True:
n = int(input('How many trials?')) # raw_input for Python 2.x
simul(n)
If you want, you can simplify this further. For example, you can turn the whole loop into a sum call with a generator expression:
def simul(n):
score = sum(rps(choose_rps(), choose_rps()) for _ in range(n))
Here is an equivalent function without the RPS narrative:
import random
def simul(n):
score = sum([random.choice([-1, 0, 1]) for i in xrange(n)])
winner_idx = 0 if score > 0 \
else 1 if score < 0 \
else 2
print ['Player 1', 'Player 2', 'Tie'][winner_idx]
Here is one that follows the story and is expanded to Rock, Paper, Scissors, Lizard, Spock:
import random
def rand_RPSLK():
while True:
yield random.choice('PSKLR')
def simul(n):
p1 = rand_RPSLK()
p2 = rand_RPSLK()
choice = lambda px: 'PSKLR'.index(next(px))
score = sum([[0,1,-1,1,-1][choice(p1)-choice(p2)] for i in xrange(n)])
winner_idx = 0 if score > 0 \
else 1 if score < 0 \
else 2
print ['Player 1', 'Player 2', 'Tie'][winner_idx]
I edited my previous question because I came up with the code I think is correct.
The logic behind this should be:
while the set is not over and it's not a tie 10:10: player A starts serving and does it twice regardless he wins points or not, then player B takes serve and does it twice also. It continues until the set is over, except there is a tie 10:10 when servers change each point scored.
Can anyone check if the code is flawless? thank you.
def simOneSet(probA, probB):
serving = "A"
scoreA = scoreB = 0
while not setOver(scoreA, scoreB):
if scoreA != 10 and scoreB != 10:
if serving == "A":
for i in range(2):
if random() < probA:
scoreA += 1
else:
scoreB += 1
serving = "B"
else:
for i in range(2):
if random() < probB:
scoreB +=1
else:
scoreA += 1
serving = "A"
# when there is a tie 10:10
else:
if serving == "A":
if random() < probA:
scoreA += 1
serving = "B"
else:
scoreB += 1
serving = "B"
else:
if random() < probB:
scoreB += 1
serving = "B"
else:
scoreA += 1
serving = "A"
return scoreA, scoreB
I would use a dict to "switch" between players:
other = {'A':'B', 'B':'A'}
Then, if serving equals 'A', then other[serving] would equal 'B', and if serving equals 'B', then other[serving] would equal 'A'.
You could also use a collections.Counter to keep track of the score:
In [1]: import collections
In [2]: score = collections.Counter()
In [3]: score['A'] += 1
In [4]: score['A'] += 1
In [5]: score['B'] += 1
In [6]: score
Out[6]: Counter({'A': 2, 'B': 1})
Also notice how in this piece of code
if serving == "A":
for i in range(2):
if random() < probA:
scoreA += 1
else:
scoreB += 1
else:
for i in range(2):
if random() < probB:
scoreB +=1
else:
scoreA += 1
there are two blocks which are basically the same idea repeated twice. That's a sign that the code can be tightened-up by using a function. For example, we could define a function serve which when given a probability prob and a player (A or B) returns the player who wins:
def serve(prob, player):
if random.random() < prob:
return player
else:
return other[player]
then the above code would become
for i in range(2):
winner = serve(prob[serving], serving)
score[winner] += 1
Thus, you can compactify your code quite a bit this way:
import random
import collections
other = {'A':'B', 'B':'A'}
def serve(prob, player):
if random.random() < prob:
return player
else:
return other[player]
def simOneSet(probA, probB):
prob = {'A':probA, 'B':probB}
score = collections.Counter()
serving = "A"
while not setOver(score['A'], score['B']):
for i in range(2):
winner = serve(prob[serving], serving)
score[winner] += 1
if score['A'] == 10 and score['B'] == 10:
winner = serve(prob[serving], serving)
score[winner] += 1
serving = winner
return score['A'], score['B']
def setOver(scoreA, scoreB):
return max(scoreA, scoreB) >= 21
print(simOneSet(0.5,0.5))
Here is a hint:
If you have the roundnumber in the set and know which player started serving, you have everything you need to know who is serving.
Then a simple if statement at the start or end of your loop should be enough.
If this is too complicated, try starting by simulating a game where the server starts every round.
Something that might help is the syntax
var = 1 if var == 2 else 2
Which will make var be 1 if var is 2, and var be 2 if var is 1. I feel as though this is a school problem, so I don't want to totally give away the answer :)
Hint: You're on the right track with your thinking.
from random import *
P1=P2=0
while 1 :
p1=p2=0
while 2 :
if random() < 0.5 : p1 +=1
else : p2 +=1
if(p1 >=11 or p2 >=11) and abs(p1-p2) > 1: break
P1 += p1 > p2; P2 += p2 > p1
print "%2d : %2d (%d : %d)" % (p1, p2, P1, P2)
if P1 == 4 or P2 == 4 : break
i hope this helps, it worked for me
I'm trying to simulate n games of craps. The code seems to make sense to me but I never get the right result. For example, if I put in n = 5 i.e. fives games the wins and losses sum to something greater than 5.
Here's how it's supposed to work: if initial roll is 2, 3, or 12, the player loses. If the roll is 7 or 11, the player wins. Any other initial roll causes the player to roll again. He keeps rolling until either he rolls a 7 or the value of the initial roll. If he re-rolls the initial value before rolling a 7, it's a win. Rolling a 7 first is a loss.
from random import randrange
def roll():
dice = randrange(1,7) + randrange (1,7)
return dice
def sim_games(n):
wins = losses = 0
for i in range(n):
if game():
wins = wins + 1
if not game():
losses = losses + 1
return wins, losses
#simulate one game
def game():
dice = roll()
if dice == 2 or dice == 3 or dice == 12:
return False
elif dice == 7 or dice == 11:
return True
else:
dice1 = roll()
while dice1 != 7 or dice1 != dice:
if dice1 == 7:
return False
elif dice1 == dice:
return True
else:
dice1 = roll()
def main():
n = eval(input("How many games of craps would you like to play? "))
w, l = sim_games(n)
print("wins:", w,"losses:", l)
The problem is with
if game():
wins = wins + 1
if not game():
losses = losses + 1
Instead, it should be
if game():
wins = wins + 1
else:
losses = losses + 1
In your code, you are simulating two games instead of one (by calling game() twice). This gives four possible outcomes instead of two (win/loss), giving inconsistent overall results.
In this code
for i in range(n):
if game():
wins = wins + 1
if not game():
losses = losses + 1
you call game() twice, so you play two games right there. What you want is a else block:
for i in range(n):
if game():
wins = wins + 1
else:
losses = losses + 1
Btw, you can simplify the logic with in:
def game():
dice = roll()
if dice in (2,3,12):
return False
if dice in (7,11):
return True
# keep rolling
while True:
new_roll = roll()
# re-rolled the initial value => win
if new_roll==dice:
return True
# rolled a 7 => loss
if new_roll == 7:
return False
# neither won or lost, the while loop continues ..
The code is quite literally the description you gave.
Don't do this
for i in range(n):
if game():
wins = wins + 1
if not game():
losses = losses + 1
It doesn't work out well at all.
There are numerous problems with this code. Most importantly, you're calling game() twice per loop. You need to call it once and store the result, and switch based on that.
An OO rewrite:
import random
try:
rng = xrange # Python 2.x
inp = raw_input
except NameError:
rng = range # Python 3.x
inp = input
def makeNSidedDie(n):
_ri = random.randint
return lambda: _ri(1,n)
class Craps(object):
def __init__(self):
super(Craps,self).__init__()
self.die = makeNSidedDie(6)
self.firstRes = (0, 0, self.lose, self.lose, 0, 0, 0, self.win, 0, 0, 0, self.win, self.lose)
self.reset()
def reset(self):
self.wins = 0
self.losses = 0
def win(self):
self.wins += 1
return True
def lose(self):
self.losses += 1
return False
def roll(self):
return self.die() + self.die()
def play(self):
first = self.roll()
res = self.firstRes[first]
if res:
return res()
else:
while True:
second = self.roll()
if second==7:
return self.lose()
elif second==first:
return self.win()
def times(self, n):
wins = sum(self.play() for i in rng(n))
return wins, n-wins
def main():
c = Craps()
while True:
n = int(inp("How many rounds of craps would you like to play? (0 to quit) "))
if n:
print("Won {0}, lost {1}".format(*(c.times(n))))
else:
break
print("Total: {0} wins, {1} losses".format(c.wins, c.losses))
if __name__=="__main__":
main()