# You must make sure to run all cells in sequence using shift + enter or you might encounter errors
from pykubegrader.initialize import initialize_assignment

responses = initialize_assignment("homework_4", "week_4", "homework", assignment_points = 35.0, assignment_tag = 'week4-homework')

# Initialize Otter
import otter
grader = otter.Notebook("homework_4.ipynb")

๐Ÿ ๐Ÿง™โ€โ™‚๏ธ๐Ÿ“œ Casting Spells and Conditional Loops: Homework That Taps Your Mana!#

Question 1: Card Rarity Detector#

  • Card Rarity Detector

Magic: The Gathering

In Magic: The Gathering, card rarities play a key role in gameplay and deck-building. Rarities include common, uncommon, rare, and mythic rare. Cards like โ€œBlack Lotusโ€ or โ€œJace, the Mind Sculptorโ€ are examples of mythic rare cards that are prized possessions.

Problem Statement#

Write a Python function card_rarity(score) that determines the rarity of a Magic: The Gathering card based on its score, following these rules:

  • 95-100: Mythic Rare

  • 90-94: Rare

  • 80-89: Uncommon

  • 70-79: Common

  • 0-69: Basic Land

Additionally:

  • Scores outside the valid range (0โ€“100) should return the message "Invalid card score".

Implementation#

  1. Define a function card_rarity(score) with an integer input argument score. We provide this function for you.

  2. Implement a series of conditions to determine the rarity based on the given score.

  3. Return the corresponding rarity as a string.

  4. If the score is outside the valid range, return the message "Invalid card score".

def card_rarity(score):
    ...

# Test the function  
print(card_rarity(98))  # Mythic Rare  
print(card_rarity(92))  # Rare  
print(card_rarity(85))  # Uncommon  
print(card_rarity(75))  # Common  
print(card_rarity(50))  # Basic Land  
print(card_rarity(105))  # Invalid card score
grader.check("mtg-card-rarity")

Question 1 (Points: 6.0): ๐Ÿง™โ€โ™‚๏ธ Question 2: Magic The Gathering Fibonacci Cards#

Magic: The Gathering

In Magic: The Gathering, creatures and spells often have synergistic effects. For example, creating a Fibonacci sequence of tokens represents a creative challenge for deck builders who utilize mechanics like โ€œDoubling Seasonโ€ or โ€œParallel Livesโ€.

Problem Statement#

Create a function that generates a sequence of creature token powers based on a Fibonacci-like growth pattern. For example, โ€œKalonian Hydraโ€ doubles its counters, and cards like โ€œAether Vialโ€ enable incremental growth.

Write a function to simulate the Fibonacci sequence for such token growth mechanics.

A Fibonacci sequence is a list of integers in which the \(n\text{th}\) entry \(x_n\) is computed from:

\[ x_n = x_{n-1} + x_{n-2} \]

To initiate a Fibbonaci sequence, one must provide values for \(x_0\) and \(x_1\).

For example, if \(x_0 = 0\) and \(x_1 = 1\), the first 10 terms of the sequence are:

\[ 0, 1, 1, 2, 3, 5, 8, 13, 21, 34 \]

Instructions#

  1. Define a function called fibonacci_tokens that accepts 3 inputs:

    • x0: Initial power of the first token.

    • x1: Initial power of the second token.

    • L: The total number of tokens to generate in the sequence.

  2. Implement the function to return a list of the first L Fibonacci-like token powers starting with x0 and x1.

    • Define a list seq to store the first two initial values x0 and x1, of the sequence.

    • Use a for loop to generate the remaining L-2 values of the sequence. I would recommend using the range function to iterate over the remaining values.

    • Append the new values to the list seq at each iteration, by indexing the last 2 elements of the list. Use the built-in append method of the list object. hint: This is a perfect opportunity to revisit the negative indexing of lists. Return the sequence seq if the input is valid.

  3. If the input L is less than 2, print โ€œFibonacci-like growth not possibleโ€. Use if statements to enforce that the fibonacci sequence is not generated.

