Reputation: 113
Ive been having an issue with getting a random int from a function after going through a while loop. The purpose of the function is to shuffle a deck:
def shuffling(maindeck, shuffle_steps):
random.seed()
# sets a number of steps and creates a new array to be returned
steps = 0
shuffler = maindeck
while steps < shuffle_steps:
firstR = random.randrange(len(maindeck) - 1)
secondR = random.randrange(len(maindeck) - 1)
shuffler[firstR], shuffler[secondR] = shuffler[secondR], shuffler[firstR]
steps +=1
return shuffler
and this is the code that uses the function:
from deck import *
from shuffle import shuffling
gameState = True
while gameState:
input("Welcome to War! Press any key to continue... \n")
game_deck = shuffling(total_deck, 500)
while gameState and len(game_deck) > 1:
print("Both players draw a card...")
playerCard = game_deck.pop(0)
opponentCard = game_deck.pop(0)
# some code
keep_playing = input("Play again? (y/n) \n")
if keep_playing is not ('y' or 'Y'):
gameState = False
gameState = False
if len(game_deck) < 2:
print("No cards left!")
keepPlaying = input("Play again? (y/n) \n")
if keepPlaying is ('y' or 'Y'):
gameState = True
where total_deck is an array from a file deck.py
This code works fine over the first iteration of the while loop, but when the loop iterates I get the error:
ValueError: empty range for randrange()
And that the error occurs when
random.randrange(len(Maindeck) - 1)
is called, since
len(Maindeck) - 1
now evaluates to a number equal to or lower than 0? Why is this?
Upvotes: 1
Views: 525
Reputation: 51643
If you have a set of cards in a list and want to rearrage the whole sequence, simply use random.shuffle(sequence) for an inplace shuffling.
If you want to get a shuffled copy of it, use random.sample() and set the length to the length of the sequence.
This line:
shuffler = maindeck
presuming maindeck
is somekind of mutable list data structure, shuffler
references the same data that maindeck
does - you are shuffling your maindeck
(in disguise) and return it - and further down you are maniputaling it. Youcould use random.sample()
instead wich returns a shuffled copy of your maindeck.
random.seed()
sets the starting-internal state of the Mersenne_Twister that generates your randomness - set it once if you need but not every time you shuffle through your deck. Seeding
your random with a fixed value will lead to the same random numbers given u use the same random operations as each random operation changes the internal state - using it without any seed is not needed, its done by default in the sources: see Python's random: What happens if I don't use seed(someValue)?
Your code should use random.sample(maindeck,k=len(maindeck))
.
Code:
import random
random.seed(42) # generate same states if same operations are used
# https://en.wikipedia.org/wiki/Standard_52-card_deck
ranks = ["ace"] + [str(c) for c in range(2,11)] + ["jack", "queen", "king"]
colors = ["clubs","diamonds","hearts","spades"]
def generate_52_card_deck():
"""Returns a new deck of cards. Each card is a tuple of (rank,color)."""
return [ (r,c) for c in colors for r in ranks ]
deck = generate_52_card_deck()
print(deck)
# inplace
random.shuffle(deck)
print(deck)
new_deck = random.sample(deck,k=52)
print("sampled deck: ", new_deck[:10])
print("original deck: ", deck[::5])
Output:
# after generation (shortened)
[('ace', 'clubs'), ('2', 'clubs'), ('3', 'clubs'), ('4', 'clubs'),
('5', 'clubs'), ('6', 'clubs'), ('7', 'clubs'), ('8', 'clubs'),
('9', 'clubs'), ('10', 'clubs'), ('jack', 'clubs'), ('queen', 'clubs'),
('king', 'clubs'),
('ace', 'diamonds'), ('2', 'diamonds'), ('3', 'diamonds'), ('4', 'diamonds'),
('5', 'diamonds'), ('6', 'diamonds'), ('7', 'diamonds'), ... ,
('jack', 'spades'), ('queen', 'spades'), ('king', 'spades')]
# after shuffling once (shortened)
[('10', 'clubs'), ('jack', 'diamonds'), ('king', 'diamonds'), ('4', 'clubs'),
('9', 'diamonds'), ('king', 'hearts'), ('4', 'diamonds'), ('ace', 'spades'),
('7', 'diamonds'), ('queen', 'clubs'), ('8', 'spades'),
('queen', 'diamonds'), ('8', 'hearts'), ('4', 'hearts'), ...,
('9', 'spades'), ('2', 'clubs'), ('8', 'clubs'), ('2', 'spades')]
# first 10 cards ...
sampled deck: [('4', 'spades'), ('king', 'hearts'), ('ace', 'diamonds'),
('jack', 'clubs'), ('queen', 'hearts'), ('2', 'hearts'),
('6', 'diamonds'), ('3', 'spades'), ('8', 'hearts'),
('9', 'diamonds')]
# first 10 cards
original deck: [('10', 'clubs'), ('jack', 'diamonds'), ('king', 'diamonds'),
('4', 'clubs'), ('9', 'diamonds'), ('king', 'hearts'),
('4', 'diamonds'), ('ace', 'spades'), ('7', 'diamonds'),
('queen', 'clubs')]
If you need the cards value, use:
def get_base_card_value(c):
# ace == 11 not done here
v = {"ace":1 ,"jack":10, "queen":10, "king":10}
return v.get(c[0]) or int(c[0])
Upvotes: 1
Reputation: 2335
Ah! I see the problem! Your Game "uses up" the main deck because the shuffling()
function permutes and returns the original deck:
shuffler = maindeck
does not create a copy of the main deck. Thus
game_deck.pop(0)
takes its cards from the main deck, too.
Fix: make a deep copy of the main deck and use that (in shuffling()
):
import copy
...
shuffler = copy.deepcopy(maindeck)
Et voila! a fresh game deck for every game.
Upvotes: 1