Three Card Monte
Three-card monte is a common scam played on gullible tourists and other easy marks. Three playing cards, one of which is the “red lady” Queen of Hearts, are put facedown on a cardboard box. The dealer quickly rearranges the cards and then asks the mark to pick the Queen of Hearts. But the dealer can use all sorts of tricks to hide the card or otherwise cheat, guaranteeing that the victim never wins. It’s also common for the dealer to have shills in the crowd who secretly work with the dealer but pretend to win the game (to make the victim think they too could win) or purposefully lose badly (to make the victim think they could do much better).
This program shows the three cards and then quickly describes a series of swaps. At the end, it clears the screen, and the player must pick a card. Can you keep up with the “red lady”? For the authentic three-card monte experience, you can enable the cheat feature, which causes the player to always lose, even if they select the correct card.
three-card_monte.py
1"""Three-Card Monte, by Al Sweigart al@inventwithpython.com
2Find the Queen of Hearts after cards have been swapped around.
3(In the real-life version, the scammer palms the Queen of Hearts so you
4always lose.)
5More info at https://en.wikipedia.org/wiki/Three-card_Monte
6This code is available at https://nostarch.com/big-book-small-python-programming
7Tags: large, card game, game"""
8
9import random, time
10
11# Set up the constants:
12NUM_SWAPS = 16 # (!) Try changing this to 30 or 100.
13DELAY = 0.8 # (!) Try changing this 2.0 or 0.0.
14
15# The card suit characters:
16HEARTS = chr(9829) # Character 9829 is '♥'
17DIAMONDS = chr(9830) # Character 9830 is '♦'
18SPADES = chr(9824) # Character 9824 is '♠'
19CLUBS = chr(9827) # Character 9827 is '♣'
20# A list of chr() codes is at https://inventwithpython.com/chr
21
22# The indexes of a 3-card list:
23LEFT = 0
24MIDDLE = 1
25RIGHT = 2
26
27
28def displayCards(cards):
29 """Display the cards in "cards", which is a list of (rank, suit)
30 tuples."""
31 rows = ['', '', '', '', ''] # Stores the text to display.
32
33 for i, card in enumerate(cards):
34 rank, suit = card # The card is a tuple data structure.
35 rows[0] += ' ___ ' # Print the top line of the card.
36 rows[1] += '|{} | '.format(rank.ljust(2))
37 rows[2] += '| {} | '.format(suit)
38 rows[3] += '|_{}| '.format(rank.rjust(2, '_'))
39
40
41 # Print each row on the screen:
42 for i in range(5):
43 print(rows[i])
44
45
46def getRandomCard():
47 """Returns a random card that is NOT the Queen of Hearts."""
48 while True: # Make cards until you get a non-Queen of hearts.
49 rank = random.choice(list('23456789JQKA') + ['10'])
50 suit = random.choice([HEARTS, DIAMONDS, SPADES, CLUBS])
51
52 # Return the card as long as it's not the Queen of Hearts:
53 if rank != 'Q' and suit != HEARTS:
54 return (rank, suit)
55
56
57print('Three-Card Monte, by Al Sweigart al@inventwithpython.com')
58print()
59print('Find the red lady (the Queen of Hearts)! Keep an eye on how')
60print('the cards move.')
61print()
62
63# Show the original arrangement:
64cards = [('Q', HEARTS), getRandomCard(), getRandomCard()]
65random.shuffle(cards) # Put the Queen of Hearts in a random place.
66print('Here are the cards:')
67displayCards(cards)
68input('Press Enter when you are ready to begin...')
69
70# Print the swaps:
71for i in range(NUM_SWAPS):
72 swap = random.choice(['l-m', 'm-r', 'l-r', 'm-l', 'r-m', 'r-l'])
73
74 if swap == 'l-m':
75 print('swapping left and middle...')
76 cards[LEFT], cards[MIDDLE] = cards[MIDDLE], cards[LEFT]
77 elif swap == 'm-r':
78 print('swapping middle and right...')
79 cards[MIDDLE], cards[RIGHT] = cards[RIGHT], cards[MIDDLE]
80 elif swap == 'l-r':
81 print('swapping left and right...')
82 cards[LEFT], cards[RIGHT] = cards[RIGHT], cards[LEFT]
83 elif swap == 'm-l':
84 print('swapping middle and left...')
85 cards[MIDDLE], cards[LEFT] = cards[LEFT], cards[MIDDLE]
86 elif swap == 'r-m':
87 print('swapping right and middle...')
88 cards[RIGHT], cards[MIDDLE] = cards[MIDDLE], cards[RIGHT]
89 elif swap == 'r-l':
90 print('swapping right and left...')
91 cards[RIGHT], cards[LEFT] = cards[LEFT], cards[RIGHT]
92
93 time.sleep(DELAY)
94
95# Print several new lines to hide the swaps.
96print('\n' * 60)
97
98# Ask the user to find the red lady:
99while True: # Keep asking until LEFT, MIDDLE, or RIGHT is entered.
100 print('Which card has the Queen of Hearts? (LEFT MIDDLE RIGHT)')
101 guess = input('> ').upper()
102
103 # Get the index in cards for the position that the player entered:
104 if guess in ['LEFT', 'MIDDLE', 'RIGHT']:
105 if guess == 'LEFT':
106 guessIndex = 0
107 elif guess == 'MIDDLE':
108 guessIndex = 1
109 elif guess == 'RIGHT':
110 guessIndex = 2
111 break
112
113# (!) Uncomment this code to make the player always lose:
114#if cards[guessIndex] == ('Q', HEARTS):
115# # Player has won, so let's move the queen.
116# possibleNewIndexes = [0, 1, 2]
117# possibleNewIndexes.remove(guessIndex) # Remove the queen's index.
118# newInd = random.choice(possibleNewIndexes) # Choose a new index.
119# # Place the queen at the new index:
120# cards[guessIndex], cards[newInd] = cards[newInd], cards[guessIndex]
121
122displayCards(cards) # Show all the cards.
123
124# Check if the player won:
125if cards[guessIndex] == ('Q', HEARTS):
126 print('You won!')
127 print('Thanks for playing!')
128else:
129 print('You lost!')
130 print('Thanks for playing, sucker!')