How to randomize the card selection

Hey fellow learners.
I thought of a fun twist to this interesting exercise - I thought - it would be much more thrilling if the cards were actually randomly assigned, rather than written in by the corresponding key.

I’ve written and run the code, myy result was:
Your past is the Justice card.
Your present is the Strength card.
Your future is the The Magician card.

You can try to write the code on your own, here are some tips:

  • We need numpy.py - we’ve used this library before. We can bring its functionality into our program with a command: import numpy as np . As a reminder numpy is a mathematic library with a bunch of useful functions.
  • numpy.random.choice() will be the most suitable option to choose the card. It takes a list of numbers and returns a random choice from that list.
  • In order to retreive each card will need to use the list of all available card indices, which hold all the currently present keys in the tarot list as an argument for np.random.choice(). Convenient for us, once the card is ‘popped’ it disappears from the list together with its index, and if we update the list each time np.random.choice() won’t consider that index again.

If you’re stuck somewhere or it’s too difficult here’s the code:

tarot = { 1:	"The Magician", 2:	"The High Priestess", 3:	"The Empress", 4:	"The Emperor", 5:	"The Hierophant", 6:	"The Lovers", 7:	"The Chariot", 8:	"Strength", 9:	"The Hermit", 10:	"Wheel of Fortune", 11:	"Justice", 12:	"The Hanged Man", 13:	"Death", 14:	"Temperance", 15:	"The Devil", 16:	"The Tower", 17:	"The Star", 18:	"The Moon", 19:	"The Sun", 20:	"Judgement", 21:	"The World", 22: "The Fool"}
#importing numpy
import numpy as np
spread = {}

#following loop updates the available card list each turn 
#and creates needed dictionary entries, while removing used cards
for each in ['past','present','future']:
  cards_left = list(tarot.keys())
  spread[each] = tarot.pop(np.random.choice(cards_left))
  
for key, value in spread.items():
  print("Your {} is the {} card.".format(key, value))

Yours is a much cleaner method than what I came up with, but I haven’t gone through a Numpy course (yet). I know of the random library though , so I came up with the below.

# Create a list of 3 random numbers to pull for the tarot cards.
random_lst = []
for i in range(3):
  rand_num = random.randint(1,22)
  while rand_num in random_lst:
    rand_num = random.randint(1,22)
  random_lst.append(rand_num)

spread = {}
spread['past'] = tarot.pop(random_lst[0])
spread['present'] = tarot.pop(random_lst[1])
spread['future'] = tarot.pop(random_lst[2])

I am using the base code that the project used, and just randomly coming up with which key’s to pop.

7 Likes

You came up with that just with the knowledge from this course or did you had some other coding experience? I’m asking cause there’s no way I could do it and I’m wondering if I’m just stupid or people like you had some other experiences that learned them how to think like a programmer.

Man, I don’t even fully understand what your code is doing :confused:

8 Likes

My code is doing the following.

  1. Generating a random number between 1 and 22
  2. Verifying that that number hasn’t already been selected by seeing if it is in ‘random_lst’
  3. Adding the new number to the ‘random_lst’
    After the above is complete I have the ‘random_lst’ list with 3 different and random numbers that I can use as key’s to pull the values with from the tarot dictionary.

Everything except maybe the random library has been covered up to this point.

I have limited coding experience from my job, but all that experience is in C++ and is very basic automation control. With that said, I do have an technical degree that required a couple programming classes, but nothing overly advanced.

Just keep with it and you’ll get there.

7 Likes

I don’t understand why we need numpy.
I just uses random and it works. Is there a specific reason you use it?

5 Likes

numpy library has a larger variety of mathematical functions, thus is more preferred generally speaking instead of adding for example the random and sqrt function from separate modules.

2 Likes
import random

tarot = { 1:	"The Magician", 2:	"The High Priestess", 3:	"The Empress", 4:	"The Emperor", 5:	"The Hierophant", 6:	"The Lovers", 7:	"The Chariot", 8:	"Strength", 9:	"The Hermit", 10:	"Wheel of Fortune", 11:	"Justice", 12:	"The Hanged Man", 13:	"Death", 14:	"Temperance", 15:	"The Devil", 16:	"The Tower", 17:	"The Star", 18:	"The Moon", 19:	"The Sun", 20:	"Judgement", 21:	"The World", 22: "The Fool"}

