Skip to content

Instantly share code, notes, and snippets.

@trvswgnr
Created December 1, 2022 06:45
Show Gist options
  • Save trvswgnr/663eaafdb8aa3af4e1514df9b8d192d1 to your computer and use it in GitHub Desktop.
Save trvswgnr/663eaafdb8aa3af4e1514df9b8d192d1 to your computer and use it in GitHub Desktop.
proj10.py
from cards import Card, Deck
MENU ='''Prompt the user for an option and check that the input has the
form requested in the menu, printing an error message, if not.
Return:
TT s d: Move card from end of Tableau pile s to end of pile d.
TF s d: Move card from end of Tableau pile s to Foundation d.
WT d: Move card from Waste to Tableau pile d.
WF d: Move card from Waste to Foundation pile d.
SW : Move card from Stock to Waste.
R: Restart the game (after shuffling)
H: Display this menu of choices
Q: Quit the game
'''
def initialize():
"""
The function has no parameters and returns the starting state of the game with the four data structures initialized as described above.
Check the Game rules section to see how to deal cards to each data structure.
Remember that stock is of type Class Deck. Also, we need all tableau cards face down except last cards in each column.
Cards are initially face up (as defined by the Card class) so turn all tableau cards face down.
Then, turn last card in each tableau column face up (Hint: use the flip_card() method from the Card class).
You will need to shuffle the stock when you create it. Use lists to create the structures.
"""
# initialize the stock ([10♣, 4♥, Q♠, Q♣, 7♠, K♦, 10♦, 2♥, 7♥, 8♣, 9♥, 4♠, 6♣, J♠, 4♦, 4♣, A♦, 9♦, 2♦, 7♦, 10♥, 5♥, A♠])
stock = Deck()
stock.shuffle()
# initialize the waste
waste = []
# initialize the foundation ([[], [], [], []])
foundation = [[],[],[],[]]
# initialize the tableau
tableau = [[],[],[],[],[],[],[]]
# deal cards to the tableau and turn all cards face down except last cards in each column ( tableau = [[ Q♥], [ 3♥, 3♦], [ 6♦, J♣, A♣], [ 2♠, 5♠, 3♣, 9♣], [ 8♠, Q♦, J♥, 5♦, 6♠], [ J♦, 9♠, K♥, 8♥, 6♥, A♥], [ 2♣, K♠, 3♠, K♣, 8♦, 10♠, 7♣]] )
# One card is placed in each of the 7 columns, from left to right
# A second card is placed in the rightmost 6 columns, from left to right A third card is placed in the rightmost 5 columns, from left to right A fourth card is placed in the rightmost 4 columns, from left to right A fifth card is placed in the rightmost 3 columns, from left to right
# A sixth card is placed in the rightmost 2 columns, from left to right A seventh card is placed in the rightmost column
for i in range(7):
for j in range(i, 7):
tableau[j].append(stock.deal())
# turn all tableau cards face down except last cards in each column
if j != i:
flip_last(tableau[j])
# The top card in the stock is turned over and placed face up in the waste pile
waste.append(stock.deal())
return tableau, stock, foundation, waste
def display(tableau, stock, foundation, waste):
""" display the game setup """
stock_top_card = "empty"
found_top_cards = ["empty","empty","empty","empty"]
waste_top_card = "empty"
if len(waste):
waste_top_card = waste[-1]
if len(stock):
stock_top_card = "XX" #stock[-1]
for i in range(4):
if len(foundation[i]):
found_top_cards[i] = foundation[i][-1]
print()
print("{:5s} {:5s} \t\t\t\t\t {}".format("stock","waste","foundation"))
print("\t\t\t\t ",end = '')
for i in range(4):
print(" {:5d} ".format(i+1),end = '')
print()
print("{:5s} {:5s} \t\t\t\t".format(str(stock_top_card), str(waste_top_card)), end = "")
for i in found_top_cards:
print(" {:5s} ".format(str(i)), end = "")
print()
print()
print()
print()
print("\t\t\t\t\t{}".format("tableau"))
print("\t\t ", end = '')
for i in range(7):
print(" {:5d} ".format(i+1),end = '')
print()
# calculate length of longest tableau column
max_length = max([len(stack) for stack in tableau])
for i in range(max_length):
print("\t\t ",end = '')
for tab_list in tableau:
# print card if it exists, else print blank
try:
print(" {:5s} ".format(str(tab_list[i])), end = '')
except IndexError:
print(" {:5s} ".format(''), end = '')
print()
print()
def stock_to_waste( stock, waste ):
"""
Has two parameters: the data structure representing the stock, and the data structure representing the waste.
Hint: check that there are cards in the stock before trying to move one to the waste.
Remember that stock is of type Class Deck. The function will return True if the move was done successfully. Otherwise, it returns False.
"""
if len(stock):
waste.append(stock.deal())
return True
else:
return False
def waste_to_foundation( waste, foundation, f_num ):
'''
Function has three parameters:
1. The data structure representing the waste.
2. The data structure representing the foundations
3. a foundation number (the correct index in the foundation).
The function will return True if the move is valid (False otherwise).
If the move is valid, perform it.
To be moved into one of the foundations, the card must be the correct suit and rank:
it must be the same suit as the other cards in that foundation,
and it must have a rank which is exactly one higher than the card that is currently at the top of the foundation (as above, the Two on the Ace, the Three on the Two, and so on).
'''
# make sure f_num is valid
if f_num < 0 or f_num > 3:
return False
if len(waste):
waste_card = waste[-1]
f_target = foundation[f_num]
if len(f_target):
f_card = f_target[-1]
suit_matches = waste_card.suit() == f_card.suit()
rank_valid = waste_card.rank() == f_card.rank() + 1
if suit_matches and rank_valid:
f_target.append(waste.pop())
flip_last(f_target)
return True
else:
return False
else:
if waste_card.rank() == 1:
f_target.append(waste.pop())
flip_last(f_target)
return True
else:
return False
else:
return False
def waste_to_tableau( waste, tableau, t_num ):
'''
That function has three parameters:
1. the data structure representing the waste
2. the data structure representing the tableau
3. a column number (the correct index in the tableau).
The function will return True if the move is valid (False otherwise). If the move is valid, perform it.
To be moved into one of the columns in the tableau, the top card in the waste (talon) must be either a King (if the destination in the Tableau is empty),
or the opposite color and exactly one rank lower than the card which is the last face-up card in that column. For example, a red Seven may be moved onto a black Eight.
Hint: a red card will have suits as either 2 or 3. A black card will have suits either 1 or 4.
'''
if len(waste):
waste_card = waste[-1]
t_target = tableau[t_num]
if len(t_target):
t_card = t_target[-1]
waste_card_black = waste_card.suit() == 1 or waste_card.suit == 4
t_card_black = t_card.suit() == 1 or t_card.suit == 4
rank_valid = waste_card.rank() == t_card.rank() - 1
if waste_card_black != t_card_black and rank_valid:
t_target.append(waste.pop())
flip_last(t_target)
return True
else:
return False
else:
if waste_card.rank() == 13: # if the target is empty and the waste card is a king
t_target.append(waste.pop())
flip_last(t_target)
return True
else:
return False
return False
def tableau_to_foundation( tableau, foundation, t_num, f_num ):
# make sure t_num and f_num are valid
if t_num < 0 or t_num > 6 or f_num < 0 or f_num > 3:
return False
t_source = tableau[t_num]
f_target = foundation[f_num]
if len(t_source):
t_card = t_source[-1]
if len(f_target):
f_card = f_target[-1]
rank_valid = t_card.rank() == f_card.rank() + 1
if rank_valid and t_card.suit() == f_card.suit():
f_target.append(t_source.pop())
flip_last(t_source)
flip_last(f_target)
return True
else:
return False
else: # the foundation column is empty
if t_card.rank() == 1: # the tableau card is an ace
f_target.append(t_source.pop())
flip_last(t_source)
flip_last(f_target)
return True
else:
return False
else:
return False
def flip_last(column):
'''
Flips the last card in the column face up.
'''
if len(column) > 0:
if not column[-1].is_face_up():
column[-1].flip_card()
def tableau_to_tableau( tableau, t_num1, t_num2 ):
'''
Moves a card from one tableau column to another.
Returns True if the move was successful, False otherwise.
'''
# make sure t_num1 and t_num2 are valid
if t_num1 < 0 or t_num1 > 6 or t_num2 < 0 or t_num2 > 6:
return False
src_col = tableau[t_num1] # source tableau column
if not len(src_col): # source column is empty
return False
src_card = src_col[-1] # last card in source tableau column
dest_col = tableau[t_num2] # destination tableau column
if not len(dest_col): # destination column is empty
src_is_king = src_card.rank() == 13 # true if source card is a king (rank 13)
if src_is_king: # source card is a king
dest_col.append(src_col.pop()) # remove card from source column and add to destination column
flip_last(src_col)
flip_last(dest_col)
return True
return False
dest_card = dest_col[-1] # last card in destination tableau column
src_card_color = src_card.suit() == 1 or src_card.suit() == 4 # true for black, false for red
dest_card_color = dest_card.suit() == 1 or dest_card.suit() == 4 # true for black, false for red
src_rank_is_valid = src_card.rank() == dest_card.rank() - 1 # true if source card is one rank lower than destination card
src_color_is_valid = src_card_color != dest_card_color # true if source card is opposite color of destination card
is_valid_move = src_color_is_valid and src_rank_is_valid # true if source card is one rank lower and opposite color of destination card
if is_valid_move: # a valid move was made
dest_col.append(src_col.pop()) # move card from source to destination column
flip_last(src_col)
flip_last(dest_col)
return True
return False
def check_win (stock, waste, foundation, tableau):
'''
Returns True if the game is in a winning state: all cards are in the foundation,
stock is empty, waste is empty and tableau is empty. Otherwise, return False.
'''
foundation_is_full = False not in [len(col) == 13 for col in foundation] # true if all columns in foundation are full
stock_is_empty = not len(stock) # true if stock is empty
waste_is_empty = not len(waste) # true if waste is empty
tableau_is_empty = not any(tableau) # true if all columns in tableau are empty
if foundation_is_full and stock_is_empty and waste_is_empty and tableau_is_empty:
return True
return False
def parse_option( in_str ):
'''Prompt the user for an option and check that the input has the
form requested in the menu, printing an error message, if not.
Return:
TT s d: Move card from end of Tableau pile s to end of pile d.
TF s d: Move card from end of Tableau pile s to Foundation d.
WT d: Move card from Waste to Tableau pile d.
WF d: Move card from Waste to Foundation pile d.
SW : Move card from Stock to Waste.
R: Restart the game (after shuffling)
H: Display this menu of choices
Q: Quit the game
The function has one parameter: the raw input string. It returns a list which has the option specified followed by any arguments entered or returns None, if there is an error. The option returned must be upper case. All error checking of input is done in this function. Error statements are printed from this function (see specifications below). Note that if a list is returned, the list will be in the correct format with correct arguments—making your main function easier.
An example of a return (there are multiple variations):
return [option_str, source, destination, count]
where option_str is uppercase and the three arguments are correct integers (the column number and not the index in the list) Remember that column numbers start from 1 and not from 0.
Note that the function test we provide can check that None was returned for an error, but cannot check that a correct error message was printed—correctly printed error messages are only checked when the whole program is tested.
If an argument is an integer but is out of range, orprint("Error in Source")
print("Error in Destination")
as appropriate.
If the mTT count is an integer but not positive,
print("Error in Count")
If any argument is not all digits (e.g. -1) and all other errors.
print("Error in option:", in_str) # in_str is the raw input string
(The “not all digits” is an attempt to make some error checking easier for you.) Remember that all errors return None after printing the error message.
'''
option_list = in_str.strip().split()
opt_char = option_list[0][0].upper()
if opt_char in 'RHQ' and len(option_list) == 1: # correct format
return [opt_char]
if opt_char == 'S' and len(option_list) == 1:
if option_list[0].upper() == 'SW':
return ['SW']
if opt_char == 'W' and len(option_list) == 2:
if option_list[0].upper() == 'WT' or option_list[0].upper() == 'WF':
dest = option_list[1]
if dest.isdigit():
dest = int(dest)
if option_list[0].upper() == 'WT' and (dest < 1 or dest > 7):
print("\nError in Destination")
return None
if option_list[0].upper() == 'WF' and (dest < 1 or dest > 4):
print("\nError in Destination")
return None
opt_str = option_list[0].strip().upper()
return [opt_str,dest]
valid_digits = all([option.isdigit() for option in option_list[1:]])
if opt_char == 'T' and len(option_list) == 3 and valid_digits:
opt_str = option_list[0].strip().upper()
if opt_str in ['TT','TF']:
source = int(option_list[1])
dest = int(option_list[2])
# check for valid source values
if opt_str in ['TT','TF'] and (source < 1 or source > 7):
print("\nError in Source.")
return None
#elif opt_str == 'MFT' and (source < 0 or source > 3):
#print("Error in Source.")
#return None
# source values are valid
# check for valid destination values
if (opt_str =='TT' and (dest < 1 or dest > 7)) \
or (opt_str == 'TF' and (dest < 1 or dest > 4)):
print("\nError in Destination")
return None
return [opt_str,source,dest]
print("\nError in option:", in_str)
return None # none of the above
def main():
# initialize the board
tableau, stock, foundation, waste = initialize()
# display the menu
print(MENU)
# display the starting board
display(tableau, stock, foundation, waste)
prompt_msg = "\nInput an option (TT,TF,WT,WF,SW,R,H,Q): "
error_msg = "\nInvalid move!\n"
won_msg = "\nYou won!\n"
input_str = input(prompt_msg)
while input_str.upper() != 'Q':
options = parse_option(input_str)
if options == None:
display(tableau, stock, foundation, waste)
input_str = input(prompt_msg)
continue
if options[0] == 'R':
tableau, stock, foundation, waste = initialize()
display(tableau, stock, foundation, waste)
input_str = input(prompt_msg)
continue
if options[0] == 'H':
print(MENU)
input_str = input(prompt_msg)
continue
if options[0] == 'SW':
if stock_to_waste(stock, waste):
display(tableau, stock, foundation, waste)
else:
print(error_msg)
display(tableau, stock, foundation, waste)
input_str = input(prompt_msg)
continue
if options[0] == 'WT':
if waste_to_tableau(waste, tableau, options[1]):
display(tableau, stock, foundation, waste)
else:
print(error_msg)
display(tableau, stock, foundation, waste)
input_str = input(prompt_msg)
continue
if options[0] == 'WF':
if waste_to_foundation(waste, foundation, options[1]):
if check_win(stock, waste, foundation, tableau):
print(won_msg)
display(tableau, stock, foundation, waste)
break
else:
display(tableau, stock, foundation, waste)
else:
print(error_msg)
display(tableau, stock, foundation, waste)
input_str = input(prompt_msg)
continue
if options[0] == 'TT':
if tableau_to_tableau(tableau, options[1], options[2]):
display(tableau, stock, foundation, waste)
else:
print(error_msg)
display(tableau, stock, foundation, waste)
input_str = input(prompt_msg)
continue
if options[0] == 'TF':
if tableau_to_foundation(tableau, foundation, options[1], options[2]):
if check_win(stock, waste, foundation, tableau):
print(won_msg)
display(tableau, stock, foundation, waste)
break
else:
display(tableau, stock, foundation, waste)
else:
print(error_msg)
display(tableau, stock, foundation, waste)
input_str = input(prompt_msg)
continue
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment