← Computer Programming II

Problem 1 (Easy): Book Catalog Entry

You are building a simple digital library system. Define a dataclass called Book that stores information about a book.

  1. Import dataclass from the dataclasses module.
  2. Create a Book dataclass with three fields: title (str), author (str), and pages (int).
  3. Create two Book instances with the same title, author, and pages values of your choice.
  4. Print the first book.
  5. Print whether the two books are equal.

Expected Output (example)

Book(title='asd', author='zxc', pages=123)
True

Problem 2 (Easy+): Coffee Order

You work at a coffee shop and need to track drink orders. Create a dataclass called CoffeeOrder with default values.

  1. Import dataclass from the dataclasses module.
  2. Create a CoffeeOrder dataclass with four fields: drink (str), customer (str), size (str) with a default of "Medium", and price (float) with a default of 3.5.
  3. Create one order with all four values: "Latte", "Paul", "Large", 4.75.
  4. Create another order with only the required fields: "Espresso", "Chani".
  5. Print both orders.

Input

o1 = CoffeeOrder("Latte", "Paul", "Large", 4.75)
o2 = CoffeeOrder("Espresso", "Chani")
print(o1)
print(o2)

Expected Output

CoffeeOrder(drink='Latte', customer='Paul', size='Large', price=4.75)
CoffeeOrder(drink='Espresso', customer='Chani', size='Medium', price=3.5)

Problem 3 (Medium): Cargo Crate

You are managing cargo shipments across planets. Create a CargoCrate dataclass that tracks its contents and enforces a weight limit.

  1. A CargoCrate has fields: crate_id (str), destination (str), max_weight (float), and items which starts as an empty list of (name, weight) tuples.
  2. total_weight(self) -> float — returns the combined weight of all items in the crate.
  3. add_item(self, name: str, weight: float) -> bool — adds the item only if doing so would not exceed the crate’s weight limit. Returns whether the item was successfully added.
  4. manifest(self) — prints a formatted summary of the crate. Study the expected output to determine the exact format.

Input

c = CargoCrate("CR-401", "Arrakis", 50.0)
print(c.add_item("Stillsuit", 8.5))
print(c.add_item("Spice Melange", 30.0))
print(c.add_item("Shield Generator", 15.0))
print(c.total_weight())
c.manifest()

Expected Output

True
True
False
38.5
Crate CR-401 -> Arrakis
  - Stillsuit: 8.5kg
  - Spice Melange: 30.0kg
Total: 38.5/50.0kg

Problem 4 (Medium+): Training Session

You are building a combat academy tracker. Create a TrainingSession dataclass that tracks a trainee’s scores and automatically maintains computed statistics.

  1. A TrainingSession has trainee (str), discipline (str), scores (list of ints, empty by default), and two computed fields — average (float) and rank (str) — that should not be constructor parameters.
  2. average and rank must be computed immediately after creation and stay in sync whenever scores change. Rank is "Elite" if average ≥ 90, "Skilled" if average ≥ 70, and "Novice" otherwise.
  3. add_score(self, score: int) — records a new score and ensures computed fields reflect the updated data.
  4. outperforms(self, other: 'TrainingSession') -> bool — returns whether this trainee’s average exceeds another’s.

Input

t1 = TrainingSession("Duncan", "Swordsmanship", [85, 92, 78])
print(t1)
t1.add_score(95)
print(t1.average)
print(t1.rank)

t2 = TrainingSession("Feyd", "Swordsmanship", [95, 90, 98])
print(t2.rank)
print(t1.outperforms(t2))

t3 = TrainingSession("Rabban", "Swordsmanship")
print(t3.rank)

Expected Output

TrainingSession(trainee='Duncan', discipline='Swordsmanship', scores=[85, 92, 78], average=85.0, rank='Skilled')
87.5
Skilled
Elite
False
Novice

Problem 5 (Advanced): Bounty Board