def fibonacci_tokens(x0, x1, L):  
    ...
grader.check("mtg-fibonacci-tokens")

Question 2 (Points: 11.0): ๐Ÿง™โ€โ™‚๏ธ Question 3: Find the Best Card to Play#

Magic: The Gathering

In Magic The Gathering, choosing the right card to play at the right time can be the difference between victory and defeat. Suppose youโ€™re building a deck that revolves around cards with varying mana costs, power, toughness, and special abilities.

Your task is to determine the best card to play given the available mana and the current game state.

Problem Statement#

Implement a function that evaluates a list of cards and finds the best card to play based on the following criteria:

  1. The cardโ€™s mana cost must not exceed the available mana in any color or the total mana.

  2. The card with the highest combined power and toughness is preferred if multiple cards can be played.

  3. In case of a tie in power and toughness, a card with a special ability (e.g., โ€œTrampleโ€) should be prioritized.

Instructions#

  1. We have defined a function find_best_card_to_play that accepts the following inputs:

    • available_mana: A dictionary specifying the available mana in each color.
      Example:

      available_mana = {"green": 3, "red": 2, "colorless": 1}
      
    • cards: A list of dictionaries, where each dictionary represents a card with the following attributes:

      • "name": Name of the card.

      • "mana_cost": A dictionary specifying the cardโ€™s mana cost in each color.

      • "power": The cardโ€™s power.

      • "toughness": The cardโ€™s toughness.

      • "abilities": A list of special abilities (e.g., ["Trample", "Flying"]).
        Example:

      cards = [
          {"name": "Elf Warrior", "mana_cost": {"green": 2, "red": 0, "colorless": 0}, "power": 2, "toughness": 3, "abilities": []},
          {"name": "Goblin Berserker", "mana_cost": {"green": 0, "red": 2, "colorless": 1}, "power": 3, "toughness": 2, "abilities": ["Trample"]},
          {"name": "Orc Chieftain", "mana_cost": {"green": 1, "red": 1, "colorless": 1}, "power": 5, "toughness": 3, "abilities": []}
      ]
      
  2. Evaluate all cards and find the one that:

    • Can be played with the available mana.

    • Has the highest combined power and toughness.

    • If tied, has at least one special ability.

  3. Return the name of the best card to play. You can do that by assigning the name of the best card to a variable best_card, we have provided the return statement for you.

  4. If no card can be played, assign "No card can be played" to a variable best_card.

We have provided some additional inline comments to help you solve the problem.

Example Input:#

available_mana = {"green": 3, "red": 2, "colorless": 1}

cards = [
    # Plains (White)
    {"name": "Serra Angel", "mana_cost": {"white": 2, "blue": 0, "black": 0, "red": 0, "green": 0, "colorless": 3}, "power": 4, "toughness": 4, "abilities": ["Flying", "Vigilance"]},
    {"name": "Loxodon Smiter", "mana_cost": {"white": 1, "blue": 0, "black": 0, "red": 0, "green": 1, "colorless": 1}, "power": 4, "toughness": 4, "abilities": ["Cannot be countered"]},
    {"name": "Sun Titan", "mana_cost": {"white": 2, "blue": 0, "black": 0, "red": 0, "green": 0, "colorless": 4}, "power": 6, "toughness": 6, "abilities": ["Vigilance", "When it enters or attacks, return a permanent with mana cost 3 or less from your graveyard to the battlefield"]},
]

Example Output:#

If the best card to play is โ€œGoblin Berserkerโ€, the function returns:

"Goblin Berserker"

Tips#

  • All of the code you need to write is indicated by comments and ... we have already tab intended the code at the correct level for you. We would advise not changing it.

  • If you encounted a semantic error you might want to use the debugger to help you solve the problem

  • We provide one valid solution, however there are multiple ways to solve this problem. Our tests are not specific to our solution, however we would recommend following our guide.

