Lucky Stars

Tags: large, game, multiplayer

In this push-your-luck game, you roll dice to collect stars. The more you roll, the more stars you can get, but if you get three skulls you lose everything! This quick multiplayer game can support as many players as you want, making it ideal for parties.

On your turn, you pull three random dice from the dice cup and roll them. You can roll Stars, Skulls, and Question Marks. If you end your turn, you get one point per Star. If you choose to roll again, you keep the Question Marks and pull new dice to replace the Stars and Skulls. If you collect three Skulls, you lose all your Stars and end your turn.

When a player gets 13 points, everyone else gets one more turn before the game ends. Whoever has the most points wins.

There are six gold dice, four silver dice, and three bronze dice in the cup. Gold dice have more Stars, bronze dice have more Skulls, and silver is even.

lucky_stars.py
  1"""Lucky Stars, by Al Sweigart al@inventwithpython.com
  2A "press your luck" game where you roll dice to gather as many stars
  3as possible. You can roll as many times as you want, but if you roll
  4three skulls you lose all your stars.
  5
  6Inspired by the Zombie Dice game from Steve Jackson Games.
  7This code is available at https://nostarch.com/big-book-small-python-programming
  8Tags: large, game, multiplayer"""
  9
 10import random
 11
 12# Set up the constants:
 13GOLD = 'GOLD'
 14SILVER = 'SILVER'
 15BRONZE = 'BRONZE'
 16
 17STAR_FACE = ["+-----------+",
 18             "|     .     |",
 19             "|    ,O,    |",
 20             "| 'ooOOOoo' |",
 21             "|   `OOO`   |",
 22             "|   O' 'O   |",
 23             "+-----------+"]
 24SKULL_FACE = ['+-----------+',
 25              '|    ___    |',
 26              '|   /   \\   |',
 27              '|  |() ()|  |',
 28              '|   \\ ^ /   |',
 29              '|    VVV    |',
 30              '+-----------+']
 31QUESTION_FACE = ['+-----------+',
 32                 '|           |',
 33                 '|           |',
 34                 '|     ?     |',
 35                 '|           |',
 36                 '|           |',
 37                 '+-----------+']
 38FACE_WIDTH = 13
 39FACE_HEIGHT = 7
 40
 41print("""Lucky Stars, by Al Sweigart al@inventwithpython.com
 42
 43A "press your luck" game where you roll dice with Stars, Skulls, and
 44Question Marks.
 45
 46On your turn, you pull three random dice from the dice cup and roll
 47them. You can roll Stars, Skulls, and Question Marks. You can end your
 48turn and get one point per Star. If you choose to roll again, you keep
 49the Question Marks and pull new dice to replace the Stars and Skulls.
 50If you collect three Skulls, you lose all your Stars and end your turn.
 51
 52When a player gets 13 points, everyone else gets one more turn before
 53the game ends. Whoever has the most points wins.
 54
 55There are 6 Gold dice, 4 Silver dice, and 3 Bronze dice in the cup.
 56Gold dice have more Stars, Bronze dice have more Skulls, and Silver is
 57even.
 58""")
 59
 60print('How many players are there?')
 61while True:  # Loop until the user enters a number.
 62    response = input('> ')
 63    if response.isdecimal() and int(response) > 1:
 64        numPlayers = int(response)
 65        break
 66    print('Please enter a number larger than 1.')
 67
 68playerNames = []  # List of strings of player names.
 69playerScores = {}  # Keys are player names, values are integer scores.
 70for i in range(numPlayers):
 71    while True:  # Keep looping until a name is entered.
 72        print('What is player #' + str(i + 1) + '\'s name?')
 73        response = input('> ')
 74        if response != '' and response not in playerNames:
 75            playerNames.append(response)
 76            playerScores[response] = 0
 77            break
 78        print('Please enter a name.')
 79print()
 80
 81turn = 0  # The player at playerNames[0] will go first.
 82# (!) Uncomment to let a player named 'Al' start with three points:
 83#playerScores['Al'] = 3
 84endGameWith = None
 85while True:  # Main game loop.
 86    # Display everyone's score:
 87    print()
 88    print('SCORES: ', end='')
 89    for i, name in enumerate(playerNames):
 90        print(name + ' = ' + str(playerScores[name]), end='')
 91        if i != len(playerNames) - 1:
 92            # All but the last player have commas separating their names.
 93            print(', ', end='')
 94    print('\n')
 95
 96    # Start the number of collected stars and skulls at 0.
 97    stars = 0
 98    skulls = 0
 99    # A cup has 6 gold, 4 silver, and 3 bronze dice:
