Solved CSE2050 Homework 02: OOP & TDD

$25.00

Original Work ?
Category: Tags: , , You will Instantly receive a download link for .ZIP solution file upon Payment

Description

5/5 - (1 vote)

OOP with Animals

  1. Create classes to represent the following hierarchy using inheritance in a module called py. Note the follow-on instructions on characteristics of each class.

Figure 1: Class Hierarchy to Implement

  1. Every class should be initialized with an attribue name. Because all objects share this behavior, it should be defined in the superclass constructor (Which is the “superclass”?  What is a “constructor”?).  It should have the default value of “It” (This is not recommended, you should give your pets a name.  I named my first dog “Stay” ;).

 

 

  1. Every class except ComputerScientist should define its own speak() method, which returns a string giving the name and an appropriate sound for that class. For example:

c1 = Cat(‘Babs’)

c1.speak()

>>> ‘Babs says Meow!’

  1. Do not implement speak in the ComputerScientist subclass – it should default to whatever method you define for Primate.
  2. The Animal superclass should define a method called reply(), which just calls the relevant speak:

c1 = Cat(‘Babs’)

c1.speak() # should call Cat.speak()

c1.reply() # should call Animal.reply()

>>> ‘Babs says Meow!’

>>> ‘Babs says Meow!’

  1. Provide a listing of your code for py below this bullet. Include the test code for your program (following the TDD approach as described in the text).

class Animal:
“””Represents a non-specific animal”””
def __init__(self, name=”It”):
“””Instantiates a new animal with a name”””
self.name = name

def speak(self):
“””Allows for an error to be caught if speak is not defined elsewhere”””
raise NotImplementedError(“Subclasses must implement this method”)

def reply(self):
“””Calls the speak method”””
return self.speak()

class Mammal(Animal):
“””Represents a Mammal in animal kingdom”””
def speak(self):
return f”{self.name} says HuH!”

class Primate(Mammal):
“””Represents subclass of Mammals that are Primates”””
def speak(self):
return f”{self.name} says EE-EE-OOH-OOH”

class ComputerScientist(Primate):
“”””Represents a subclass of Primates that are Computer Scientists”””
# speak is inherited from primate
pass

class Cat(Mammal):
“””Represents a subclass of Mammals that are Cats.”””
def speak(self):
return f”{self.name} says Meow!”

# Test Cat Initialization and speak method
cat = Cat(“Wilbur”)
assert cat.name == “Wilbur”, “Cat name initialization failed”
assert cat.speak() == “Wilbur says Meow!”, “Cat speak method failed with the name Wilbur”

# Test Animal reply method
assert cat.reply() == “Wilbur says Meow!”, “Animal reply method failed using Cat instance with the name Wilbur”

# Testing Mammal class
mammal = Mammal(“Brittney”)
assert mammal.name == “Brittney”, “Mammal name initialization failed”
assert mammal.speak() == “Brittney says HuH!”, “Mammal speak method failed”

# Testing Primate class
primate = Primate(“George”)
assert primate.name == “George”, “Primate name initialization failed”
assert primate.speak() == “George says EE-EE-OOH-OOH”, “Primate speak method failed”

# Testing ComputerScientist class
cs = ComputerScientist(“Alice”)
assert cs.name == “Alice”, “ComputerScientist name initialization failed”
assert cs.speak() == “Alice says EE-EE-OOH-OOH”, “ComputerScientist speak method failed”

 

OOP & TDD with Cards

This is where the bulk of your work will be for this assignment. Anticipate spending at least a few hours here if you are new to OOP and TDD, and consider breaking it up over a few days if your schedule allows.

Card games are a classic application of OOP. They let us use composition (decks of cards contain several card objects) and inheritance (a hand of cards can be treated as a specialized deck). The diagram below shows the inheritance model and the specific instance variables and methods we’ll implement here.  Descriptions of attributes and methods are provided later in this document.

Figure 2: (a) Class diagram and (b) attributes for each class. Inherited attributes are in blue.