You run a bounty board where hunters claim and complete bounties. Design two interacting dataclasses.

  1. Bounty must be frozen and ordered. It has reward (int), target (str), and danger (int).
  2. Hunter has name (str), completed (list of Bounty, empty by default), and total_earnings (int, computed — not a constructor parameter). Earnings must reflect the completed list at creation and stay accurate as new bounties are taken.
  3. take_bounty(self, bounty: Bounty) — records a completed bounty and updates earnings.
  4. best_catch(self) -> Bounty — returns the highest-valued completed bounty.
  5. resume(self) -> str — returns a formatted summary. Study the expected output to determine the format.

Input

b1 = Bounty(5000, "Stilgar", 8)
b2 = Bounty(3000, "Liet", 5)
b3 = Bounty(8000, "Irulan", 3)

print(sorted([b3, b1, b2]))

h = Hunter("Gurney")
h.take_bounty(b1)
h.take_bounty(b2)
h.take_bounty(b3)
print(h.total_earnings)
print(h.best_catch())
print(h.resume())

Expected Output

[Bounty(reward=3000, target='Liet', danger=5), Bounty(reward=5000, target='Stilgar', danger=8), Bounty(reward=8000, target='Irulan', danger=3)]
16000
Bounty(reward=8000, target='Irulan', danger=3)
Hunter: Gurney
Bounties: 3
Earnings: 16000
Best: Irulan (8000 credits)

Problem 6 (Advanced+): Guild Tracker

You manage financial records for a trading guild. Design two dataclasses: an immutable transaction record and a tracker that tracks and analyzes transactions relative to its owner.

  1. Transaction must be frozen and ordered. It has amount (int), sender (str), receiver (str), and category (str).
  2. Tracker has owner (str), transactions (list of Transaction, empty by default), and balance (int, computed — not a constructor parameter). The balance must reflect whether the owner is the sender or receiver of each transaction, and stay accurate as new transactions are recorded.
  3. record(self, transaction: Transaction) — adds a transaction and updates the balance.
  4. filter_by(self, category: str) -> list[Transaction] — returns only transactions matching the given category.
  5. summary(self) -> dict[str, int] — returns net amount per category from the owner’s perspective. Study the expected output to understand the sign logic.
  6. top_partners(self, n: int) -> list[tuple[str, int]] — returns the top n trading partners ranked by total transaction volume, regardless of direction.

Input

t1 = Transaction(5000, "Gurney", "Duncan", "spice")
t2 = Transaction(3000, "Duncan", "Stilgar", "weapons")
t3 = Transaction(2000, "Chani", "Duncan", "spice")
t4 = Transaction(1000, "Duncan", "Gurney", "supplies")

print(sorted([t3, t1, t4, t2]))

tracker = Tracker("Duncan")
tracker.record(t1)
tracker.record(t2)
tracker.record(t3)
tracker.record(t4)
print(tracker.balance)
print(tracker.filter_by("spice"))
print(tracker.summary())
print(tracker.top_partners(2))

Expected Output

[Transaction(amount=1000, sender='Duncan', receiver='Gurney', category='supplies'), Transaction(amount=2000, sender='Chani', receiver='Duncan', category='spice'), Transaction(amount=3000, sender='Duncan', receiver='Stilgar', category='weapons'), Transaction(amount=5000, sender='Gurney', receiver='Duncan', category='spice')]
3000
[Transaction(amount=5000, sender='Gurney', receiver='Duncan', category='spice'), Transaction(amount=2000, sender='Chani', receiver='Duncan', category='spice')]
{'spice': 7000, 'weapons': -3000, 'supplies': -1000}
[('Gurney', 6000), ('Stilgar', 3000)]

Problem 7 (Extra): Arena Championship

