Initial commit. Note - this is the original assignment submission. No modifications have been made.

This commit is contained in:
Rory Healy 2024-06-08 21:22:26 +10:00
commit ebf938c48c
Signed by: roryhealy
GPG key ID: 0A3CBDE9C2AE672F
7 changed files with 2020 additions and 0 deletions

217
part1.py Normal file
View 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
View 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
View 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
View 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

Binary file not shown.

File diff suppressed because it is too large Load diff

BIN
project03.pdf Executable file

Binary file not shown.