# import card_list from cards.py
# cards.py contains a list of dictionaries representing Magic: The Gathering cards
# Use the syntax from module import name to import the card_list function
# Note: You do not need the .py extension when importing a module from a Python file
...

def find_best_card_to_play(available_mana, cards):
    """
    Finds the best card to play based on available mana and card attributes using a nested loop structure,
    adding unused mana from other colors to the colorless pool.

    Args:
    - available_mana (dict): Dictionary with available mana in each color.
    - cards (list): List of dictionaries representing cards.

    Returns:
    - str: The name of the best card to play, or "No card can be played" if none are valid.
    """
    # initialize variables to store the best card and its combined power and toughness
    # Initialize the variable `best_card` to store the best card to play. Set it to None initially.
    ...
    
    # Initialize the variable `max_combined_power` to store the maximum combined power and toughness of the best card.
    # Set it to -1 initially.
    ...
    
    # loops through each card in the list of cards
    # recall that dictionaries are iterable, so you can iterate through the list of cards using a for loop directly. 
    ...
    
        # Check if the card's mana cost can be satisfied
        # Create a variable `can_play` and set it to True initially. This variable will be used to check if the card can be played.
        ...
        
        # Calculate the total unused mana based on the sum of the available mana pool
        # Hint: You can use the sum() function to calculate the total unused mana by extracting the values from the available_mana dictionary. Use the values() method to get the values.
        ...
        
        # iterate through each cards mana cost, checking if it can be satisfied
        # To do this you should iterate through each key-value pair in the card's "mana_cost" dictionary. Do this by using the items() method on the dictionary.
        ...
            
            # Check if the color is not colorless using an if statement
            ...
            
                # Check if the cost is greater than the available mana for that color
                # Use an if statement to check if the cost is greater than the available mana for that color. If it is, set `can_play` to False and break out of the loop.
                # You should get the available mana for the specific color using the get() method on the available_mana dictionary, this way if there is no available mana for that color, it can default to 0.
                ...
                
                # Subtract and assign the total_unused_mana, subtracting the color mana used
                ...

        # Check if colorless mana can be satisfied with unused mana
        # Write an if statement that checks that the card can be played (based on prior checks) and that the total unused mana is less than the colorless mana cost of the card.
        # If this condition is met, set `can_play` to False, because there is not enough mana to play the card.
        ...

        # If the card can be played, calculate the combined power and toughness by extracting the values from the card dictionary
        # If statement to check if the card can be played
        ...
            
            # Calculate combined power and toughness
            ...
            
            # Check if the combined power of the current card is greater than the maximum combined power
            # If it is, set the `best_card`` to the current card and update the `maximum_combined_power`
            ...
            
            # else if the combined power is equal to the maximum combined power, prioritize cards with abilities
            # Use an elif statement to check if the combined power is equal to the maximum combined power.
            ...
            
                # Check if the current card has any abilities
                # to do this, use a for loop to iterate through each ability in the card's "abilities" list.
                ...
                    # If the card has an ability, set it as the best card and break out of the loop.
                    # hint: you can use an if statement to check if the ability exists, and empty lists evaluate to False in Python. A non-empty list evaluates to True.
                    ...
                        # Set the best card to the current card and break out of the loop
                        ...
        
    # if a best card is found, i.e. best_card is not None, return the name of the best card
    ...

        # We have provided the return statement for you
        return best_card["name"]
    
    # Else return "No card can be played"
    ...
        # We have provided the return statement for you
        return "No card can be played"
        

# Example usage
available_mana = {"red": 3, "white": 2, "colorless": 0}
result = find_best_card_to_play(available_mana, card_list())
print(result)  # Expected output: "Serra Angel"
grader.check("mtg-combo-solver")

Submitting Assignment#

Please run the following block of code using shift + enter to submit your assignment, you should see your score.

from pykubegrader.submit.submit_assignment import submit_assignment

submit_assignment("week4-homework", "homework_4")