Create the classes above in a file called Cards.py.  Using the TDD approach, write unittests BEFORE you write your other code and place your unittests in a file called TestCards.py.  This includes any exceptions you should raise.  See the unittest basic example below for an illustration of how to test that an error is raised and read more about unittest (link).  Your final TestCards.py class should include 3 classes – one for each class you are trying to test:

from Cards import Card, Deck, Hand

import unittest

class TestCard(unittest.TestCase):

“Test cases specific to the Card class” def test_init(self):

“Add a docstring here”

# other tests

class TestDeck(unittest.TestCase):

# your tests here class

class TestHand(unittest.TestCase):

if __name__ == ‘__main__’:

unittest.main()

 

 

Structure your code based on OOP principles. Hand should inherit from Deck, and should not overload any methods unless it needs to.

Every method (including your unittests) should have a docstring.

When writing tests, create your own examples. Do not use any of the examples shown below.

Some OOP relationships:

  • Composition – Deck comprises a list of Card objects
  • Inheritance – Hand is inherited from Deck so .len() and .sort() do not need to be recoded.

The expected input/output of each method is defined below.  Most do what you would think intuitively. Before you start implementing code, review these guidelines:

 

 

class Card

  • __init__ – initialize a new card based on the specified parameters:
    • .value – the value of a card (i.e. the 3 in 3 of hearts)
    • .suit – the suit of a card (i.e. the hearts in 3 of hearts)
  • __repr__ – return a “code-perspective” string of the object “Card(3, ‘hearts’)”. This is very similar to the __str__ magic method.  Read this article to get insight into the different uses of each (i.e., they both “do” the same thing. But, convention is that a class definition “uses” them differently).
  • __str__ – NOTE: This is not visible in the architecture picture.  Return a “user-perspective” view of the card object “3 of hearts”.
  • __lt__ (that is a small “el” in front of the “t”) – Implement as a magic method (__lt__) so it can be called with the standard operator (<). Sort by suit first, then increasing value (suits are sorted alphabetically, so clubs < diamonds)
  • __eq__ – Implement as a magic method so it can be called with the standard operator (=). Sort by suit first, then value (suits are sorted alphabetically, so clubs < diamonds)

Examples

>>> c1 = Card(3, ‘hearts’)

>>> repr(c1)

‘Card(3, ‘hearts’)’

>>> print(c1)

‘3 of hearts’’

 

>>> c2 = card(3, ‘spades’)

>>> c1 < c2

True

>>> c3 = Card(4, ‘hearts’)

>>> c3 < c2

True

 

 

