comp10001-project03/part3.py

351 lines
12 KiB
Python
Raw Normal View History

# Title: Project 3 - Group validation
# Author: Rory Healy
# Date created - 27th May 2019
# Date modified - 30th May 2019
def convert_0jqk(cards):
'''Takes a list of cards and converts special numbers (0, J, Q, K) to
their respective values (10, 11, 12, 13) respectively.'''
# Replaces letters with numbers.
for i in range(len(cards)):
# Replaces Kings with their numerical value - 13.
if cards[i][0] == 'K':
suit = cards[i][1]
cards[i] = '13' + suit
# Replaces Queens with their numerical value - 12.
elif cards[i][0] == 'Q':
suit = cards[i][1]
cards[i] = '12' + suit
# Replaces Jacks with their numerical value - 11.
elif cards[i][0] == 'J':
suit = cards[i][1]
cards[i] = '11' + suit
# Replaces '0', representing 10, with its numerical value.
elif cards[i][0] == '0':
suit = cards[i][1]
cards[i] = '10' + suit
return cards
def convert_10111213(cards):
'''Takes a list of cards and converts values (such as 10, 11, 12 and 13) to
their respective card representation (0, J, Q, K).'''
for i in range(len(cards)):
# Replaces 13 with its value - K.
if cards[i][:-1] == '13':
suit = cards[i][-1]
cards[i] = 'K' + suit
# Replaces 12 with its value - Q.
elif cards[i][:-1] == '12':
suit = cards[i][-1]
cards[i] = 'Q' + suit
# Replaces 11 with its value - J.
elif cards[i][:-1] == '11':
suit = cards[i][-1]
cards[i] = 'J' + suit
# Replaces 10 with its value - 0.
elif cards[i][:-1] == '10':
suit = cards[i][-1]
cards[i] = '0' + suit
return cards
def suits_are_alternating(suits):
'''Takes a list of cards and returns True if the suits of the cards are
alternating, and False otherwise.'''
for i in range(1, len(suits)):
if suits[i][1] == suits[i - 1][1]:
return False
return True
def order_a_list(cards):
'''Orders a list of cards based their numerical value with Aces last.'''
ordered_list = []
numbers_of_cards = []
aces = []
for i in range(len(cards)):
if cards[i][:-1] == 'A':
aces.append('A')
else:
numbers_of_cards.append(cards[i][:-1])
numbers_of_cards.sort(key=int)
numbers_of_cards += aces
for number in numbers_of_cards:
for card in cards:
if number == card[:-1]:
if card in ordered_list:
continue
ordered_list.append(number + card[-1])
return ordered_list
def comp10001go_play(discard_history, player_no, hand):
'''Takes a list of lists, "discard_history", of the previous cards played
by each of the 4 players, an integer "player_no", and a list of cards held
by the player "hand".'''
# Handles the case where there is only 1 card left.
if len(hand) == 1:
return hand[0]
# Copies the hand to a list that can be changed without affecting the hand.
hhand = []
for card in hand:
hhand.append(card)
# Sorts the hand by numerical order, and also sorts the discard pile for
# the current player by numerical order.
hhand = convert_0jqk(hhand)
hhand = order_a_list(hhand)
current_discard = []
for turn in discard_history:
current_discard.append(turn[player_no])
current_discard = convert_0jqk(current_discard)
current_discard = order_a_list(current_discard)
# Extracts the numbers and suits from the discarded pile.
discarded_numbers = []
discarded_suits = []
red_cards = 'DH'
for card in current_discard:
discarded_numbers.append(card[:-1])
discarded_suits.append(card[-1])
discarded_colours = ''
for card in discarded_suits:
if card in red_cards:
discarded_colours += 'R'
else:
discarded_colours += 'B'
# Preferences n-of-a-kind scoring to maximise points by returning the card
# that gives the highest scoring n-of-a-kind.
potential_return_cards = []
for i in range(len(hhand)):
current_card_number = hhand[i][:-1]
for number in discarded_numbers:
if current_card_number == number and number != 'A':
potential_return_cards.append(hand[i])
# A frequency list of the values of the cards in the discard pile is
# created, then sorted by the frequency. The card with the highest
# frequency is always preferred over the card with the highest value due to
# the growth of x!.
freq_dict = {}
for number in discarded_numbers:
if number not in freq_dict.keys():
freq_dict[number] = 1
else:
freq_dict[number] += 1
# Generates a list of tuple of items where the values and keys are swapped.
dict_items = []
for item in freq_dict.items():
key, value = item
dict_items.append((value, key))
# Sorts the tuples by frequency then flips the tuples again.
dict_items = sorted(dict_items, reverse=True)
sorted_items = []
for item in dict_items:
value, key = item
sorted_items.append((key, value))
# Selects the card with the highest frequency from the list of potential
# cards that could be returned.
for item in sorted_items:
key, value = item
for card in potential_return_cards:
if key == card:
return card
# If there aren't any cards that fit in a run, this returns the lowest
# valued card to minimise the points subtracted from singleton cards.
hand_numbers = []
for i in range(len(hhand)):
if hhand[i][:-1] == 'A':
continue
hand_numbers.append(int(hhand[i][:-1]))
for i in range(len(hand)):
if hhand[i][:-1] == 'A':
continue
elif int(hhand[i][:-1]) == min(hand_numbers):
return hand[i]
def is_run(cards, aces):
'''Takes two lists of cards (represented as strings), cards and aces, and
returns a list of the lists of runs that are possible with all the given
cards.'''
return_list = []
possible_runs = []
original_cards_len = len(cards)
for i in range(1, len(cards)):
current_card = cards[0]
current_run = []
if len(possible_runs) > 0:
# This is to prevent any IndexErrors due to the use of a while
# loop within a for loop.
for run in possible_runs:
if len(run) + len(cards) != original_cards_len:
break
else:
continue
break
n = 1
while n < len(cards):
if len(cards) == 1:
break
# This will check that the cards form a run.
elif int(current_card[:-1]) == int(cards[i][:-1]) - n:
current_run.append(cards[i])
cards.remove(cards[i])
n += 1
if len(current_run) > 0:
current_run.append(current_card)
possible_runs.append(current_run)
cards.remove(current_card)
# Numerically sorts the runs.
for i in range(len(possible_runs)):
current_run = order_a_list(possible_runs[i])
possible_runs.pop(i)
possible_runs.insert(i, current_run)
# Inserts any aces where necessary.
for run in possible_runs:
for i in range(len(run)):
if i == len(run) - 1:
break
elif int(run[i][:-1]) != (int(run[i + 1][:-1]) - 1):
if len(aces) > 0:
run.insert(i + 1, aces[0])
aces.pop(0)
break
else:
for card in run:
cards.append(card)
possible_runs.remove(run)
break
# Checks for alternating suits.
for run in possible_runs:
if not suits_are_alternating(run):
for a_card in run:
card = a_card
cards.append(a_card)
possible_runs.remove(run)
# Returns all the runs and leftover cards.
return_list.append(possible_runs)
return_list.append(cards)
return_list.append(aces)
return return_list
def comp10001go_group(discard_history, player_no):
'''Takes a list of the lists of cards played by each player each turn,
"discard_history", and an integer "player_no", and returns a list of lists
of cards based on the discard history of the player that could be used in
scoring.'''
# Extracts the cards that the player has discarded, and orders them into a#
# list.
discarded_cards = []
discarded_numbers = []
for turn in discard_history:
discarded_cards.append(turn[player_no])
discarded_cards = convert_0jqk(discarded_cards)
discarded_cards = order_a_list(discarded_cards)
for card in discarded_cards:
number = card[:-1]
discarded_numbers.append(number)
# Identifies n-of-a-kind cards.
freq_dict = {}
for number in discarded_numbers:
if number not in freq_dict.keys():
freq_dict[number] = 1
else:
freq_dict[number] += 1
# Generates a list of tuple of items where the values and keys are swapped.
dict_items = []
for item in freq_dict.items():
key, value = item
dict_items.append((value, key))
# Sorts the tuples by frequency then flips the tuples again.
dict_items = sorted(dict_items, reverse=True)
sorted_items = []
for item in dict_items:
value, key = item
sorted_items.append([key, value])
# Using "sorted_items", we then need to identify cards who's number occurs
# more than once and removes them from "discarded_cards" and add them to a
# list to be returned.
return_list = []
n_of_a_kind_list = []
for cards in sorted_items:
if cards[1] > 1:
for card in discarded_cards:
if card[0] == 'A':
continue
elif int(card[:-1]) == int(cards[0]):
n_of_a_kind_card = card
n_of_a_kind_list.append(n_of_a_kind_card)
for card in n_of_a_kind_list:
for cards in discarded_cards:
if card == cards:
discarded_cards.remove(cards)
if len(n_of_a_kind_list) > 1:
return_list.append(n_of_a_kind_list)
# Using the cards leftover from the previous check, we need to identify
# runs in the list of discarded cards. To do this, the aces are extracted
# and stored as a seperate list, then the first list is checked to see if a
# run is able to be made and if so, then it is added to the returned list.
ace_list = []
for card in discarded_cards:
if card[0] == 'A':
ace_list.append(card)
for card in ace_list:
for discard in discarded_cards:
if discard == card:
discarded_cards.remove(card)
runs = is_run(discarded_cards, ace_list)
if len(runs[0]) > 0:
for run in runs[0]:
for card in run:
for discard in discarded_cards:
if discard == card:
discarded_cards.remove(card)
return_list.append(run)
# Checks for singletons and adds them to a list to be added to the return
# list.
if len(discarded_cards) > 0:
for card in discarded_cards:
return_list.append([card])
if len(runs[2]) > 0:
for card in runs[2]:
return_list.append([card])
for lst in return_list:
tmp = convert_10111213(lst)
return_list.insert(return_list.index(lst), tmp)
return_list.remove(lst)
return return_list