100    cup = ([GOLD] * 6) + ([SILVER] * 4) + ([BRONZE] * 3)
101    hand = []  # Your hand starts with no dice.
102    print('It is ' + playerNames[turn] + '\'s turn.')
103    while True:  # Each iteration of this loop is rolling the dice.
104        print()
105
106        # Check that there's enough dice left in the cup:
107        if (3 - len(hand)) > len(cup):
108            # End this turn because there are not enough dice:
109            print('There aren\'t enough dice left in the cup to '
110                + 'continue ' + playerNames[turn] + '\'s turn.')
111            break
112
113        # Pull dice from the cup until you have 3 in your hand:
114        random.shuffle(cup)  # Shuffle the dice in the cup.
115        while len(hand) < 3:
116            hand.append(cup.pop())
117
118        # Roll the dice:
119        rollResults = []
120        for dice in hand:
121            roll = random.randint(1, 6)
122            if dice == GOLD:
123                # Roll a gold die (3 stars, 2 questions, 1 skull):
124                if 1 <= roll <= 3:
125                    rollResults.append(STAR_FACE)
126                    stars += 1
127                elif 4 <= roll <= 5:
128                    rollResults.append(QUESTION_FACE)
129                else:
130                    rollResults.append(SKULL_FACE)
131                    skulls += 1
132            if dice == SILVER:
133                # Roll a silver die (2 stars, 2 questions, 2 skulls):
134                if 1 <= roll <= 2:
135                    rollResults.append(STAR_FACE)
136                    stars += 1
137                elif 3 <= roll <= 4:
138                    rollResults.append(QUESTION_FACE)
139                else:
140                    rollResults.append(SKULL_FACE)
141                    skulls += 1
142            if dice == BRONZE:
143                # Roll a bronze die (1 star, 2 questions, 3 skulls):
144                if roll == 1:
145                    rollResults.append(STAR_FACE)
146                    stars += 1
147                elif 2 <= roll <= 4:
148                    rollResults.append(QUESTION_FACE)
149                else:
150                    rollResults.append(SKULL_FACE)
151                    skulls += 1
152
153        # Display roll results:
154        for lineNum in range(FACE_HEIGHT):
155            for diceNum in range(3):
156                print(rollResults[diceNum][lineNum] + ' ', end='')
157            print()  # Print a newline.
158
159        # Display the type of dice each one is (gold, silver, bronze):
160        for diceType in hand:
161            print(diceType.center(FACE_WIDTH) + ' ', end='')
162        print()  # Print a newline.
163
164        print('Stars collected:', stars, '  Skulls collected:', skulls)
165
166        # Check if they've collected 3 or more skulls:
167        if skulls >= 3:
168            print('3 or more skulls means you\'ve lost your stars!')
169            input('Press Enter to continue...')
170            break
171
172        print(playerNames[turn] + ', do you want to roll again? Y/N')
173        while True:  # Keep asking the player until they enter Y or N:
174            response = input('> ').upper()
175            if response != '' and response[0] in ('Y', 'N'):
176                break
177            print('Please enter Yes or No.')
178
179        if response.startswith('N'):
180            print(playerNames[turn], 'got', stars, 'stars!')
181            # Add stars to this player's point total:
182            playerScores[playerNames[turn]] += stars
183
184            # Check if they've reached 13 or more points:
185            # (!) Try changing this to 5 or 50 points.
186            if (endGameWith == None
187                and playerScores[playerNames[turn]] >= 13):
188                # Since this player reached 13 points, play one more
189                # round for all other players:
190                print('\n\n' + ('!' * 60))
191                print(playerNames[turn] + ' has reached 13 points!!!')
192                print('Everyone else will get one more turn!')
193                print(('!' * 60) + '\n\n')
194                endGameWith = playerNames[turn]
195            input('Press Enter to continue...')
196            break
197
198        # Discard the stars and skulls, but keep the question marks:
199        nextHand = []
200        for i in range(3):
201            if rollResults[i] == QUESTION_FACE:
202                nextHand.append(hand[i])  # Keep the question marks.
203        hand = nextHand
204
205    # Move on to the next player's turn:
206    turn = (turn + 1) % numPlayers
207
208    # If the game has ended, break out of this loop:
209    if endGameWith == playerNames[turn]:
210        break  # End the game.
211
212print('The game has ended...')
213
214# Display everyone's score:
215print()
216print('SCORES: ', end='')
217for i, name in enumerate(playerNames):
218    print(name + ' = ' + str(playerScores[name]), end='')
219    if i != len(playerNames) - 1:
220        # All but the last player have commas separating their names.
221        print(', ', end='')
222print('\n')
223
224# Find out who the winners are:
225highestScore = 0
226winners = []
227for name, score in playerScores.items():
228    if score > highestScore:
229        # This player has the highest score:
230        highestScore = score
231        winners = [name]  # Overwrite any previous winners.
232    elif score == highestScore:
233        # This player is tied with the highest score.
234        winners.append(name)
235
236if len(winners) == 1:
237    # There is only one winner:
238    print('The winner is ' + winners[0] + '!!!')
239else:
240    # There are multiple tied winners:
241    print('The winners are: ' + ', '.join(winners))
242
243print('Thanks for playing!')

https://inventwithpython.com/bigbookpython/project41.html