class Deck

  • __init__ – initialize a Deck based on the collection of values and suits passed – make one card for each value/suit
    • .values – collection of values stored in deck. This should be a parameter with a default of the numbers 1 through 13
    • .suits – collection of suits stored in deck. This should be a parameter with a default of (‘clubs’, ‘diamonds’, ‘hearts’, ‘spades’)
    • .card_list – list of Card objects, containing all cards in the deck
  • __len__ – override the magic method for length and return the number of cards in the deck.
  • sort – sorts the cards in the deck
  • __str__ – returns a “user perspective” string representation of the deck.
  • shuffle – randomize the order of the deck. Import the random module and use shuffle() method here (look up in your chosen reference what this method is.
  • draw_top – remove and return the top card of the deck.
    • Treat the last item in card_list as the “top” of the deck.
    • Raise a RuntimeError with the string “Cannot draw from empty deck” if someone tries to draw from an empty deck

Examples

>>> d1 = Deck() # use default values and suits

>>> len(d1)

52

>>> d2 = Deck([2, 1], [‘triangles’, ‘dots’])

>>> print(d2)

‘Deck: [Card(1 of dots), Card(2 of dots), Card(1 of triangles), Card(2 of triangles)]’

>>> d2.shuffle()

>>> print(d2)

‘Deck: [Card(2 of triangles), Card(1 of dots), Card(2 of dots), Card(1 of triangles)]’

>>> d2.draw_top()

1 of triangles

>>> d2.draw_top()

2 of dots

>>> d2.draw_top()

1 of dots

>>> d2.draw_top()

2 of triangles

>>> d2.draw_top()

Traceback (most recent call last): …

RuntimeError: Cannot draw from empty deck

class Hand

  • __init__ – create a Hand with a passed in list of cards
  • __str__ – returns a string representation of the Hand. Note that the class definition for Hand inherits all of the methods of Deck which already has a __str__  This will override that __str__ method and provide a new __str__ magic method for a Hand.
  • play(card)
    • removes and returns card from hand
    • raises a RuntimeError with the string “Attempted to play Card(1 of clubs) that is not in Hand: [Card(5 of clubs),…<and then list the whole hand>” if card is not in hand

Examples

>>> h_clubs = Hand([Card(value, ‘clubs’) for value in range(5, 0, 1)])

>>> print(h_clubs)

‘Hand: [Card(5 of clubs), Card(4 of clubs), Card(3 of clubs), Card(2 of clubs), …’

>>> h_clubs.sort() # inherited from Deck

>>> print(h_clubs)

‘Hand: [Card(1 of clubs), Card(2 of clubs), Card(3 of clubs), Card(4 of clubs), …’

>>> len(h_clubs)

5

>>> h_clubs.play(Card(1, ‘clubs’))

`Card(1 of clubs)`

>>> h_clubs.play(Card(1, ‘clubs’)) Traceback (most recent call last): …

RuntimeError: Attempted to play Card(1 of clubs) that is not in Hand: [Card(5 of clubs),…

Submitting

At a minimum, submit the following files:

  • This Word document with your all of your code pasted in order after these bullets. Each code file should start on a new page in this document (CTRL+ENTER).  Have this file on the top of your submission (so that it opens by default).  And then, in the same submission but under this Word document, provide your three code files:
  • py
  • py

 

 

 

 

import random
class Card:
def __init__(self, value, suit):
self.value = value
self.suit = suit

def __repr__(self):
”’Return the machine representation of the card”’
return f”Card({self.value!r}, {self.suit!r}”

def __str__(self):
”’Return string representation of the card”’
return f”{self.value} of {self.suit}”

def __lt__(self, other):
”’Compare Cards first by suit then by value”’
if self.suit == other.suit:
return self.value < other.value
return self.suit < other.suit

def __eq__(self, other):
”’Check if two cards are equal first by suit then by value”’
return self.value == other.value and self.suit == other.suit

class Deck:
def __init__(self, values=range(1, 14), suits=(‘clubs’, ‘diamonds’, ‘hearts’, ‘spades’)):
self.card_list = []
for suit in suits:
for value in values:
self.card_list.append(Card(value, suit))

def __len__(self):
“””Return the number of cards in the deck.”””
return len(self.card_list)

def sort(self):
“””Sort the cards in the deck.”””
self.card_list.sort()

def __str__(self):
“””Return a string representation of the deck.”””
return ‘, ‘.join(str(card) for card in self.card_list)

def shuffle(self):
“””Shuffle the cards in the deck.”””
random.shuffle(self.card_list)

def draw_top(self):
“””Remove and return the top card of the deck.”””
if not self.card_list:
raise RuntimeError(“Cannot draw from an empty deck”)
return self.card_list.pop()

class Hand(Deck):
def __init__(self, cards=None):
“””Initialize a hand with an optional list of cards.”””
if cards is not None:
self.card_list = cards
else:
self.card_list = []

def __str__(self):
“””Return a string representation of the hand.”””
return ‘Hand: ‘ + ‘, ‘.join(str(card) for card in self.card_list)

def play(self, card):
“””Play a card from the hand.”””
if card not in self.card_list:
raise RuntimeError(f”Attempted to play {card} that is not in hand: {self}”)
self.card_list.remove(card)
return card

 

from Cards import Card, Deck, Hand

import unittest

class TestCard(unittest.TestCase):
“””Class to evaluate the performance of Card class”””
def test_card_init(self):
“””Test the instantiation of a Card.”””
card = Card(‘3’, ‘hearts’)
self.assertEqual(card.value, ‘3’)
self.assertEqual(card.suit, ‘hearts’)

def test_card_repr(self):
“””Test the machine representation of a Card.”””
card = Card(‘3’, ‘hearts’)
self.assertEqual(repr(card), “Card(‘3’, ‘hearts'”)

def test_card_str(self):
“””Test the human-readable representation of a Card.”””
card = Card(‘3’, ‘hearts’)
self.assertEqual(str(card), “3 of hearts”)

def test_card_lt(self):
“””Test the less-than comparison of two Cards.”””
card1 = Card(‘3’, ‘hearts’)
card2 = Card(‘4’, ‘hearts’)
self.assertTrue(card1 < card2)

def test_card_eq(self):
“””Test the equality comparison of two Cards.”””
card1 = Card(‘3’, ‘hearts’)
card2 = Card(‘3’, ‘hearts’)
self.assertTrue(card1 == card2)

class TestDeck(unittest.TestCase):
“””Class to evaluate the performance of Deck class”””
def setUp(self):
“””Set up a deck of cards for each test.”””
self.deck = Deck()

def test_deck_init(self):
“””Test the initialization of a Deck.”””
self.assertEqual(len(self.deck.card_list), 52)

def test_deck_len(self):
“””Test the length of the Deck.”””
self.assertEqual(len(self.deck), 52)

def test_deck_sort(self):
“””Test the sorting of the Deck.”””
self.deck.shuffle()  # Shuffle first
self.deck.sort()  # Sort it in order
self.assertEqual(self.deck.card_list[0].suit, ‘clubs’)  # Assume clubs
self.assertEqual(self.deck.card_list[0].value, 1)  # Assuming clubs value 1

def test_deck_str(self):
“””Test the string representation of the Deck.”””
deck_str = str(self.deck)
self.assertIsInstance(deck_str, str)
self.assertIn(‘of’, deck_str)

def test_deck_shuffle(self):
“””Test the shuffle method.”””
original_deck = self.deck.card_list.copy()
self.deck.shuffle()
self.assertNotEqual(original_deck, self.deck.card_list)

def test_deck_draw_top(self):
“””Test drawing the top card from the Deck.”””
top_card = self.deck.card_list[-1]
drawn_card = self.deck.draw_top()
self.assertEqual(top_card, drawn_card)
self.assertEqual(len(self.deck), 51)

class TestHand(unittest.TestCase):
“””Class to evaluate the performance of Hand class”””
def setUp(self):
“””Create a Hand instance with some Cards for testing.”””
self.hand = Hand([Card(3, ‘hearts’), Card(4, ‘diamonds’), Card(2, ‘spades’)])

def test_hand_init(self):
“””Test the initialization of Hand with a list of Cards.”””
self.assertEqual(len(self.hand.card_list), 3, “Hand should be initialized with 3 cards”)

def test_hand_len(self):
“””Test the __len__ method to ensure it returns the correct number of cards.”””
self.assertEqual(len(self.hand), 3, “Length of hand should be 3”)

def test_hand_sort(self):
“””Test the sort method to ensure the Hand is sorted correctly.”””
self.hand.sort()
self.assertEqual(self.hand.card_list[0].suit, ‘diamonds’, “First card should be of suit ‘diamonds'”)
self.assertEqual(self.hand.card_list[0].value, 4, “First card should be 4 of diamonds”)

def test_hand_str(self):
“””Test the __str__ method for a correct string representation of the Hand.”””
hand_str = str(self.hand)
self.assertIsInstance(hand_str, str)
self.assertIn(“4 of diamonds”, hand_str, “String representation of hand should contain ‘4 of diamonds'”)

def test_hand_play(self):
“””Test the play method by playing a card from the Hand.”””
card_to_play = Card(4, ‘diamonds’)
played_card = self.hand.play(card_to_play)
self.assertEqual(played_card, card_to_play, “The played card should be the same as the card requested to play”)
self.assertEqual(len(self.hand), 2, “The hand should have one less card after playing”)

def test_play_card_not_in_hand(self):
“””Test the play method raises an error if the card is not in the Hand.”””
card_not_in_hand = Card(5, ‘hearts’)
with self.assertRaises(RuntimeError):
self.hand.play(card_not_in_hand)

if __name__ == ‘__main__’:
unittest.main()