Initial commit. Note - this is the original assignment submission. No modifications have been made.
This commit is contained in:
commit
ebf938c48c
7 changed files with 2020 additions and 0 deletions
217
part1.py
Normal file
217
part1.py
Normal file
|
@ -0,0 +1,217 @@
|
|||
# Title: Project 3 - Group scoring
|
||||
# Author: Rory Healy
|
||||
# Date created - 17th May 2019
|
||||
# Date modified - 30th May 2019
|
||||
|
||||
def is_n_of_a_kind(cards):
|
||||
'''Takes a list of cards and returns a Boolean value based on if those
|
||||
cards are said to be "N-of-a-kind".'''
|
||||
|
||||
# Analyse the numbers of the cards to determine validity.
|
||||
numbers_of_cards = []
|
||||
for i in range(len(cards)):
|
||||
numbers_of_cards.append(cards[i][:-1])
|
||||
if numbers_are_same(numbers_of_cards):
|
||||
return True
|
||||
return False
|
||||
|
||||
def numbers_are_same(numbers):
|
||||
'''Takes a list of strings, "numbers", and returns True if all the numbers
|
||||
in the list of strings are the same, and False otherwise.'''
|
||||
original_number = numbers[0]
|
||||
for i in range(len(numbers)):
|
||||
if numbers[i] != original_number:
|
||||
return False
|
||||
return True
|
||||
|
||||
def is_run(cards):
|
||||
'''Determines if a set of cards are said to be a "valid run" and returns a
|
||||
corresponding Boolean value.'''
|
||||
|
||||
# Extracts the suits of all the cards in the list.
|
||||
suits_of_cards = []
|
||||
colour_of_cards = ''
|
||||
red_cards = 'DH'
|
||||
for i in range(len(cards)):
|
||||
suits_of_cards.append(cards[i][-1])
|
||||
for card in suits_of_cards:
|
||||
if card in red_cards:
|
||||
colour_of_cards += 'R'
|
||||
else:
|
||||
colour_of_cards += 'B'
|
||||
|
||||
# Extracts the numbers of the cards in the list.
|
||||
numbers_of_cards = []
|
||||
for i in range(len(cards)):
|
||||
numbers_of_cards.append(cards[i][:-1])
|
||||
|
||||
# Return True/False based on the validity of the suits and numbers.
|
||||
if suits_are_alternating(colour_of_cards) and \
|
||||
numbers_are_monotonic(numbers_of_cards):
|
||||
return True
|
||||
return False
|
||||
|
||||
def suits_are_alternating(suits):
|
||||
'''Takes a string "suits" and returns True if the suits of the string are
|
||||
alternating, and False otherwise.'''
|
||||
for i in range(1, len(suits)):
|
||||
if suits[i] == suits[i - 1]:
|
||||
return False
|
||||
return True
|
||||
|
||||
def numbers_are_monotonic(numbers):
|
||||
'''Takes a list of strings of numbers and returns True if the numbers in
|
||||
the list are monotonic (i.e. strictly increasing or strictly decreasing),
|
||||
where aces can represent any number.'''
|
||||
|
||||
# Replaces Aces with their numerical value, based on the values of the
|
||||
# cards adjacent to the Ace.
|
||||
for i in range(len(numbers)):
|
||||
if numbers[i] == 'A':
|
||||
if (i == 0) or (i == (len(numbers) - 1)):
|
||||
return False
|
||||
|
||||
# The value the a is actually taking depends on whether the cards
|
||||
# are increasing or decreasing in value, so this step is necessary
|
||||
# for dealing with Aces.
|
||||
if int(numbers[0]) < int(numbers[-1]):
|
||||
a_actually_is = int(numbers[i - 1]) + 1
|
||||
elif int(numbers[0]) > int(numbers[-1]):
|
||||
a_actually_is = int(numbers[i + 1]) + 1
|
||||
elif int(numbers[0]) == int(numbers[-1]):
|
||||
return False
|
||||
|
||||
# Validating that the ace represents the correct number.
|
||||
a_should_represent = (int(numbers[i + 1]) + int(numbers[i - 1])) \
|
||||
/2
|
||||
if a_should_represent != a_actually_is:
|
||||
return False
|
||||
else:
|
||||
numbers.insert(i, str(a_actually_is))
|
||||
numbers.remove('A')
|
||||
|
||||
# Checks if the numbers are consecutive.
|
||||
for i in range(len(numbers)):
|
||||
if int(numbers[0]) == int(numbers[-1]):
|
||||
return False
|
||||
|
||||
# For runs that are ascending.
|
||||
elif int(numbers[0]) < int(numbers[-1]):
|
||||
if i == 0:
|
||||
if int(numbers[0]) != (int(numbers[1]) - 1):
|
||||
return False
|
||||
|
||||
elif i == (len(numbers) - 1):
|
||||
if int(numbers[i]) != (int(numbers[i - 1]) + 1):
|
||||
return False
|
||||
|
||||
elif int(numbers[i]) != (int(numbers[i - 1]) + 1):
|
||||
return False
|
||||
|
||||
# For runs that are descending.
|
||||
elif int(numbers[0]) > int(numbers[-1]):
|
||||
if i == 0:
|
||||
if int(numbers[0]) != (int(numbers[1]) + 1):
|
||||
return False
|
||||
|
||||
elif i == (len(numbers) - 1):
|
||||
if int(numbers[i]) != (int(numbers[i - 1]) - 1):
|
||||
return False
|
||||
|
||||
elif int(numbers[i]) != (int(numbers[i - 1]) - 1):
|
||||
return False
|
||||
|
||||
# If the run passes all of these cases, then it must be a valid run.
|
||||
return True
|
||||
|
||||
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 factorial(num):
|
||||
'''Calculates the factorial of the number and returns it.'''
|
||||
if num == 1:
|
||||
return 1
|
||||
else:
|
||||
return num * factorial(num - 1)
|
||||
|
||||
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_score_group(cards):
|
||||
'''Takes a list of cards (each a 2-element string, where the first letter
|
||||
is the card value and the second letter is the card suit), and returns the
|
||||
calculated score of that set of cards as an integer.'''
|
||||
|
||||
score = 0
|
||||
cards = convert_0jqk(cards)
|
||||
cards = order_a_list(cards)
|
||||
|
||||
# Given a list, identifies an n-of-a-kind case or a run.
|
||||
if len(cards) > 1:
|
||||
for i in range(2, len(cards) + 1):
|
||||
# Handles the case where all of the cards are n-of-a-kind.
|
||||
if is_n_of_a_kind(cards):
|
||||
score += int(cards[0][:-1]) * factorial(len(cards))
|
||||
return score
|
||||
|
||||
# Handles the case where only some of the cards are n-of-a-kind
|
||||
# and the rest aren't.
|
||||
elif not is_n_of_a_kind(cards[0:i]):
|
||||
score += int(cards[0][:-1]) * factorial(i - 1)
|
||||
value = int(cards[0][:-1])
|
||||
for j in range(i):
|
||||
if int(cards[0][:-1]) == value:
|
||||
cards.pop(0)
|
||||
|
||||
# Any leftover cards have their values taken from the total score.
|
||||
if len(cards) > 0:
|
||||
for card in cards:
|
||||
value = card[:-1]
|
||||
if value == 'A':
|
||||
value = 20
|
||||
else:
|
||||
value = int(value)
|
||||
score -= value
|
||||
return score
|
180
part2.py
Normal file
180
part2.py
Normal file
|
@ -0,0 +1,180 @@
|
|||
# Title: Project 3 - Group validation
|
||||
# Author: Rory Healy
|
||||
# Date created - 23rd May 2019
|
||||
# Date modified - 30th May 2019
|
||||
|
||||
def is_n_of_a_kind(cards):
|
||||
'''Takes a list of cards and returns a Boolean value based on if those
|
||||
cards are said to be "N-of-a-kind".'''
|
||||
|
||||
# Analyse the numbers of the cards to determine validity.
|
||||
numbers_of_cards = []
|
||||
for i in range(len(cards)):
|
||||
numbers_of_cards.append(cards[i][:-1])
|
||||
if numbers_are_same(numbers_of_cards):
|
||||
return True
|
||||
return False
|
||||
|
||||
def numbers_are_same(numbers):
|
||||
'''Takes a list of strings, "numbers", and returns True if all the numbers
|
||||
in the list of strings are the same, and False otherwise.'''
|
||||
original_number = numbers[0]
|
||||
for i in range(len(numbers)):
|
||||
if numbers[i] != original_number:
|
||||
return False
|
||||
return True
|
||||
|
||||
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):
|
||||
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 card in run:
|
||||
cards.append(card)
|
||||
run.remove(card)
|
||||
|
||||
# Returns all the runs and leftover cards.
|
||||
return_list.append(possible_runs)
|
||||
return_list.append(cards)
|
||||
return_list.append(aces)
|
||||
return return_list
|
||||
|
||||
def suits_are_alternating(suits):
|
||||
'''Takes a string "suits" and returns True if the suits of the string are
|
||||
alternating, and False otherwise.'''
|
||||
for i in range(1, len(suits)):
|
||||
if suits[i] == suits[i - 1]:
|
||||
return False
|
||||
return True
|
||||
|
||||
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 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_valid_groups(groups):
|
||||
'''Takes a list of lists of cards (stored as strings) and returns True or
|
||||
False depending on if it matches the conditions of valiidty as specified
|
||||
in the rules.'''
|
||||
|
||||
# Handles empty lists.
|
||||
if not groups:
|
||||
return True
|
||||
|
||||
for group in groups:
|
||||
|
||||
# Converts all cards in the group to actual numbers (excluding aces)
|
||||
group = convert_0jqk(group)
|
||||
group = order_a_list(group)
|
||||
|
||||
# Checks if the group is either an n-of-a-kind or a run
|
||||
if not is_n_of_a_kind(group):
|
||||
return False
|
||||
|
||||
elif len(group) == 1:
|
||||
aces = []
|
||||
for card in group:
|
||||
if card[0] == 'A':
|
||||
aces.append(card)
|
||||
group.remove(card)
|
||||
runs = is_run(group, aces)
|
||||
if len(runs[0]) == 0 and len(group) != 1:
|
||||
return False
|
||||
return True
|
350
part3.py
Normal file
350
part3.py
Normal file
|
@ -0,0 +1,350 @@
|
|||
# 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
|
197
project03
Normal file
197
project03
Normal file
|
@ -0,0 +1,197 @@
|
|||
--------------------------------------------------------------------------------
|
||||
Part 1 - Valid table
|
||||
|
||||
Write a function comp10001huxxy_valid_table() which takes a single argument:
|
||||
|
||||
- groups, a list of lists of cards (each a 2-element string, where the first
|
||||
letter is the card value and the second letter is the card suit, e.g. '3H' for
|
||||
the 3 of Hearts), where each list of cards represents a single group on the
|
||||
table, and the combined list of lists represents the combined groups played to
|
||||
the table.
|
||||
|
||||
Your function should return a bool, which evaluates whether the table state is
|
||||
valid or not. Recall from the rules of the game that the table is valid if all
|
||||
groups are valid, where a group can take one of the following two forms:
|
||||
|
||||
- an N-of-a-kind (i.e. three or more cards of the same value), noting that in
|
||||
the case of a 3-of-a-kind, each card must have a unique suit (e.g. ['2S',
|
||||
'2S', '2C'] is not a valid 3-of-a-kind, as the Two of Spades has been played
|
||||
twice), and if there are 4 or more cards, all suits must be present.
|
||||
- a run (i.e. a group of 3 or more cards, starting from the lowest-valued card,
|
||||
and ending with the highest-valued card, forming a continuous sequence in
|
||||
terms of value, and alternating in colour; note that the specific ordering of
|
||||
cards in the list is not significant, i.e. ['2C', '3D', '4S'] and ['4S', '2C',
|
||||
'3D'] both make up the same run.
|
||||
|
||||
Example function calls are as follows:
|
||||
|
||||
>>> comp10001huxxy_valid_table([])
|
||||
True
|
||||
>>> comp10001huxxy_valid_table([['AC']])
|
||||
False
|
||||
>>> # run too short
|
||||
>>> comp10001huxxy_valid_table([['AC', '2S']])
|
||||
False
|
||||
>>> # run doesn't alternate in colour
|
||||
>>> comp10001huxxy_valid_table([['AC', '2S', '3H']])
|
||||
False
|
||||
>>> # values not adjacent
|
||||
>>> comp10001huxxy_valid_table([['AC', '2S', '4H']])
|
||||
False
|
||||
>>> comp10001huxxy_valid_table([['AC', '2H', '3S']])
|
||||
True
|
||||
>>> # test unsorted run
|
||||
>>> comp10001huxxy_valid_table([['3C', 'AS', '2H']])
|
||||
True
|
||||
>>> comp10001huxxy_valid_table([['0C', 'JH', 'QS', 'KH', '9D']])
|
||||
True
|
||||
>>> # n-of-kind too short
|
||||
>>> comp10001huxxy_valid_table([['2C', '2H']])
|
||||
False
|
||||
>>> # same suit twice for 3-of-kind
|
||||
>>> comp10001huxxy_valid_table([['2C', '2H', '2C']])
|
||||
False
|
||||
>>> # same suit twice for 4-of-kind
|
||||
>>> comp10001huxxy_valid_table([['2C', '2H', '2S', '2C']])
|
||||
False
|
||||
>>> comp10001huxxy_valid_table([['2C', '2H', '2S']])
|
||||
True
|
||||
>>> comp10001huxxy_valid_table([['2C', '2H', '2S', '2D']])
|
||||
True
|
||||
>>> comp10001huxxy_valid_table([['2C', '2H', '2S', '2D', '2S']])
|
||||
True
|
||||
>>> comp10001huxxy_valid_table([['2C', '2H', '2S', '2D', '2S'],
|
||||
['0D', '9C', '8H']])
|
||||
True
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
Part 2 - Group validation
|
||||
|
||||
Write a function comp10001go_valid_groups() which takes a single argument:
|
||||
|
||||
- groups, a list of groups, each of which is a list of cards (following the same
|
||||
definition as Part 1)
|
||||
|
||||
Your function should return a Boolean indicating whether all groups are valid or
|
||||
not (i.e. a singleton card, a valid N-of-a-kind or a valid run). Note that the
|
||||
function may be used to validate a grouping of partial discards or the full set
|
||||
of discards, i.e. the total number of cards in groups will be between 0 and 10.
|
||||
|
||||
Example function calls are as follows:
|
||||
|
||||
>>> comp10001go_valid_groups([['KC', 'KH', 'KS', 'KD'], ['2C']])
|
||||
True
|
||||
>>> comp10001go_valid_groups([['KC', 'KH', 'KS', 'AD'], ['2C']])
|
||||
False
|
||||
>>> comp10001go_valid_groups([['KC', 'KH', 'KS', 'KD'], ['2C', '3H']])
|
||||
False
|
||||
>>> comp10001go_valid_groups([])
|
||||
True
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
Part 3 - Play and Group!
|
||||
|
||||
The third question requires that you implement the two functions that are called
|
||||
in the tournament: (1) comp10001_play, which is used to select a discard over
|
||||
the 10 turns of a game; and (2) comp10001_group, which is used to group the
|
||||
discards into groups for scoring. We combine these together into a single
|
||||
question in Grok as a means of validating that you have a complete player that
|
||||
is qualified to enter the tournament. Note that in each case, we provide only a
|
||||
single test case (and no hidden test cases) for two reasons: (1) there are very
|
||||
few game states where there is a single possible option to either play a discard
|
||||
or group the discards, and testing relies on there only being one possible
|
||||
output; and (2) the real testing occurs in simulation mode in the actual
|
||||
tournament, in the form of random games against other players. On validation of
|
||||
implementations of each of the two functions, you will be given the option to
|
||||
submit your player to the tournament.
|
||||
|
||||
First, write a function comp10001go_play() which takes three arguments:
|
||||
|
||||
- discard_history, a list of lists of four cards, each representing the discards
|
||||
from each of the four players in preceding turns (up to 9 turns) in sequence
|
||||
of player number (i.e. the first element in each list of four cards is for
|
||||
Player 0, the second is for Player 1, etc.). Note that the list is sequenced
|
||||
based on the turns, i.e. the first list of four cards corresponds to the first
|
||||
turn, and the last list of four cards corresponds to the last turn.
|
||||
- player_no, an integer between 0 and 3 inclusive, indicating which player is
|
||||
being asked to play. player_no can also be used to determine the discards for
|
||||
that player from discard_history by indexing within each list of four cards.
|
||||
- hand, a list of cards held by the player.
|
||||
|
||||
Your function should return a single card to discard from hand.
|
||||
|
||||
An example function call is as follows:
|
||||
|
||||
>>> comp10001go_play([['0S', 'KH', 'AC', '3C'], ['JH', 'AD', 'QS', '5H'],
|
||||
['9C', '8S', 'QH', '9S'], ['8C', '9D', '0D', 'JS'],
|
||||
['5C', 'AH', '5S', '4C'], ['8H', '2D', '6C', '2C'],
|
||||
['8D', '4D', 'JD', 'AS'], ['0H', '6S', '2H', 'KC'],
|
||||
['KS', 'KD', '7S', '6H']], 3, ['QC'])
|
||||
'QC'
|
||||
|
||||
Second, write a function comp10001go_group() which takes two arguments:
|
||||
|
||||
- discard_history, a list of lists of four cards, each representing the discards
|
||||
from each of the four players in preceding turns in sequence of player number
|
||||
(i.e., the first element in each list of four cards is for Player 0, the
|
||||
second is for Player 1, etc.). Note that the list is sequenced based on the
|
||||
turns, i.e. the first list of four cards corresponds to the first turn, and
|
||||
the last list of four cards corresponds to the last turn. Additionally note
|
||||
that the number of turns contained in discard_history will always be 10.
|
||||
- player_no, an integer between 0 and 3 inclusive, indicating which player is
|
||||
being asked to play. player_no can also be used to determine the discards for
|
||||
that player from discard_history by indexing within each list of four cards.
|
||||
|
||||
Your function should return a list of lists of cards based on the discard
|
||||
history of player_no, to use in scoring the player. Note that the grouping of
|
||||
cards represented by the output must be valid (i.e. each list of cards must be a
|
||||
singleton card, or a valid N-of-a-kind or run), but that the ordering of cards
|
||||
within groups, and the ordering of groups is not significant.
|
||||
|
||||
An example function call is as follows:
|
||||
|
||||
>>> comp10001go_group([['0S', 'KH', 'AC', '3C'], ['JH', 'AD', 'QS', '5H'],
|
||||
['9C', '8S', 'QH', '9S'], ['8C', '9D', '0D', 'JS'],
|
||||
['5C', 'AH', '5S', '4C'], ['8H', '2D', '6C', '2C'],
|
||||
['8D', '4D', 'JD', 'AS'], ['0H', '6S', '2H', 'KC'],
|
||||
['KS', 'KD', '7S', '6H'], ['JC', 'QD', '4H', 'QC']], 3)
|
||||
[['3C'], ['5H'], ['9S'], ['JS'], ['4C'],
|
||||
['2C'], ['AS'], ['KC'], ['6H'], ['QC']]
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
Part 4 - Optimal grouping
|
||||
|
||||
The final question is for bonus marks, and is deliberately quite a bit harder
|
||||
than the four basic questions (and the number of marks on offer is, as always,
|
||||
deliberately not commensurate with the amount of effort required). Only attempt
|
||||
this is you have completed the earlier questions, and are up for a challenge!
|
||||
|
||||
Write a function comp10001go_best_partitions() which takes a single argument:
|
||||
|
||||
- cards, a list of up to 10 cards
|
||||
|
||||
Your function should return a list of list of lists of cards, representing the
|
||||
groupings of cards that score the most points from cards. Note that the ordering
|
||||
of the groupings is not significant, and neither is the ordering of the groups
|
||||
within a grouping, nor the order of cards within a group.
|
||||
|
||||
One area of particular focus with this question is efficiency: there are strict
|
||||
time limits associated with running your code over each example, that you must
|
||||
work within, or you will fail the test. Good luck!
|
||||
|
||||
Example function calls are as follows:
|
||||
|
||||
>>> comp10001go_best_partitions(['0H', '8S', '6H', 'AC', '0S',
|
||||
'JS', '8C', '7C', '6D', 'QS'])
|
||||
[[['AC'], ['0H', '0S'], ['JS'], ['8S', '8C'], ['7C'], ['6H', '6D'], ['QS']]]
|
||||
>>> comp10001go_best_partitions(['9D', '2S', '4D', '4H', '6D',
|
||||
'AH', '2C', 'JH', '3C', '9H'])
|
||||
[[['4D', '4H'], ['6D'], ['AH'], ['2S', '2C'],
|
||||
['JH'], ['3C'], ['9D', '9H']]]
|
||||
>>> comp10001go_best_partitions(['3C', '5H', '9S', 'JS', '4C',
|
||||
'2C', 'AS', 'KC', '6H', 'QC'])
|
||||
[[['3C'], ['5H'], ['9S'], ['JS'], ['4C'], ['2C'], ['AS'], ['KC'], ['6H'], ['QC']]]
|
||||
>>> comp10001go_best_partitions(['0D', 'AS', '5C', '8H',
|
||||
'KS', 'AH', 'QH', 'AC'])
|
||||
[[['AS', '5C', '8H', 'AH'], ['0D', 'KS', 'QH', 'AC']],
|
||||
[['0D', 'AS', 'KS', 'QH'], ['5C', '8H', 'AH', 'AC']]]
|
BIN
project03-rubric.pdf
Executable file
BIN
project03-rubric.pdf
Executable file
Binary file not shown.
1076
project03-sample-solutions.py
Normal file
1076
project03-sample-solutions.py
Normal file
File diff suppressed because it is too large
Load diff
BIN
project03.pdf
Executable file
BIN
project03.pdf
Executable file
Binary file not shown.
Loading…
Reference in a new issue