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