Tenta 2022-08-27#
Information#
Tentamen består av två delar, del A och del B.
För betyg 3 krävs 5/7 på A-delen
För betyg 4 krävs betyg 3 samt 4/7 på B-delen
För betyg 5 krävs betyg 3 samt 6/7 på B-delen
Del B rättas enbart om A-delen är godkänd.
Hjälpmedel#
Via den dator du skriver tentamen på har du åtkomst till:
Tidigare kurshemsidor på svenska/engelska.
En online programmeringsmiljö: Programiz eller Replit. Båda dessa innehåller en editor och ett shell-fönster där du kan skriva pythonkod och testa den.
Länkar till dessa hjälpmedel finns som flikar längst ned till vänster i Inspera.
Eftersom du har tillgång till en programmeringsmiljö kan du testa din kod innan du lämnar in den i Inspera. Du kopierar ditt svar från Programiz/Replit till Inspera. Markera koden med CTRL-A, kopiera med CTRL-C och klistra in i svarsrutan i Inspera med CTRL-V. Ångra med CTRL-Z.
Medan du skriver tentamen sparas dina svar i Inspera var femtonde sekund. Du kan inte öppna tentan igen när du har lämnat in den.
Bedömning#
Såvida inget annat anges får man bygga på lösningar till föregående uppgifter även om dessa inte har lösts. Det är tillåtet att införa extra metoder eller funktioner. Uttryck som ”skriv en funktion som” ska alltså inte tolkas så att lösningen inte får struktureras med hjälp av fler funktioner. Alla uppgifter gäller programmeringsspråket Python och programkod ska skrivas i Python. Koden ska vara läslig, dvs. den ska vara vettigt strukturerad och indenterad. Namn på variabler, funktioner, metoder, klasser etc. ska vara beskrivande men kan ändå hållas ganska korta.
Kommentering av kod behövs ej för del A. Uppgifterna på del B skall kommenteras, men bara om det inte framgår av koden hur du löst uppgiften. Svenska kommentarer är ok.
Observera att betyget (bedömningen) kan påverkas negativt bland annat av:
onödiga variabler
dålig läslighet
upprepning av identisk kod
inkorrekt användning av globala variabler
A1#
Betrakta följande kod
import random
class Animal:
def __init__(self, sound):
self.sound = sound
def make_noise(self):
volume = random.randint(1, 5)
print(self.sound+"!"*volume)
def create_and_sound():
duck = Animal("Quack!")
duck.make_noise()
sheep = Animal("BÄÄ!")
sheep.make_noise()
create_and_sound()
Vad är följande?

