Quartett/quartett.py

387 lines
13 KiB
Python

"""This module handles everything for the game but the cleaning of user input."""
__author__ = ""
__credits__ = ""
__email__ = ""
import random
import copy
import time
import clean_input
total_time = 1.5
def central_function():
"""
Calls the other function.
Keeps track of played rounds and who is the active player.
Game continues until one player has no cards left.
:return: None
"""
print("Wilkommen bei Quartett! ")
print("Sie können gegen 1-7 Spieler spielen.")
print("Sie können das Spiel bei jeder Eingabe mit 'q' beenden oder mit 'r' neustarten.\n")
switch = True
card_stack, players_with_cards, players, complete_card_stack = initialize()
active = random.randint(0, len(players) - 1) # contains index of the active player
# stores the dropped cards
all_quartets = []
drop_cards(players_with_cards, all_quartets)
if active == 0:
print("Du bist als erstes dran!")
else:
print(f"{players[active]} ist als erstes dran.")
time.sleep(total_time)
while switch:
round(card_stack, players_with_cards, players, active, complete_card_stack, all_quartets)
for i in range(len(players)): # check if any card deck is empty
if not bool(players_with_cards[i]['cards_on_hand']):
switch = False
active += 1
if active >= len(players):
active = 0
the_winner_is(players_with_cards, players)
def round(card_stack, players_with_cards, players, active, complete_card_stack, all_quartets):
"""
Structures one round in the game.
Active player chooses another player from whom to steal a card.
If no card is stolen a card has to be drawn from card_stack.
Also handles AI decisions.
:param card_stack: list of not distributed cards
:param players_with_cards: list of dicts with player name, cards and number of quartets
:param players: list of player names
:param active: index
:param complete_card_stack: list of 32 cards for comparison
:param all_quartets: list of dropped cards
:return: None
"""
switch = True
while switch:
print("")
players_without_active = copy.copy(players)
players_without_active.pop(active)
if players[active] == "player0": # human player
pretty_print_deck(players_with_cards, active)
# chosen_player has the index of player list
if len(players_without_active) > 1:
print('Folgende Spieler stehen zur Verfügung:')
print(players_without_active)
chosen_player = players.index(clean_input.io_str(
'Welchen Spieler möchtest du nach einer Karte fragen? ',
players_without_active))
else:
chosen_player = 1
player_request = clean_input.io_card_selection('Welche Karte möchtest du haben? ')
if player_request in players_with_cards[chosen_player]['cards_on_hand']:
steal(active, chosen_player, players_with_cards, player_request)
drop_cards(players_with_cards, all_quartets, active)
if not players_with_cards[active]['cards_on_hand']:
switch = False
else:
print("Diese Karte hat der Spieler nicht. Der nächste Spieler ist dran...")
time.sleep(total_time)
# end of round
# except if stack is not empty, which is not only the case with two players
if card_stack:
# last card from the stack gets added to active player's hand
draw_card = card_stack.pop()
print(f"Du ziehst die Karte {draw_card['number']}{draw_card['letter']}"
f" vom Stapel.")
players_with_cards[active]['cards_on_hand'].append(draw_card)
drop_cards(players_with_cards, all_quartets, active)
time.sleep(total_time)
switch = False
else: # AI players
# select random player
chosen_player = players.index(random.choice(players_without_active))
# get list of cards that are not on the hand of active AI player
cards_to_choose_from = [c for c in complete_card_stack if
c not in players_with_cards[active]["cards_on_hand"]]
# get list of cards that are not on the hand of active AI player AND not in quartets
cards_to_choose_from = [c for c in cards_to_choose_from if
c not in all_quartets]
# select card
player_request = random.choice(cards_to_choose_from)
if players[chosen_player] == "player0":
print(
f"{players[active]} fragt dich nach "
f"der Karte {player_request['number']}{player_request['letter']}.")
else:
print(
f"{players[active]} fragt {players[chosen_player]} nach "
f"der Karte {player_request['number']}{player_request['letter']}.")
time.sleep(total_time)
if player_request in players_with_cards[chosen_player]['cards_on_hand']:
steal(active, chosen_player, players_with_cards, player_request)
drop_cards(players_with_cards, all_quartets, active)
if not players_with_cards[active]['cards_on_hand']:
switch = False
else:
if players[chosen_player] == "player0":
print(f"Du hast die Karte {player_request['number']}{player_request['letter']}"
f" nicht. "
f"Der nächste Spieler ist dran...")
else:
print(
f"{players[chosen_player]} hat die Karte "
f"{player_request['number']}{player_request['letter']} nicht. "
f"Der nächste Spieler ist dran...")
time.sleep(total_time)
# end of round
# except if stack is not empty, which is not only the case with two players
if card_stack:
# last card from the stack gets added to active player's hand
players_with_cards[active]['cards_on_hand'].append(card_stack.pop())
drop_cards(players_with_cards, all_quartets, active)
switch = False
def pretty_print_deck(players_with_cards, player):
"""
Prints the cards in a players deck in a readable format.
:param players_with_cards: list of dicts with player name, cards and number of quartets
:param player: index
:return: None
"""
pretty_deck = []
for card in players_with_cards[player]['cards_on_hand']:
pretty_deck.append(card['number'] + card['letter'])
if player == 0:
print("Dein Deck: ", pretty_deck)
time.sleep(total_time)
def steal(active, chosen_player, players_with_cards, player_request):
"""
Transfers cards between player decks.
:param active: index
:param chosen_player: index of player to be asked
:param players_with_cards: list of dicts with player name, cards and number of quartets
:param player_request: wanted card
:return: None
"""
if active == 0:
print(f"player{chosen_player} hat die Karte, die du "
"haben möchtest und sie wandert in dein Deck.")
else:
if chosen_player == 0:
print(f"Du hast die Karte, die player{active} "
"haben möchte und sie wandert in sein Deck.")
else:
print(f"player{chosen_player} hat die Karte, die player{active} "
"haben möchte und sie wandert in sein Deck.")
time.sleep(total_time)
card_index = players_with_cards[chosen_player]['cards_on_hand'].index(player_request)
players_with_cards[active]['cards_on_hand'].append(
players_with_cards[chosen_player]['cards_on_hand'].pop(card_index))
def drop_cards(players_with_cards, all_quartets, active=None):
"""
Checks whether human or AI player is supposed to drop cards.
:param players_with_cards: list of dicts with player name, cards and number of quartets
:param all_quartets: list of cards
:param active: index
:return: None
"""
# default, check all players
if active is None:
for p in players_with_cards:
drop_cards_help(p, all_quartets)
else:
p = players_with_cards[active]
drop_cards_help(p, all_quartets)
def drop_cards_help(p, all_quartets):
"""
Counts the how often a letter is in the players hand.
If there is quartet it will be dropped.
The cards a then added to all_quartets.
:param p: dict with player name, cards and number of quartets
:param all_quartets: list of cards
:return: None
"""
reference = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
counter = [0, 0, 0, 0, 0, 0, 0, 0]
for card in p["cards_on_hand"]:
counter[reference.index(card["letter"])] += 1
for i in counter:
if i == 4:
quartet_letter = reference[counter.index(i)]
counter[counter.index(i)] = 0
p["quartet"] += 1
if p["player"] == "player0":
print(f"Du legst das {quartet_letter}-Quartett ab...")
else:
print(f"{p['player']} legt das {quartet_letter}-Quartett ab...")
# build list of cards that are now dropped
q = [c for c in p["cards_on_hand"] if quartet_letter == c["letter"]]
# add quartet cards to list
for c in q:
all_quartets.append(c)
p["cards_on_hand"] = [c for c in p["cards_on_hand"] if quartet_letter != c["letter"]]
def initialize():
"""
Constructs two card stacks. Distributes one of them between the players.
Player number is set here, player names are also constructed here.
:return: list, list (of dicts), list, list (of dicts)
"""
card_stack = []
players = []
players_with_cards = []
# define card stack
x = 0
for i in range(4):
for l in "abcdefgh":
d = {"id": str(x), "number": str(i), "letter": l}
card_stack.append(d)
x += 1
complete_card_stack = copy.copy(card_stack)
# determine number of players
number_of_players = clean_input.io_int(
"Gegen wie viele Spieler wollen Sie spielen (als Ganzzahl)?")
while number_of_players < 1 or number_of_players > 7:
print("Fehler! Sie dürfen lediglich gegen 1-7 Spieler spielen.")
number_of_players = clean_input.io_int(
"Gegen wie viele Spieler wollen Sie spielen (als Ganzzahl)?")
number_of_players += 1 # Add also human player.
for i in range(number_of_players):
players.append(f"player{i}")
# determine number of cards per player
if len(players) == 2:
cards_per_player = 10
else:
cards_per_player = int(len(card_stack) // len(players))
# distribute hand cards to players
for i in range(number_of_players):
cards_of_player = []
for j in range(cards_per_player):
x = random.randint(0, len(card_stack) - 1)
selected_card = card_stack[x]
del card_stack[x]
cards_of_player.append(selected_card)
# create a list that contains every player, their deck and number of made quartets
players_with_cards.append(
{"player": players[i], "cards_on_hand": cards_of_player, "quartet": 0})
return card_stack, players_with_cards, players, complete_card_stack
def the_winner_is(players_with_cards, players):
"""
Counts the number of quartets
a player has made, and chooses the
winner, who got the most.
:param players_with_cards: Main list of dicts with players and their 'hand'
:param players: list of names
:return: None
"""
temp = 1
winners = []
# all potential winners are saved in the winners-list
# (in case there is more than one)
for i in range(len(players)):
winners.append(0)
dropped_quartets = players_with_cards[i]['quartet']
if temp < dropped_quartets:
temp = dropped_quartets
winners.insert(i, dropped_quartets)
if temp == dropped_quartets:
winners[i] = dropped_quartets
# winners then functions as a template
# to print all the players' names who have won
print("")
for i in range(len(winners)):
if winners[i] == temp:
if players[i] == "player0":
print("Du hast das Spiel gewonnen!")
else:
print(f"{players[i]} hat das Spiel gewonnen!")
end_screen()
def end_screen():
"""
Shows the end screen.
:return: None
"""
clean_input.io_str("Möchten Sie das Spiel beenden oder ein neues Spiel starten? "
"Drücken Sie 'q' zum Beenden oder 'r', "
"um ein neues Spiel zu starten...", [])
# Call central_function() only if quartett.py is the main module
if __name__ == "__main__":
# remove end_screen() from the_winner_is() if you want to run tests
# import doctest
# doctest.testfile("tests.txt")
central_function()