You are designing a fighting arena system. Fighters have stats that determine a computed power level, and the arena manages registration, matchmaking, and a ranked leaderboard. Combine dataclasses with inheritance, abstract methods, and properties.

  1. Create an abstract dataclass Fighter with name (str), base_attack (int), base_defense (int), and a computed field power (int, not a constructor parameter). It must define an abstract method special_bonus(self) -> int. After creation, power should equal base_attack + base_defense + special_bonus(). Fighter must be ordered so fighters can be compared and sorted by their stats.
  2. Warrior inherits from Fighter and adds armor (int, default 10). Its special_bonus returns self.armor * 2.
  3. Assassin inherits from Fighter and adds stealth (int, default 15). Its special_bonus returns self.stealth * 3.
  4. Arena has name (str), roster (list of Fighter, empty by default). It provides:
    • register(self, fighter: Fighter) -> bool — adds a fighter only if no fighter with the same name is already registered.
    • match(self, name1: str, name2: str) -> str — finds two fighters by name and returns a result string. The fighter with the higher power wins. Study the expected output for the format.
    • leaderboard(self) -> list[str] — returns a list of strings ranking all fighters from highest to lowest power. Study the expected output for the format.

Input

w1 = Warrior("Duncan", 85, 70, 20)
w2 = Warrior("Stilgar", 78, 65)
a1 = Assassin("Feyd", 90, 50, 25)
a2 = Assassin("Thufir", 60, 80)

arena = Arena("Arrakeen Pit")
print(arena.register(w1))
print(arena.register(a1))
print(arena.register(w2))
print(arena.register(a2))
print(arena.register(Warrior("Duncan", 50, 50)))

print(arena.match("Duncan", "Feyd"))
print(arena.match("Stilgar", "Thufir"))

for line in arena.leaderboard():
    print(line)

Expected Output

True
True
True
True
False
Duncan (195) vs Feyd (215) -> Feyd wins!
Stilgar (163) vs Thufir (185) -> Thufir wins!
1. Feyd (215)
2. Duncan (195)
3. Thufir (185)
4. Stilgar (163)

Problem 8 (Extra): Fleet Convoy

You are managing a fleet of cargo ships. Each ship carries frozen, ordered cargo items and has a weight capacity. Combine dataclasses with @property, @classmethod, __add__, and __contains__.

  1. Cargo must be frozen and ordered. It has value (int), cargo_type (str), and weight (int).
  2. Ship has name (str), capacity (int), and hold (list of Cargo, empty by default). It provides:
    • A remaining_capacity property that returns how much weight the ship can still carry.
    • from_string(cls, text: str) -> Ship — a class method that creates a Ship from a string like "Atreides Frigate:80".
    • load(self, cargo: Cargo) -> bool — loads cargo only if it fits within remaining capacity.
    • __add__(self, other: Ship) -> Ship — merges two ships into a new one. The new ship’s name is both names joined by " & ", its capacity is the sum of both capacities, and its hold contains all cargo from both ships.
    • __contains__(self, cargo_type: str) -> bool — returns True if the ship carries cargo of the given type.
    • transfer(self, other: Ship, cargo_type: str) -> bool — moves a cargo item of the given type from this ship to another. Succeeds only if the cargo exists and the other ship has enough remaining capacity. Returns whether the transfer succeeded.

Input

c1 = Cargo(5000, "Spice", 30)
c2 = Cargo(2000, "Water", 50)
c3 = Cargo(8000, "Artifacts", 20)

s1 = Ship.from_string("Atreides Frigate:80")
s1.load(c1)
s1.load(c2)
print(s1.remaining_capacity)

s2 = Ship("Smuggler", 60)
s2.load(c3)
print(s2.remaining_capacity)

print("Spice" in s1)
print("Spice" in s2)

print(s1.transfer(s2, "Spice"))
print("Spice" in s1)
print("Spice" in s2)
print(s1.remaining_capacity)
print(s2.remaining_capacity)

print(s1.transfer(s2, "Water"))
print(s1.transfer(s2, "Weapons"))

Expected Output

0
40
True
False
True
False
True
30
10
False
False