A2#
Skriv en funktion names_starting_with_b som tar in en lista med namn och returnerar en ny lista med de namn som börjar på B. Följande kod
names = ["Beata", "Anders", "Börje", "Erik"]
b_names = names_starting_with_b(names)
print(b_names)
ska alltså ge utskriften
['Beata', 'Börje']
Din funktion ska fungera även för listor som innehåller andra namn än i exemplet ovan.
Lösning
def names_on_b(names):
b_names = []
for current_name in names:
if current_name[0] == "B":
b_names.append(current_name)
return b_names
names = ["Beata", "Anders", "Börje", "Erik"]
b_names = names_on_b(names)
print(b_names)
A3#
Du ska åka på semester och vill säkerställa att din resväska inte är för tung. Du har därför skrivit upp vikten på varje sak i väskan i ett lexikon. Alla vikter är angivna i enheten kg.
suitcase_content = {
"towel": "0.3",
"clothes": "2.2",
"shoes": "1.1",
"souvenirs": "10.3"}
Skriv en funktion total_weight som tar ett lexikon med samma struktur som suitcase_content som parameter och returnerar den sammanlagda vikten av innehållet. Koden
weight = total_weight(suitcase_content)
print(f'The suitcase weighs {weight} kg.')
ska ge utskriften
The suitcase weighs 13.9 kg.
Din funktion ska även fungera med andra lexikon med samma struktur som suitcase_content.
Lösning
suitcase_content = {"towel": "0.3",
"clothes": "2.2",
"shoes": "1.1",
"souvenirs": "10.3"}
def total_weight(content):
total_weight = 0
for _, weight in content.items():
total_weight += float(weight)
return total_weight
print(f'The suitcase weighs {total_weight(suitcase_content)} kg.')
A4#
Du har följande kod given:
class Person:
def __init__(self, name):
self.name = name
def demon_name(self):
""""Return a person's demon name"""
return self.name[::-1].upper() + "IUS"
names = ["Göran", "Fredrik", "Torsten"]
Skriv kod som, med hjälp av klassen, skriver ut namn och demonnamn för namnen i listan, names, på följande form:
Göran has the demon name NARÖGIUS
Fredrik has the demon name KIRDERFIUS
Torsten has the demon name NETSROTIUS
Din kod ska fungera även om listan names ändras till en annan lista med namn.
Lösning
class Person:
def __init__(self, name):
self.name = name
def demon_name(self):
""""Return a person's demon name"""
return self.name[::-1].upper() + "IUS"
names = ["Göran", "Fredrik", "Torsten"]
for name in names:
person = Person(name)
print(f"{name} has the demon name {person.demon_name()}")
A5#
I koden till kassan i en mataffär finns två lexikon. Det ena, barcode_to_product
,
har det 13-siffriga tal som motsvarar streckkoden som nycklar och namnen på varorna som värden.
Det andra, product_to_price
, har namnen på varorna som nycklar och varornas pris som värden.
Skriv en funktion
build_barcode_to_price(barcode_to_product, product_to_price)
som returnerar ett lexikon med streckkoder som nycklar och varornas pris som värden.
Följande kod:
barcode_to_product = {
'0001103014391': 'Milk',
'0002032462372': 'Juice',
'0002092551463': 'Cereal',
'0002110442694': 'Yoghurt',
}
product_to_price = {
'Cereal': '44.95 kr',
'Yoghurt': '19.95 kr',
'Juice': '29.95 kr',
'Milk': '18.95 kr'
}
barcode_to_price = build_barcode_to_price(barcode_to_product, product_to_price)
print(barcode_to_price)
ska ge utskriften
{'0001103014391': '18.95 kr', '0002032462372': '29.95 kr', '0002092551463': '44.95 kr', '0002110442694': '19.95 kr'}
Din funktion ska fungera även för andra lexikon med samma struktur.
Lösning
def build_barcode_to_price(barcode_to_product, product_to_price):
barcode_to_price = {}
for barcode, product in barcode_to_product.items():
barcode_to_price[barcode] = product_to_price[product]
return barcode_to_price
barcode_to_product = {
'0001103014391': 'Milk',
'0002032462372': 'Juice',
'0002092551463': 'Cereal',
'0002110442694': 'Yoghurt',
}
product_to_price = {
'Cereal': '44.95 kr',
'Yoghurt': '19.95 kr',
'Juice': '29.95 kr',
'Milk': '18.95 kr'
}
barcode_to_price = build_barcode_to_price(barcode_to_product, product_to_price)
print(barcode_to_price)
A6#
class Point:
"""Represents a point in 2D space.
Instance variables:
self.coords: a tuple of x- and y-coordinates"""
def __init__(self, x_coord, y_coord):
self.coords = (x_coord, y_coord)
def __str__(self):
return f'Point at position (x,y) = {self.coords}.'
def move(self, x_distance, y_distance):
"""Move the point a specified distance.
Moves the point x_distance units in the x-direction and
y_distance units in the y-direction."""
self.coords[0] = self.coords[0] + x_distance
self.coords[1] = self.coords[1] + y_distance
def main():
p = Point(2, 3)
print(p)
p.move(-1, 2)
print(p)
if __name__ == '__main__':
main()
Betrakta koden ovan. Kopiera koden till en editor för att komma igång.
Den givna modulen definierar en klass Point
som representerar en punkt i x-y-planet.
Klassen har en enda instansvariabel, self.coords
, vilken är en tupel som innehåller punktens koordinater.
Metoden move
är tänkt att flytta punkten en given sträcka i x-led och en given sträcka i y-led,
men metoden är just nu felaktig och när modulens main-funktion körs fås ett felmeddelande.
Korrigera metoden move
så att den fungerar som avsett. Du får endast ändra i metoden move. När du är klar bör main-funktionen ge utskriften
Point at position (x,y) = (2, 3).
Point at position (x,y) = (1, 5).
Lösning
class Point:
"""Represents a point in 2D space.
Instance variables:
self.coords: a tuple of x- and y-coordinates"""
def __init__(self, x_coord, y_coord):
self.coords = (x_coord, y_coord)
def __str__(self):
return f'Point at position (x,y) = {self.coords}.'
def move(self, x_distance, y_distance):
"""Move the point a specified distance.
Moves the point x_distance units in the x-direction and
y_distance units in the y-direction."""
self.coords = (self.coords[0] + x_distance,
self.coords[1] + y_distance)
def main():
p = Point(2, 3)
print(p)
p.move(-1, 2)
print(p)
if __name__ == '__main__':
main()
A7#
Skriv en funktion rolls_to_three_in_a_row()
som simulerar att en tärning (med sex sidor) kastas tills den visat samma värde tre gånger i rad.
Funktionen ska returnera antal kast som krävdes innan samma värde erhölls tre gånger i rad.
Utfallet ska vara slumpmässigt, men följande kod
num_rolls = rolls_to_three_in_a_row()
print(f'Needed {num_rolls} rolls to get the same value three times in a row.')
ska till exempel kunna ge utskriften:
Needed 59 rolls to get the same value three times in a row.
Tips 1: random.randint(1, 6)
returnerar ett slumpmässigt heltal mellan 1 och 6.
Tips 2: Notera att funktionen inte behöver ha några parametrar.
Lösning
import random
def rolls_to_three_in_a_row():
three_in_a_row = False
num_rolls = 0
values = [-1, -1, -1]
while not three_in_a_row:
values.pop(0)
values.append(random.randint(1, 6))
num_rolls += 1
three_in_a_row = values[0] == values[1] == values[2]
return num_rolls
num_rolls = rolls_to_three_in_a_row()
print(f'Needed {num_rolls} rolls to get the same value three times in a row.')
B1-B3#
Betrakta koden nedan. Kopiera den till en editor för att komma igång. Instruktioner följer efter koden.
class Card:
"""Represents a playing card in a deck of 52 cards.
The integer number between 0 and 51 uniquely identifies the card.
Numbers 0-12 are clubs.
Numbers 13-25 are diamonds.
Numbers 26-38 are hearts.
Numbers 39-51 are spades.
Each suit is ordered as follows: [A, 2-10, J, Q, K].
"""
def __init__(self, number):
self.number = number
def rank(self):
"""Return card rank.
Card rank ranges from 1 to 13 as follows:
Rank 1: Ace (A)
Ranks 2-10: Cards 2-10
Rank 11: Jack (J)
Rank 12: Queen (Q)
Rank 13: King (K)
"""
return (self.number % 13)+1
def suit(self):
"""Return a symbol for the suit of a card"""
# List of string with symbol for clubs, diamonds, hearts and spades.
suits = [u"\u2663", u"\u2666", u"\u2665", u"\u2660"]
return suits[self.number // 13]
def __str__(self):
values = ["A"] + [str(i) for i in range(2, 11)] + ["J", "Q", "K"]
return values[self.rank()-1] + self.suit()
class DeckOfCards:
"""Represents a deck of playing cards.
The deck is initialized with 52 cards but decreases in size as cards are dealt."""
def __init__(self):
self.cards = [Card(i) for i in range(0, 52)]
def shuffle(self):
"""Shuffle deck by splitting in half and alternating between halves."""
cards1 = self.cards[:26]
cards2 = self.cards[26:]
self.cards = []
for i in range(26):
self.cards.append(cards1[i])
self.cards.append(cards2[i])
def draw_top_card(self):
"""Return the top card and remove it from the deck."""
return self.cards.pop(0)
def deal(self, player, number_of_cards):
"""Deal number_of_cards cards to the incoming player."""
for _ in range(number_of_cards):
player.cards.append(self.draw_top_card())
class Player:
"""Represents a player in a card game."""
def __init__(self, name, blackjack_limit):
"""A player stops drawing cards when the sum of
their cards reaches blackjack_limit."""
self.name = name
self.cards = []
self.points = 0
self.blackjack_limit = blackjack_limit
def __str__(self):
cards_as_str = f'{self.name} holds '
for card in self.cards:
cards_as_str += card.__str__() + " "
return cards_as_str
class BlackjackGame:
"""Modified version of a blackjack game.
The game starts with a single deck (52 cards).
Players compete against each other, not against the house. """
def __init__(self, players):
self.players = players
self.deck = DeckOfCards()
self.rounds_played = 0
# Shuffle deck
num_initial_shuffles = 5
for _ in range(num_initial_shuffles):
self.deck.shuffle()
def card_sum(self, player):
"""Sum the points of the incoming player's cards.
Cards 2-10 are worth 2-10 points.
Jacks, Queens, and Kings (J, Q, and K) are worth 10 points.
Aces (A) are worth 1 point."""
sum = 0
for card in player.cards:
sum += min(card.rank(), 10)
return sum
# --- THIS METHOD IS INCORRECT AND NEEDS CHANGING IN PART B1! ---
def score(self, player):
"""Assign a blackjack score to the incoming player.
If the sum of the points of the player's cards is <= 21, the score equals the sum of points.
If the sum of points is > 21, the score is 0.
"""
return 5*len(player.cards)
# --- THIS METHOD IS INCORRECT AND NEEDS CHANGING IN PART B2! ---
def determine_winners(self):
"""Return a list of all players who scored the best in the current round.
Example 1: Players A and B scored 19, player C scored 18, player D scored 0 (sum over 21).
[A,B] is returned.
Example 2: Players A and B scored 19, player C scored 20, player D scored 0 (sum over 21).
[C] is returned."""
return self.players
def print_winners(self, winning_players):
"""Print the names of all players in the list winning_players."""
if len(winning_players) == 1:
print(
f'{winning_players[0].name} wins round {self.rounds_played}.')
else:
list_of_names = [player.name for player in winning_players]
string_of_names = ", ".join(
list_of_names[0:-1]) + " and " + list_of_names[-1]
print(string_of_names + f' tie round {self.rounds_played}.')
def print_holdings(self):
"""Print all cards currently held by all players in the game, and the corresponding scores."""
for player in self.players:
print(player.__str__() + f'for a score of {self.score(player)}.')
def num_cards_remaining(self):
"""Return the number of cards remaining in the deck."""
return len(self.deck.cards)
def play_round(self):
"""Play a round of blackjack and print results."""
# Make sure all players have empty hands
for player in self.players:
player.cards = []
# Let all players draw cards until they reach their limit
for player in self.players:
while self.card_sum(player) < player.blackjack_limit:
self.deck.deal(player, 1)
# Print all players' cards
print(" ")
self.print_holdings()
# Determine winners of this round
winning_players = self.determine_winners()
# Update number of rounds and print winners of this round
self.rounds_played += 1
self.print_winners(winning_players)
def main():
"""Main function that simulates a game of Blackjack"""
# Initialize player objects
names = ["Martin", "Simon", "Camille"]
blackjack_limits = [16, 17, 18]
players = [Player(names[i], blackjack_limits[i])
for i in range(len(names))]
# Create game
game = BlackjackGame(players)
# Play rounds until fewer than 12 cards remain
while game.num_cards_remaining() >= 12:
game.play_round()
if __name__ == '__main__':
main()
Koden ovan implementerar en enklare version av kortspelet Black Jack. I den här versionen spelar 3 personer (Martin, Simon och Camille) mot varandra. När det är en spelares tur får spelaren dra valfritt antal kort ur leken, ett i taget. Målet är att summan av ens kort ska vara så nära 21 som möjligt, men inte över 21. 21 är bästa möjliga resultat, men kommer man över 21 har man förlorat. Spelaren kan välja att sluta dra kort när den vill.
I koden ovan stannar Martin så fort han nått 16 poäng eller högre. Simon stannar på 17 eller högre och Camille på 18 eller högre.
Korten ger poäng på följande sätt: Korten 2-10 ger sitt siffervärde som poäng. Korten knekt, dam och kung ger vart och ett 10 poäng. Ess ger 1 poäng (förutom i uppgift B3, se nedan).
I koden används följande symboler för korten: A: Ess (Ace) J: Knekt (Jack) Q: Dam (Queen) K: Kung (King)
Den givna koden innehåller flera felaktigheter och därför ser programmets utskrift konstig ut. Du kommer nu få i uppgift att korrigera koden ett steg i taget.
B1. (2 poäng)#
Metoden score i klassen BlackjackGame är felaktig. Ändra så att metoden returnerar poängsumman av de kort som spelaren player har, förutsatt att poängsumman inte överstiger 21. Om poängsumman överstiger 21 ska metoden istället returnera 0. OBS! Du behöver endast ändra i metoden score. Du får anropa lämpliga existerande metoder eller funktioner.
Efter att du korrigerat metoden score bör programmets utskrift bli följande:
Martin holds A♣ 9♣ 4♦ Q♦ for a score of 0.
Simon holds 7♥ 2♠ 10♠ for a score of 19.
Camille holds 6♣ A♦ 9♦ 4♥ for a score of 20.
Martin, Simon and Camille tie round 1.
Martin holds Q♥ 7♠ for a score of 17.
Simon holds 3♣ J♣ 6♦ for a score of 19.
Camille holds A♥ 9♥ 4♠ Q♠ for a score of 0.
Martin, Simon and Camille tie round 2.
Martin holds 8♣ 3♦ J♦ for a score of 21.
Simon holds 6♥ A♠ 9♠ 5♣ for a score of 21.
Camille holds K♣ 8♦ for a score of 18.
Martin, Simon and Camille tie round 3.
Martin holds 3♥ J♥ 6♠ for a score of 19.
Simon holds 2♣ 10♣ 5♦ for a score of 17.
Camille holds K♦ 8♥ for a score of 18.
Martin, Simon and Camille tie round 4.
Martin holds 3♠ J♠ 7♣ for a score of 20.
Simon holds 2♦ 10♦ 5♥ for a score of 17.
Camille holds K♥ 8♠ for a score of 18.
Martin, Simon and Camille tie round 5.
Svar B1
def score(self, player):
"""Assign a blackjack score to the incoming player.
If the sum of the points of the player's cards is <= 21,
the score equals the sum of points.
If the sum of points is > 21, the score is 0.
"""
sum = self.card_sum(player)
if sum > 21:
return 0
else:
return sum
B2. (2 poäng)#
Metoden determine_winners i klassen BlackjackGame är felaktig. Ändra så att metoden returnerar en lista med den/de spelare som vann den senaste rundan. Vinner gör den spelare som har högst score (kom ihåg att en poängsumma över 21 ger score 0). Om flera spelare delar på förstaplatsen räknas de alla som vinnare. OBS! Du behöver endast ändra i metoden determine_winners. Du får anropa lämpliga existerande metoder eller funktioner. Efter att du korrigerat metoden determine_winners (och löst B1 korrekt) bör programmets utskrift bli följande:
Martin holds A♣ 9♣ 4♦ Q♦ for a score of 0.
Simon holds 7♥ 2♠ 10♠ for a score of 19.
Camille holds 6♣ A♦ 9♦ 4♥ for a score of 20.
Camille wins round 1.
Martin holds Q♥ 7♠ for a score of 17.
Simon holds 3♣ J♣ 6♦ for a score of 19.
Camille holds A♥ 9♥ 4♠ Q♠ for a score of 0.
Simon wins round 2.
Martin holds 8♣ 3♦ J♦ for a score of 21.
Simon holds 6♥ A♠ 9♠ 5♣ for a score of 21.
Camille holds K♣ 8♦ for a score of 18.
Martin and Simon tie round 3.
Martin holds 3♥ J♥ 6♠ for a score of 19.
Simon holds 2♣ 10♣ 5♦ for a score of 17.
Camille holds K♦ 8♥ for a score of 18.
Martin wins round 4.
Martin holds 3♠ J♠ 7♣ for a score of 20.
Simon holds 2♦ 10♦ 5♥ for a score of 17.
Camille holds K♥ 8♠ for a score of 18.
Martin wins round 5.
Svar B2
def determine_winners(self):
"""Return a list of all players who scored the best in the current round.
Example 1: Players A and B scored 19, player C scored 18,
player D scored 0 (sum over 21).
[A,B] is returned.
Example 2: Players A and B scored 19, player C scored 20,
player D scored 0 (sum over 21).
[C] is returned."""
num_players = len(self.players)
scores = [self.score(player) for player in self.players]
winning_score = max(scores)
winning_players = [self.players[i]
for i in range(num_players) if scores[i] == winning_score]
return winning_players
B3. (3 poäng)#
Spelarna ska nu få välja att räkna ett Ess som antingen 1 eller 11 poäng. För att åstadkomma detta behöver du ändra i metoden card_sum. Just nu är metoden skriven så att Ess alltid ger 1 poäng. Ändra metoden så att den alltid returnerar det mest fördelaktiga valet.
[Ess, Ess, 5] ska till exempel ge 11+1+5=17 poäng och inte 1+1+5=7 poäng eller 11+11+5=27 poäng.
OBS! Kom ihåg att ändra metodens doc string (dvs. kommentarerna som beskriver vad metoden gör) på lämpligt sätt. Du kan välja att skriva kommentarerna på svenska.
Tips: Summera först poängen på alla kort som inte är Ess. Räkna sedan fram alla möjliga poängsummor som går att få genom att räkna varje Ess som antingen 1 eller 11. Välj sedan det högsta alternativet under 22.
Tips: För att testa att din kod fungerar som avsett när en spelare har två Ess på handen, testa att ändra till num_initial_shuffles = 1 i konstruktorn i klassen BlackjackGame. Detta kommer göra så att Martin drar två Ess i den första rundan.
Svar B3
def card_sum(self, player):
"""Sum the points of the incoming player's cards.
Cards 2-10 are worth 2-10 points.
Jacks, Queens, and Kings (J, Q, and K) are worth 10 points.
Aces (A) are worth 1 or 11 points.
This method selects Ace points that yield the best possible sum
(highest possible value <= 21, if one exists)."""
sum_without_aces = 0
for card in player.cards:
if card.rank() != 1:
sum_without_aces += min(card.rank(), 10)
n_aces = len([card for card in player.cards if card.rank() == 1])
possible_sums = [sum_without_aces + n_aces+10*i
for i in range(n_aces+1)]
sums_less_than_22 = [
score for score in possible_sums if score < 22]
if len(sums_less_than_22) > 0:
return max(sums_less_than_22)
else:
return min(possible_sums)
Lösning hela B1–B3
class Card:
"""Represents a playing card in a deck of 52 cards.
The integer number between 0 and 51 uniquely identifies the card.
Numbers 0-12 are clubs.
Numbers 13-25 are diamonds.
Numbers 26-38 are hearts.
Numbers 39-51 are spades.
Each suit is ordered as follows: [A, 2-10, J, Q, K].
"""
def __init__(self, number):
self.number = number
def rank(self):
"""Return card rank.
Card rank ranges from 1 to 13 as follows:
Rank 1: Ace (A)
Ranks 2-10: Cards 2-10
Rank 11: Jack (J)
Rank 12: Queen (Q)
Rank 13: King (K)
"""
return (self.number % 13)+1
def suit(self):
"""Return a symbol for the suit of a card"""
# List of string with symbol for clubs, diamonds, hearts and spades.
suits = [u"\u2663", u"\u2666", u"\u2665", u"\u2660"]
return suits[self.number // 13]
def __str__(self):
values = ["A"] + [str(i) for i in range(2, 11)] + ["J", "Q", "K"]
return values[self.rank()-1] + self.suit()
class DeckOfCards:
"""Represents a deck of playing cards.
The deck is initialized with 52 cards but decreases in size
as cards are dealt."""
def __init__(self):
self.cards = [Card(i) for i in range(0, 52)]
def shuffle(self):
"""Shuffle deck by splitting in half and alternating between halves."""
cards1 = self.cards[:26]
cards2 = self.cards[26:]
self.cards = []
for i in range(26):
self.cards.append(cards1[i])
self.cards.append(cards2[i])
def draw_top_card(self):
"""Return the top card and remove it from the deck."""
return self.cards.pop(0)
def deal(self, player, number_of_cards):
"""Deal number_of_cards cards to the incoming player."""
for _ in range(number_of_cards):
player.cards.append(self.draw_top_card())
class Player:
"""Represents a player in a card game."""
def __init__(self, name, blackjack_limit):
"""A player stops drawing cards when the sum of
their cards reaches blackjack_limit."""
self.name = name
self.cards = []
self.points = 0
self.blackjack_limit = blackjack_limit
def __str__(self):
cards_as_str = f'{self.name} holds '
for card in self.cards:
cards_as_str += card.__str__() + " "
return cards_as_str
class BlackjackGame:
"""Modified version of a blackjack game.
The game starts with a single deck (52 cards).
Players compete against each other, not against the house. """
def __init__(self, players):
self.players = players
self.deck = DeckOfCards()
self.rounds_played = 0
# Shuffle deck
num_initial_shuffles = 5
for _ in range(num_initial_shuffles):
self.deck.shuffle()
def card_sum(self, player):
"""Sum the points of the incoming player's cards.
Cards 2-10 are worth 2-10 points.
Jacks, Queens, and Kings (J, Q, and K) are worth 10 points.
Aces (A) are worth 1 or 11 points.
This method selects Ace points that yield the best possible sum
(highest possible value <= 21, if one exists)."""
sum_without_aces = 0
for card in player.cards:
if card.rank() != 1:
sum_without_aces += min(card.rank(), 10)
n_aces = len([card for card in player.cards if card.rank() == 1])
possible_sums = [sum_without_aces + n_aces+10*i
for i in range(n_aces+1)]
sums_less_than_22 = [
score for score in possible_sums if score < 22]
if len(sums_less_than_22) > 0:
return max(sums_less_than_22)
else:
return min(possible_sums)
def score(self, player):
"""Assign a blackjack score to the incoming player.
If the sum of the points of the player's cards is <= 21,
the score equals the sum of points.
If the sum of points is > 21, the score is 0.
"""
sum = self.card_sum(player)
if sum > 21:
return 0
else:
return sum
def determine_winners(self):
"""Return a list of all players who scored the best in the current round.
Example 1: Players A and B scored 19, player C scored 18,
player D scored 0 (sum over 21).
[A,B] is returned.
Example 2: Players A and B scored 19, player C scored 20,
player D scored 0 (sum over 21).
[C] is returned."""
num_players = len(self.players)
scores = [self.score(player) for player in self.players]
winning_score = max(scores)
winning_players = [self.players[i]
for i in range(num_players) if scores[i] == winning_score]
return winning_players
def print_winners(self, winning_players):
"""Print the names of all players in the list winning_players."""
if len(winning_players) == 1:
print(
f'{winning_players[0].name} wins round {self.rounds_played}.')
else:
list_of_names = [player.name for player in winning_players]
string_of_names = ", ".join(
list_of_names[0:-1]) + " and " + list_of_names[-1]
print(string_of_names + f' tie round {self.rounds_played}.')
def print_holdings(self):
"""Print all cards currently held by all players in the game,
and the corresponding scores."""
for player in self.players:
print(player.__str__() + f'for a score of {self.score(player)}.')
def num_cards_remaining(self):
"""Return the number of cards remaining in the deck."""
return len(self.deck.cards)
def play_round(self):
"""Play a round of blackjack and print results."""
# Make sure all players have empty hands
for player in self.players:
player.cards = []
# Let all players draw cards until they reach their limit
for player in self.players:
while self.card_sum(player) < player.blackjack_limit:
self.deck.deal(player, 1)
# Print all players' cards
print(" ")
self.print_holdings()
# Determine winners of this round
winning_players = self.determine_winners()
# Update number of rounds and print winners of this round
self.rounds_played += 1
self.print_winners(winning_players)
def main():
"""Main function that simulates a game of Blackjack"""
# Initialize player objects
names = ["Martin", "Simon", "Camille"]
blackjack_limits = [16, 17, 18]
players = [Player(names[i], blackjack_limits[i])
for i in range(len(names))]
# Create game
game = BlackjackGame(players)
# Play rounds until fewer than 12 cards remain
while game.num_cards_remaining() >= 12:
game.play_round()
if __name__ == '__main__':
main()