11# uno game #
22
33import random
4+ from typing import List
45
56"""
67Generate the UNO deck of 108 cards.
7- Parameters: None
8- Return values: deck=>list
8+
9+ Doctest examples:
10+
11+ >>> deck = buildDeck()
12+ >>> len(deck)
13+ 108
14+ >>> sum(1 for c in deck if 'Wild' in c)
15+ 8
16+
17+ Return: list of card strings (e.g. 'Red 7', 'Wild Draw Four')
918"""
1019
1120
12- def buildDeck ():
13- deck = []
21+ def buildDeck () -> List [ str ] :
22+ deck : List [ str ] = []
1423 # example card:Red 7,Green 8, Blue skip
1524 colours = ["Red" , "Green" , "Yellow" , "Blue" ]
1625 values = [0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , "Draw Two" , "Skip" , "Reverse" ]
@@ -24,7 +33,6 @@ def buildDeck():
2433 for i in range (4 ):
2534 deck .append (wilds [0 ])
2635 deck .append (wilds [1 ])
27- print (deck )
2836 return deck
2937
3038
@@ -35,10 +43,9 @@ def buildDeck():
3543"""
3644
3745
38- def shuffleDeck (deck ):
39- for cardPos in range (len (deck )):
40- randPos = random .randint (0 , 107 )
41- deck [cardPos ], deck [randPos ] = deck [randPos ], deck [cardPos ]
46+ def shuffleDeck (deck : List [str ]) -> List [str ]:
47+ # use Python's built-in shuffle which is efficient and correct
48+ random .shuffle (deck )
4249 return deck
4350
4451
@@ -48,10 +55,18 @@ def shuffleDeck(deck):
4855"""
4956
5057
51- def drawCards (numCards ):
52- cardsDrawn = []
58+ def drawCards (numCards : int ) -> List [str ]:
59+ """
60+ Draw a number of cards from the top of the global `unoDeck`.
61+
62+ Raises ValueError if the deck runs out of cards.
63+ """
64+ cardsDrawn : List [str ] = []
5365 for x in range (numCards ):
54- cardsDrawn .append (unoDeck .pop (0 ))
66+ try :
67+ cardsDrawn .append (unoDeck .pop (0 ))
68+ except IndexError :
69+ raise ValueError ("The deck is empty; cannot draw more cards" )
5570 return cardsDrawn
5671
5772
@@ -62,7 +77,7 @@ def drawCards(numCards):
6277"""
6378
6479
65- def showHand (player , playerHand ) :
80+ def showHand (player : int , playerHand : List [ str ]) -> None :
6681 print ("Player {}'s Turn" .format (players_name [player ]))
6782 print ("Your Hand" )
6883 print ("------------------" )
@@ -80,7 +95,15 @@ def showHand(player, playerHand):
8095"""
8196
8297
83- def canPlay (colour , value , playerHand ):
98+ def canPlay (colour : str , value : str , playerHand : List [str ]) -> bool :
99+ """
100+ Return True if any card in playerHand is playable on a discard with given colour and value.
101+
102+ >>> canPlay('Red','5',['Red 3','Green 5'])
103+ True
104+ >>> canPlay('Blue','7',['Green 1'])
105+ False
106+ """
84107 for card in playerHand :
85108 if "Wild" in card :
86109 return True
@@ -89,101 +112,118 @@ def canPlay(colour, value, playerHand):
89112 return False
90113
91114
115+ # --- Global deck and initial setup ---
92116unoDeck = buildDeck ()
93117unoDeck = shuffleDeck (unoDeck )
94118unoDeck = shuffleDeck (unoDeck )
95- discards = []
119+ discards : List [ str ] = []
96120
97- players_name = []
98- players = []
121+ players_name : List [ str ] = []
122+ players : List [ List [ str ]] = []
99123colours = ["Red" , "Green" , "Yellow" , "Blue" ]
100- numPlayers = int (input ("How many players?" ))
101- while numPlayers < 2 or numPlayers > 4 :
102- numPlayers = int (
103- input ("Invalid. Please enter a number between 2-4.\n How many players?" )
104- )
105- for player in range (numPlayers ):
106- players_name .append (input ("Enter player {} name: " .format (player + 1 )))
107- players .append (drawCards (5 ))
108-
109-
110- playerTurn = 0
111- playDirection = 1
112- playing = True
113- discards .append (unoDeck .pop (0 ))
114- splitCard = discards [0 ].split (" " , 1 )
115- currentColour = splitCard [0 ]
116- if currentColour != "Wild" :
117- cardVal = splitCard [1 ]
118- else :
119- cardVal = "Any"
120-
121- while playing :
122- showHand (playerTurn , players [playerTurn ])
123- print ("Card on top of discard pile: {}" .format (discards [- 1 ]))
124- if canPlay (currentColour , cardVal , players [playerTurn ]):
125- cardChosen = int (input ("Which card do you want to play?" ))
126- while not canPlay (
127- currentColour , cardVal , [players [playerTurn ][cardChosen - 1 ]]
128- ):
129- cardChosen = int (input ("Not a valid card. Which card do you want to play?" ))
130- print ("You played {}" .format (players [playerTurn ][cardChosen - 1 ]))
131- discards .append (players [playerTurn ].pop (cardChosen - 1 ))
132-
133- # cheak if player won
134- if len (players [playerTurn ]) == 0 :
135- playing = False
136- # winner = "Player {}".format(playerTurn+1)
137- winner = players_name [playerTurn ]
138- else :
139- # cheak for special cards
140- splitCard = discards [- 1 ].split (" " , 1 )
141- currentColour = splitCard [0 ]
142- if len (splitCard ) == 1 :
143- cardVal = "Any"
144- else :
145- cardVal = splitCard [1 ]
146- if currentColour == "Wild" :
147- for x in range (len (colours )):
148- print ("{}) {}" .format (x + 1 , colours [x ]))
149- newColour = int (input ("What colour would you like to choose? " ))
150- while newColour < 1 or newColour > 4 :
151- newColour = int (
152- input ("Invalid option. What colour would you like to choose" )
153- )
154- currentColour = colours [newColour - 1 ]
155- if cardVal == "Reverse" :
156- playDirection = playDirection * - 1
157- elif cardVal == "Skip" :
158- playerTurn += playDirection
159- if playerTurn >= numPlayers :
160- playerTurn = 0
161- elif playerTurn < 0 :
162- playerTurn = numPlayers - 1
163- elif cardVal == "Draw Two" :
164- playerDraw = playerTurn + playDirection
165- if playerDraw == numPlayers :
166- playerDraw = 0
167- elif playerDraw < 0 :
168- playerDraw = numPlayers - 1
169- players [playerDraw ].extend (drawCards (2 ))
170- elif cardVal == "Draw Four" :
171- playerDraw = playerTurn + playDirection
172- if playerDraw == numPlayers :
173- playerDraw = 0
174- elif playerDraw < 0 :
175- playerDraw = numPlayers - 1
176- players [playerDraw ].extend (drawCards (4 ))
177- print ("" )
124+
125+
126+ def main () -> None :
127+ """Run interactive UNO game (keeps original behavior).
128+
129+ Note: main() is interactive and not exercised by doctest.
130+ """
131+ global players_name , players , discards
132+
133+ numPlayers = int (input ("How many players?" ))
134+ while numPlayers < 2 or numPlayers > 4 :
135+ numPlayers = int (
136+ input ("Invalid. Please enter a number between 2-4.\n How many players?" )
137+ )
138+ for player in range (numPlayers ):
139+ players_name .append (input ("Enter player {} name: " .format (player + 1 )))
140+ players .append (drawCards (5 ))
141+
142+ playerTurn = 0
143+ playDirection = 1
144+ playing = True
145+ discards .append (unoDeck .pop (0 ))
146+ splitCard = discards [0 ].split (" " , 1 )
147+ currentColour = splitCard [0 ]
148+ if currentColour != "Wild" :
149+ cardVal = splitCard [1 ]
178150 else :
179- print ("You can't play. You have to draw a card." )
180- players [playerTurn ].extend (drawCards (1 ))
151+ cardVal = "Any"
152+
153+ while playing :
154+ showHand (playerTurn , players [playerTurn ])
155+ print ("Card on top of discard pile: {}" .format (discards [- 1 ]))
156+ if canPlay (currentColour , cardVal , players [playerTurn ]):
157+ cardChosen = int (input ("Which card do you want to play?" ))
158+ while not canPlay (
159+ currentColour , cardVal , [players [playerTurn ][cardChosen - 1 ]]
160+ ):
161+ cardChosen = int (
162+ input ("Not a valid card. Which card do you want to play?" )
163+ )
164+ print ("You played {}" .format (players [playerTurn ][cardChosen - 1 ]))
165+ discards .append (players [playerTurn ].pop (cardChosen - 1 ))
166+
167+ # cheak if player won
168+ if len (players [playerTurn ]) == 0 :
169+ playing = False
170+ # winner = "Player {}".format(playerTurn+1)
171+ winner = players_name [playerTurn ]
172+ else :
173+ # cheak for special cards
174+ splitCard = discards [- 1 ].split (" " , 1 )
175+ currentColour = splitCard [0 ]
176+ if len (splitCard ) == 1 :
177+ cardVal = "Any"
178+ else :
179+ cardVal = splitCard [1 ]
180+ if currentColour == "Wild" :
181+ for x in range (len (colours )):
182+ print ("{}) {}" .format (x + 1 , colours [x ]))
183+ newColour = int (input ("What colour would you like to choose? " ))
184+ while newColour < 1 or newColour > 4 :
185+ newColour = int (
186+ input (
187+ "Invalid option. What colour would you like to choose"
188+ )
189+ )
190+ currentColour = colours [newColour - 1 ]
191+ if cardVal == "Reverse" :
192+ playDirection = playDirection * - 1
193+ elif cardVal == "Skip" :
194+ playerTurn += playDirection
195+ if playerTurn >= numPlayers :
196+ playerTurn = 0
197+ elif playerTurn < 0 :
198+ playerTurn = numPlayers - 1
199+ elif cardVal == "Draw Two" :
200+ playerDraw = playerTurn + playDirection
201+ if playerDraw == numPlayers :
202+ playerDraw = 0
203+ elif playerDraw < 0 :
204+ playerDraw = numPlayers - 1
205+ players [playerDraw ].extend (drawCards (2 ))
206+ elif cardVal == "Draw Four" :
207+ playerDraw = playerTurn + playDirection
208+ if playerDraw == numPlayers :
209+ playerDraw = 0
210+ elif playerDraw < 0 :
211+ playerDraw = numPlayers - 1
212+ players [playerDraw ].extend (drawCards (4 ))
213+ print ("" )
214+ else :
215+ print ("You can't play. You have to draw a card." )
216+ players [playerTurn ].extend (drawCards (1 ))
217+
218+ playerTurn += playDirection
219+ if playerTurn >= numPlayers :
220+ playerTurn = 0
221+ elif playerTurn < 0 :
222+ playerTurn = numPlayers - 1
223+
224+ print ("Game Over" )
225+ print ("{} is the Winner!" .format (winner ))
181226
182- playerTurn += playDirection
183- if playerTurn >= numPlayers :
184- playerTurn = 0
185- elif playerTurn < 0 :
186- playerTurn = numPlayers - 1
187227
188- print ( "Game Over" )
189- print ( "{} is the Winner!" . format ( winner ) )
228+ if __name__ == "__main__" :
229+ main ( )
0 commit comments