def tarot_card():
  spread = {}
  spread["past"] = tarot.pop(random.choice(list(tarot)))
  spread["present"] = tarot.pop(random.choice(list(tarot)))
  spread["future"] = tarot.pop(random.choice(list(tarot)))
  for key, value in spread.items():
    print("Your " + key +" is the " + str(value) + " card." )

tarot_card()
17 Likes

good stuff. simple, clean, easy to read and understand. well done.

1 Like
import random
spread= {}

for i in ["past", "present", "future"]:
  card_left = list(tarot.keys())
  spread[i] = tarot.pop(random.choice(card_left))
  print("Your {} is the {} card.".format(i, spread.get(i)))
9 Likes

This is my favorite solution. One question. Is the .keys() necessary? Wouldn’t cards_left = list(tarot) return the same thing?

Very nice idea, came to similar solution, I’ve just RETURNed my dictionary to be useful externally in program (don’t mind Polish words, I’ve translated some parts and defined variables in Polish for myself as an exercise):

tarot2 = { 1:	"Mag", 2:	"Najwyższa Kapłanka", 3:	"Cesarzowa", 4:	"Cesarz", 
5:	"Hierofant", 6:	"Kochankowie", 7:	"Rydwan", 8:	"Siła", 9:	"Eremita", 
10:	"Koło Fortuny", 11:	"Sprawiedliwość", 12:	"Wisielec", 13:	"Śmierć", 14:	"Powściągliwość", 
15:	"Diabeł", 16:	"Wieża", 17:	"Gwiazda", 18:	"Księżyc", 19:	"Słońce", 20:	"Sąd Ostateczny", 
21:	"Świat", 22: "Błazen"}

def rozdanie():
    lista = {}
    lista["przeszłość"] = tarot2.pop(random.choice(list(tarot2)))
    lista["teraźniejszość"] = tarot2.pop(random.choice(list(tarot2)))
    lista["przyszłość"] = tarot2.pop(random.choice(list(tarot2)))
    return lista

for key, value in rozdanie().items():
    print("Twoją " +key+ " określa karta " +str(value)+ ".")

My solution uses the random module’s sample() function, which takes a range object and picks k elements from the range, without replacement. (Can’t remember whether I learned about sample() from the forums here or while looking up the Python documentation while learning):

# Challenge: Randomize card selection

import random
tarot = { 1:	"The Magician", 2:	"The High Priestess", 3:	"The Empress", 4:	"The Emperor", 5:	"The Hierophant", 6:	"The Lovers", 7:	"The Chariot", 8:	"Strength", 9:	"The Hermit", 10:	"Wheel of Fortune", 11:	"Justice", 12:	"The Hanged Man", 13:	"Death", 14:	"Temperance", 15:	"The Devil", 16:	"The Tower", 17:	"The Star", 18:	"The Moon", 19:	"The Sun", 20:	"Judgement", 21:	"The World", 22: "The Fool"}
period_list = ['past', 'present', 'future']
spread_list = []

# Draw 3 randomized cards
spread_list = random.sample(range(1, len(tarot)+1), 3)

# Lookup the values and print
for period, number in zip(period_list, spread_list): # just remember that a zip object is exhausted after one iteration
    print(f'Your {period} is the {tarot[number]} card.')

My solution doesn’t need a new dictionary, just a new list, though if I wanted to pair the drawn cards with their periods for future use instead of just printing, I’d probably use a dictionary.

I also like that my solution doesn’t permanently remove any cards from the tarot dictionary, which means the “deck” remains complete and can be used again.

1 Like

Another solution is to use the random module’s shuffle() function, which you may have seen in this forum post off the Modules lesson. Basically, you’d first extract a list of the keys of the dictionary, shuffle the list in place using shuffle(), access the first 3 elements of the list, and then look up the values of those keys in the dictionary.

This solution also preserves the original dictionary. But is additionally cool (in my opinion) because its algorithm more closely tracks with the actions you’d take in real life to do a tarot read: shuffle a pack of cards, and then pick the first 3!

Why would having a larger number of mathematical functions automatically be preferred? If the goal of your program is simply to return a random card out of a deck, then random should be sufficient (unless you plan on adding more functionality later).

For this code to work you need to import random

import random

Once I added this it ran without errors

