You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

quartett.py 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. """This module handles everything for the game but the cleaning of user input."""
  2. __author__ = ""
  3. __credits__ = ""
  4. __email__ = ""
  5. import random
  6. import copy
  7. import time
  8. import clean_input
  9. total_time = 1.5
  10. def central_function():
  11. """
  12. Calls the other function.
  13. Keeps track of played rounds and who is the active player.
  14. Game continues until one player has no cards left.
  15. :return: None
  16. """
  17. print("Wilkommen bei Quartett! ")
  18. print("Sie können gegen 1-7 Spieler spielen.")
  19. print("Sie können das Spiel bei jeder Eingabe mit 'q' beenden oder mit 'r' neustarten.\n")
  20. switch = True
  21. card_stack, players_with_cards, players, complete_card_stack = initialize()
  22. active = random.randint(0, len(players) - 1) # contains index of the active player
  23. # stores the dropped cards
  24. all_quartets = []
  25. drop_cards(players_with_cards, all_quartets)
  26. if active == 0:
  27. print("Du bist als erstes dran!")
  28. else:
  29. print(f"{players[active]} ist als erstes dran.")
  30. time.sleep(total_time)
  31. while switch:
  32. round(card_stack, players_with_cards, players, active, complete_card_stack, all_quartets)
  33. for i in range(len(players)): # check if any card deck is empty
  34. if not bool(players_with_cards[i]['cards_on_hand']):
  35. switch = False
  36. active += 1
  37. if active >= len(players):
  38. active = 0
  39. the_winner_is(players_with_cards, players)
  40. def round(card_stack, players_with_cards, players, active, complete_card_stack, all_quartets):
  41. """
  42. Structures one round in the game.
  43. Active player chooses another player from whom to steal a card.
  44. If no card is stolen a card has to be drawn from card_stack.
  45. Also handles AI decisions.
  46. :param card_stack: list of not distributed cards
  47. :param players_with_cards: list of dicts with player name, cards and number of quartets
  48. :param players: list of player names
  49. :param active: index
  50. :param complete_card_stack: list of 32 cards for comparison
  51. :param all_quartets: list of dropped cards
  52. :return: None
  53. """
  54. switch = True
  55. while switch:
  56. print("")
  57. players_without_active = copy.copy(players)
  58. players_without_active.pop(active)
  59. if players[active] == "player0": # human player
  60. pretty_print_deck(players_with_cards, active)
  61. # chosen_player has the index of player list
  62. if len(players_without_active) > 1:
  63. print('Folgende Spieler stehen zur Verfügung:')
  64. print(players_without_active)
  65. chosen_player = players.index(clean_input.io_str(
  66. 'Welchen Spieler möchtest du nach einer Karte fragen? ',
  67. players_without_active))
  68. else:
  69. chosen_player = 1
  70. player_request = clean_input.io_card_selection('Welche Karte möchtest du haben? ')
  71. if player_request in players_with_cards[chosen_player]['cards_on_hand']:
  72. steal(active, chosen_player, players_with_cards, player_request)
  73. drop_cards(players_with_cards, all_quartets, active)
  74. if not players_with_cards[active]['cards_on_hand']:
  75. switch = False
  76. else:
  77. print("Diese Karte hat der Spieler nicht. Der nächste Spieler ist dran...")
  78. time.sleep(total_time)
  79. # end of round
  80. # except if stack is not empty, which is not only the case with two players
  81. if card_stack:
  82. # last card from the stack gets added to active player's hand
  83. draw_card = card_stack.pop()
  84. print(f"Du ziehst die Karte {draw_card['number']}{draw_card['letter']}"
  85. f" vom Stapel.")
  86. players_with_cards[active]['cards_on_hand'].append(draw_card)
  87. drop_cards(players_with_cards, all_quartets, active)
  88. time.sleep(total_time)
  89. switch = False
  90. else: # AI players
  91. # select random player
  92. chosen_player = players.index(random.choice(players_without_active))
  93. # get list of cards that are not on the hand of active AI player
  94. cards_to_choose_from = [c for c in complete_card_stack if
  95. c not in players_with_cards[active]["cards_on_hand"]]
  96. # get list of cards that are not on the hand of active AI player AND not in quartets
  97. cards_to_choose_from = [c for c in cards_to_choose_from if
  98. c not in all_quartets]
  99. # select card
  100. player_request = random.choice(cards_to_choose_from)
  101. if players[chosen_player] == "player0":
  102. print(
  103. f"{players[active]} fragt dich nach "
  104. f"der Karte {player_request['number']}{player_request['letter']}.")
  105. else:
  106. print(
  107. f"{players[active]} fragt {players[chosen_player]} nach "
  108. f"der Karte {player_request['number']}{player_request['letter']}.")
  109. time.sleep(total_time)
  110. if player_request in players_with_cards[chosen_player]['cards_on_hand']:
  111. steal(active, chosen_player, players_with_cards, player_request)
  112. drop_cards(players_with_cards, all_quartets, active)
  113. if not players_with_cards[active]['cards_on_hand']:
  114. switch = False
  115. else:
  116. if players[chosen_player] == "player0":
  117. print(f"Du hast die Karte {player_request['number']}{player_request['letter']}"
  118. f" nicht. "
  119. f"Der nächste Spieler ist dran...")
  120. else:
  121. print(
  122. f"{players[chosen_player]} hat die Karte "
  123. f"{player_request['number']}{player_request['letter']} nicht. "
  124. f"Der nächste Spieler ist dran...")
  125. time.sleep(total_time)
  126. # end of round
  127. # except if stack is not empty, which is not only the case with two players
  128. if card_stack:
  129. # last card from the stack gets added to active player's hand
  130. players_with_cards[active]['cards_on_hand'].append(card_stack.pop())
  131. drop_cards(players_with_cards, all_quartets, active)
  132. switch = False
  133. def pretty_print_deck(players_with_cards, player):
  134. """
  135. Prints the cards in a players deck in a readable format.
  136. :param players_with_cards: list of dicts with player name, cards and number of quartets
  137. :param player: index
  138. :return: None
  139. """
  140. pretty_deck = []
  141. for card in players_with_cards[player]['cards_on_hand']:
  142. pretty_deck.append(card['number'] + card['letter'])
  143. if player == 0:
  144. print("Dein Deck: ", pretty_deck)
  145. time.sleep(total_time)
  146. def steal(active, chosen_player, players_with_cards, player_request):
  147. """
  148. Transfers cards between player decks.
  149. :param active: index
  150. :param chosen_player: index of player to be asked
  151. :param players_with_cards: list of dicts with player name, cards and number of quartets
  152. :param player_request: wanted card
  153. :return: None
  154. """
  155. if active == 0:
  156. print(f"player{chosen_player} hat die Karte, die du "
  157. "haben möchtest und sie wandert in dein Deck.")
  158. else:
  159. if chosen_player == 0:
  160. print(f"Du hast die Karte, die player{active} "
  161. "haben möchte und sie wandert in sein Deck.")
  162. else:
  163. print(f"player{chosen_player} hat die Karte, die player{active} "
  164. "haben möchte und sie wandert in sein Deck.")
  165. time.sleep(total_time)
  166. card_index = players_with_cards[chosen_player]['cards_on_hand'].index(player_request)
  167. players_with_cards[active]['cards_on_hand'].append(
  168. players_with_cards[chosen_player]['cards_on_hand'].pop(card_index))
  169. def drop_cards(players_with_cards, all_quartets, active=None):
  170. """
  171. Checks whether human or AI player is supposed to drop cards.
  172. :param players_with_cards: list of dicts with player name, cards and number of quartets
  173. :param all_quartets: list of cards
  174. :param active: index
  175. :return: None
  176. """
  177. # default, check all players
  178. if active is None:
  179. for p in players_with_cards:
  180. drop_cards_help(p, all_quartets)
  181. else:
  182. p = players_with_cards[active]
  183. drop_cards_help(p, all_quartets)
  184. def drop_cards_help(p, all_quartets):
  185. """
  186. Counts the how often a letter is in the players hand.
  187. If there is quartet it will be dropped.
  188. The cards a then added to all_quartets.
  189. :param p: dict with player name, cards and number of quartets
  190. :param all_quartets: list of cards
  191. :return: None
  192. """
  193. reference = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
  194. counter = [0, 0, 0, 0, 0, 0, 0, 0]
  195. for card in p["cards_on_hand"]:
  196. counter[reference.index(card["letter"])] += 1
  197. for i in counter:
  198. if i == 4:
  199. quartet_letter = reference[counter.index(i)]
  200. counter[counter.index(i)] = 0
  201. p["quartet"] += 1
  202. if p["player"] == "player0":
  203. print(f"Du legst das {quartet_letter}-Quartett ab...")
  204. else:
  205. print(f"{p['player']} legt das {quartet_letter}-Quartett ab...")
  206. # build list of cards that are now dropped
  207. q = [c for c in p["cards_on_hand"] if quartet_letter == c["letter"]]
  208. # add quartet cards to list
  209. for c in q:
  210. all_quartets.append(c)
  211. p["cards_on_hand"] = [c for c in p["cards_on_hand"] if quartet_letter != c["letter"]]
  212. def initialize():
  213. """
  214. Constructs two card stacks. Distributes one of them between the players.
  215. Player number is set here, player names are also constructed here.
  216. :return: list, list (of dicts), list, list (of dicts)
  217. """
  218. card_stack = []
  219. players = []
  220. players_with_cards = []
  221. # define card stack
  222. x = 0
  223. for i in range(4):
  224. for l in "abcdefgh":
  225. d = {"id": str(x), "number": str(i), "letter": l}
  226. card_stack.append(d)
  227. x += 1
  228. complete_card_stack = copy.copy(card_stack)
  229. # determine number of players
  230. number_of_players = clean_input.io_int(
  231. "Gegen wie viele Spieler wollen Sie spielen (als Ganzzahl)?")
  232. while number_of_players < 1 or number_of_players > 7:
  233. print("Fehler! Sie dürfen lediglich gegen 1-7 Spieler spielen.")
  234. number_of_players = clean_input.io_int(
  235. "Gegen wie viele Spieler wollen Sie spielen (als Ganzzahl)?")
  236. number_of_players += 1 # Add also human player.
  237. for i in range(number_of_players):
  238. players.append(f"player{i}")
  239. # determine number of cards per player
  240. if len(players) == 2:
  241. cards_per_player = 10
  242. else:
  243. cards_per_player = int(len(card_stack) // len(players))
  244. # distribute hand cards to players
  245. for i in range(number_of_players):
  246. cards_of_player = []
  247. for j in range(cards_per_player):
  248. x = random.randint(0, len(card_stack) - 1)
  249. selected_card = card_stack[x]
  250. del card_stack[x]
  251. cards_of_player.append(selected_card)
  252. # create a list that contains every player, their deck and number of made quartets
  253. players_with_cards.append(
  254. {"player": players[i], "cards_on_hand": cards_of_player, "quartet": 0})
  255. return card_stack, players_with_cards, players, complete_card_stack
  256. def the_winner_is(players_with_cards, players):
  257. """
  258. Counts the number of quartets
  259. a player has made, and chooses the
  260. winner, who got the most.
  261. :param players_with_cards: Main list of dicts with players and their 'hand'
  262. :param players: list of names
  263. :return: None
  264. """
  265. temp = 1
  266. winners = []
  267. # all potential winners are saved in the winners-list
  268. # (in case there is more than one)
  269. for i in range(len(players)):
  270. winners.append(0)
  271. dropped_quartets = players_with_cards[i]['quartet']
  272. if temp < dropped_quartets:
  273. temp = dropped_quartets
  274. winners.insert(i, dropped_quartets)
  275. if temp == dropped_quartets:
  276. winners[i] = dropped_quartets
  277. # winners then functions as a template
  278. # to print all the players' names who have won
  279. print("")
  280. for i in range(len(winners)):
  281. if winners[i] == temp:
  282. if players[i] == "player0":
  283. print("Du hast das Spiel gewonnen!")
  284. else:
  285. print(f"{players[i]} hat das Spiel gewonnen!")
  286. end_screen()
  287. def end_screen():
  288. """
  289. Shows the end screen.
  290. :return: None
  291. """
  292. clean_input.io_str("Möchten Sie das Spiel beenden oder ein neues Spiel starten? "
  293. "Drücken Sie 'q' zum Beenden oder 'r', "
  294. "um ein neues Spiel zu starten...", [])
  295. # Call central_function() only if quartett.py is the main module
  296. if __name__ == "__main__":
  297. # remove end_screen() from the_winner_is() if you want to run tests
  298. # import doctest
  299. # doctest.testfile("tests.txt")
  300. central_function()