Great job! This code is super tight!

Say you wanted to do this without altering the original tarot dictionary and without creating a new global variable, is it even possible? I know that trying to keep memory use down isn’t super important right now, but I was just curious about it.

For instance, I tried to do this by replacing your first for loop:

for each in ['past','present','future']:
  cards_left = list(tarot.keys())
  spread[each] = tarot.get(cards_left.pop(np.random.choice(cards_left)))

but that just recreates a full cards_left in every loop.

I did this instead to solve the problem:

cards_left = list(tarot.keys())

#following loop updates the available card list each turn 
#and creates needed dictionary entries, while removing used cards
for each in ['past','present','future']:
    spread[each] = tarot.get(cards_left.pop(np.random.choice(cards_left)))

but that creates a cards_left variable that won’t get used again.

Is it a case of in a live application of code, if you were going to be in a situation where you were going to use the tarot card dictionary multiple times, you would define a random_choice function to do all of this, which wouldn’t lead to the extra bit of memory lying around? Am I overthinking this in the pursuit of trying to learn best practice from the start?

I used the sample as well, but my first instinct was to just make a list of 3 numbers and have my spread.updates call a list item:

from random import sample
tarot = { 1: "The Magician", 2: "The High Priestess", 3: "The Empress", 4: "The Emperor", 5: "The Hierophant", 6: "The Lovers", 7: "The Chariot", 8: "Strength", 9: "The Hermit", 10: "Wheel of Fortune", 11: "Justice", 12: "The Hanged Man", 13: "Death", 14: "Temperance", 15: "The Devil", 16: "The Tower", 17: "The Star", 18: "The Moon", 19: "The Sun", 20: "Judgement", 21: "The World", 22: "The Fool"}

draw = sample(range(1, 23), 3)
spread = {}
spread.update({"past": tarot.get(draw[0])})
spread.update({"present": tarot.get(draw[1])})
spread.update({"future": tarot.get(draw[2])})
for key, value in spread.items():
  print(f"Your {key} is the {value} card.")

after looking at this thread I realized I should replace tarot.pop with .get

I think I edited this 5 times but at least I know how to post code now…

My solution. I don’t have numpy on my PC so I just used random. Sorry for the formatting I’m new to the forum

from random import randrange tarot = { 1: "The Magician", 2: "The High Priestess", 3: "The Empress", 4: "The Emperor", 5: "The Hierophant", 6: "The Lovers", 7: "The Chariot", 8: "Strength", 9: "The Hermit", 10: "Wheel of Fortune", 11: "Justice", 12: "The Hanged Man", 13: "Death", 14: "Temperance", 15: "The Devil", 16: "The Tower", 17: "The Star", 18: "The Moon", 19: "The Sun", 20: "Judgement", 21: "The World", 22: "The Fool"} spread = {} tarotlist = list(tarot) spread["past"] = tarot.get(tarotlist.pop(randrange(0,len(tarotlist)))) spread["present"] = tarot.get(tarotlist.pop(randrange(0,len(tarotlist)))) spread["future"] = tarot.get(tarotlist.pop(randrange(0,len(tarotlist)))) for key, value in spread.items(): print("Your " + key + " is the " + value + " card.")

I tested it and you’re right: it seems to be working flawlessly.
I love this solution since it’s so concise, here’s how I simplified it based on your comment:

import random tarot = { 1: "The Magician", 2: "The High Priestess", 3: "The Empress", 4: "The Emperor", 5: "The Hierophant", 6: "The Lovers", 7: "The Chariot", 8: "Strength", 9: "The Hermit", 10: "Wheel of Fortune", 11: "Justice", 12: "The Hanged Man", 13: "Death", 14: "Temperance", 15: "The Devil", 16: "The Tower", 17: "The Star", 18: "The Moon", 19: "The Sun", 20: "Judgement", 21: "The World", 22: "The Fool"} spread = {} for time in ["past", "present", "future"]: cards_left = list(tarot) spread[time] = tarot.pop(random.choice(cards_left)) print(f"Your {time} is the {spread[time]} card.")

Seven lines only! I also used f-strings since I personally prefer their syntax and legibility.

1 Like

I’m far from advanced, but when I started programming I felt really dumb and lost too. It gets better with time as you learn new concepts. randint is used on the Python 2/3 course in Codecademy.