Problem Set Week 6: OOP & Debugging
Problem Set 6: OOP and Debugging
This problem set challenges you to build real-world applications using object-oriented design, inheritance, polymorphism, and debugging strategies.
Exercise 1: Game Character System 🎮
Build a complete RPG character system with abilities and combat mechanics:
from abc import ABC, abstractmethod
import random
from enum import Enum
class DamageType(Enum):
PHYSICAL = "physical"
MAGICAL = "magical"
FIRE = "fire"
ICE = "ice"
POISON = "poison"
class Character(ABC):
"""Base class for all game characters"""
def __init__(self, name, hp, attack, defense, speed):
self.name = name
self.max_hp = hp
self.current_hp = hp
self.attack = attack
self.defense = defense
self.speed = speed
self.status_effects = [] # List of active status effects
self.abilities = []
self.experience = 0
self.level = 1
@abstractmethod
def special_ability(self, target):
"""Each character class has unique special ability"""
pass
def take_damage(self, amount, damage_type=DamageType.PHYSICAL):
"""Apply damage with type-based resistances"""
# Implement damage calculation with defense and resistances
def heal(self, amount):
"""Heal character"""
def is_alive(self):
return self.current_hp > 0
def gain_experience(self, amount):
"""Level up system"""
# Implement leveling mechanics
def attack_target(self, target):
"""Basic attack"""
damage = self.calculate_damage(target)
target.take_damage(damage)
return damage
class Warrior(Character):
"""Tank class with high defense"""
def __init__(self, name):
super().__init__(name, hp=120, attack=15, defense=20, speed=5)
self.rage = 0 # Special resource
def special_ability(self, target):
"""Berserker Rage: Double damage, lose defense"""
# Implement rage mode
def shield_bash(self, target):
"""Stun enemy for one turn"""
class Mage(Character):
"""Magic damage dealer"""
def __init__(self, name):
super().__init__(name, hp=60, attack=8, defense=5, speed=10)
self.mana = 100
self.spells = ["fireball", "ice_shard", "teleport"]
def special_ability(self, target):
"""Cast random elemental spell"""
def cast_spell(self, spell_name, target):
"""Cast specific spell with mana cost"""
class Rogue(Character):
"""High damage, low health assassin"""
def __init__(self, name):
super().__init__(name, hp=80, attack=20, defense=8, speed=15)
self.stealth = False
self.combo_points = 0
def special_ability(self, target):
"""Shadow Strike: Critical hit from stealth"""
def vanish(self):
"""Enter stealth mode"""
class Boss(Character):
"""Special boss enemy with phases"""
def __init__(self, name, phase_count=3):
super().__init__(name, hp=500, attack=25, defense=15, speed=8)
self.phase = 1
self.phase_count = phase_count
def change_phase(self):
"""Boss changes attack pattern at health thresholds"""
def area_attack(self, targets):
"""Attack all enemies"""
# Combat System
class CombatEngine:
"""Manages turn-based combat"""
def __init__(self, team1, team2):
self.team1 = team1 # List of characters
self.team2 = team2
self.turn_order = []
self.turn_count = 0
def determine_turn_order(self):
"""Sort by speed attribute"""
def execute_turn(self, attacker, action, target):
"""Process one character's turn"""
def check_victory(self):
"""Check if either team has won"""
def simulate_battle(self):
"""Auto-battle simulation"""
Requirements:
- Implement all character classes with unique abilities
- Create a damage calculation system with elemental resistances
- Add status effects (stun, poison, burn, freeze)
- Implement combo system for Rogue class
- Create boss phase mechanics
- Add loot drops and equipment system
Exercise 2: Real-Time Stock Trading Simulator 📈
Build a complete stock trading platform with real-time updates:
import random
import time
from datetime import datetime, timedelta
from collections import deque
from typing import Dict, List, Optional
class Stock:
"""Represents a tradable stock with price history"""
def __init__(self, symbol, name, initial_price):
self.symbol = symbol
self.name = name
self.current_price = initial_price
self.price_history = deque(maxlen=100) # Last 100 prices
self.volume = 0
self.market_cap = initial_price * 1000000 # Simplified
self.volatility = random.uniform(0.01, 0.05)
def update_price(self):
"""Simulate price movement based on volatility"""
# Implement realistic price movement
change = random.gauss(0, self.volatility)
self.current_price *= (1 + change)
self.price_history.append((datetime.now(), self.current_price))
def get_moving_average(self, periods=20):
"""Calculate moving average"""
def get_rsi(self):
"""Calculate Relative Strength Index"""
class Portfolio:
"""Manages user's stock holdings"""
def __init__(self, initial_cash=10000):
self.cash = initial_cash
self.holdings = {} # {symbol: quantity}
self.transaction_history = []
self.total_profit_loss = 0
def buy_stock(self, stock: Stock, quantity: int):
"""Execute buy order"""
def sell_stock(self, symbol: str, quantity: int, current_price: float):
"""Execute sell order"""
def get_portfolio_value(self, market: 'StockMarket'):
"""Calculate total portfolio value"""
def get_performance_metrics(self):
"""Return ROI, best/worst trades, etc."""
class Order:
"""Represents a buy/sell order"""
def __init__(self, order_type, symbol, quantity, price=None):
self.order_type = order_type # 'buy', 'sell', 'limit', 'stop-loss'
self.symbol = symbol
self.quantity = quantity
self.price = price # For limit orders
self.timestamp = datetime.now()
self.status = 'pending'
class TradingStrategy:
"""Base class for automated trading strategies"""
def analyze(self, stock: Stock, portfolio: Portfolio):
"""Analyze stock and return trading signal"""
raise NotImplementedError
class MomentumStrategy(TradingStrategy):
"""Buy rising stocks, sell falling ones"""
def analyze(self, stock: Stock, portfolio: Portfolio):
# Implement momentum trading logic
pass
class MeanReversionStrategy(TradingStrategy):
"""Buy oversold, sell overbought"""
def analyze(self, stock: Stock, portfolio: Portfolio):
# Implement mean reversion logic
pass
class StockMarket:
"""Simulates a stock market"""
def __init__(self):
self.stocks = {}
self.order_book = []
self.market_open = True
self.trading_hours = (9, 16) # 9 AM to 4 PM
def add_stock(self, stock: Stock):
"""List a new stock"""
def place_order(self, order: Order):
"""Place buy/sell order"""
def execute_orders(self):
"""Match and execute orders"""
def simulate_trading_day(self):
"""Run a full trading day simulation"""
def get_top_movers(self):
"""Return biggest gainers and losers"""
class TradingBot:
"""Automated trading bot"""
def __init__(self, strategy: TradingStrategy, portfolio: Portfolio):
self.strategy = strategy
self.portfolio = portfolio
self.active = False
def start_trading(self, market: StockMarket):
"""Begin automated trading"""
def backtest(self, historical_data):
"""Test strategy on historical data"""
Requirements:
- Implement realistic price movements with volatility
- Create order matching engine for limit orders
- Add technical indicators (RSI, MACD, Bollinger Bands)
- Implement stop-loss and take-profit orders
- Create backtesting framework for strategies
- Add market events (crashes, rallies) that affect all stocks
- Track and display portfolio performance metrics
Exercise 3: AI-Powered Chess Engine ♟️
Build a chess game with AI opponent using OOP principles:
from abc import ABC, abstractmethod
from enum import Enum
from typing import List, Tuple, Optional
import copy
class Color(Enum):
WHITE = "white"
BLACK = "black"
class PieceType(Enum):
PAWN = "pawn"
KNIGHT = "knight"
BISHOP = "bishop"
ROOK = "rook"
QUEEN = "queen"
KING = "king"
class Position:
"""Represents a board position"""
def __init__(self, row: int, col: int):
self.row = row
self.col = col
def __eq__(self, other):
return self.row == other.row and self.col == other.col
def __hash__(self):
return hash((self.row, self.col))
def to_chess_notation(self):
"""Convert to chess notation (e.g., 'e4')"""
return f"{chr(97 + self.col)}{8 - self.row}"
class Piece(ABC):
"""Abstract base class for chess pieces"""
def __init__(self, color: Color, position: Position):
self.color = color
self.position = position
self.has_moved = False
self.value = 0 # Piece value for AI evaluation
@abstractmethod
def get_valid_moves(self, board: 'Board') -> List[Position]:
"""Return all valid moves for this piece"""
pass
@abstractmethod
def symbol(self) -> str:
"""Unicode symbol for display"""
pass
def can_attack(self, target_pos: Position, board: 'Board') -> bool:
"""Check if piece can attack target position"""
return target_pos in self.get_valid_moves(board)
class Pawn(Piece):
def __init__(self, color: Color, position: Position):
super().__init__(color, position)
self.value = 1
self.en_passant_vulnerable = False
def get_valid_moves(self, board: 'Board') -> List[Position]:
"""Pawn movement: forward, capture diagonally"""
# Implement pawn logic including:
# - Single/double move from starting position
# - Diagonal captures
# - En passant
# - Promotion
def symbol(self) -> str:
return '♙' if self.color == Color.WHITE else '♟'
class Knight(Piece):
def __init__(self, color: Color, position: Position):
super().__init__(color, position)
self.value = 3
def get_valid_moves(self, board: 'Board') -> List[Position]:
"""L-shaped movement"""
def symbol(self) -> str:
return '♘' if self.color == Color.WHITE else '♞'
class Bishop(Piece):
def __init__(self, color: Color, position: Position):
super().__init__(color, position)
self.value = 3
def get_valid_moves(self, board: 'Board') -> List[Position]:
"""Diagonal movement"""
def symbol(self) -> str:
return '♗' if self.color == Color.WHITE else '♝'
class Rook(Piece):
def __init__(self, color: Color, position: Position):
super().__init__(color, position)
self.value = 5
def get_valid_moves(self, board: 'Board') -> List[Position]:
"""Horizontal and vertical movement"""
def symbol(self) -> str:
return '♖' if self.color == Color.WHITE else '♜'
class Queen(Piece):
def __init__(self, color: Color, position: Position):
super().__init__(color, position)
self.value = 9
def get_valid_moves(self, board: 'Board') -> List[Position]:
"""Combination of rook and bishop movement"""
def symbol(self) -> str:
return '♕' if self.color == Color.WHITE else '♛'
class King(Piece):
def __init__(self, color: Color, position: Position):
super().__init__(color, position)
self.value = 1000 # Infinite value
def get_valid_moves(self, board: 'Board') -> List[Position]:
"""One square in any direction, plus castling"""
def symbol(self) -> str:
return '♔' if self.color == Color.WHITE else '♚'
class Move:
"""Represents a chess move"""
def __init__(self, piece: Piece, from_pos: Position, to_pos: Position,
captured_piece: Optional[Piece] = None):
self.piece = piece
self.from_pos = from_pos
self.to_pos = to_pos
self.captured_piece = captured_piece
self.is_castle = False
self.is_en_passant = False
self.promotion_piece = None
def to_notation(self) -> str:
"""Convert to algebraic notation"""
class Board:
"""Chess board with piece positions"""
def __init__(self):
self.grid = [[None for _ in range(8)] for _ in range(8)]
self.move_history = []
self.captured_pieces = {Color.WHITE: [], Color.BLACK: []}
self.current_turn = Color.WHITE
def setup_initial_position(self):
"""Place pieces in starting positions"""
def make_move(self, move: Move):
"""Execute a move on the board"""
def undo_move(self):
"""Undo the last move"""
def is_check(self, color: Color) -> bool:
"""Check if king is under attack"""
def is_checkmate(self, color: Color) -> bool:
"""Check if king is in checkmate"""
def is_stalemate(self, color: Color) -> bool:
"""Check if position is stalemate"""
def get_all_valid_moves(self, color: Color) -> List[Move]:
"""Get all legal moves for a color"""
def evaluate_position(self) -> float:
"""Evaluate board position for AI"""
class ChessAI:
"""AI player using minimax algorithm"""
def __init__(self, color: Color, depth: int = 3):
self.color = color
self.depth = depth
def get_best_move(self, board: Board) -> Move:
"""Find best move using minimax with alpha-beta pruning"""
def minimax(self, board: Board, depth: int, alpha: float, beta: float,
maximizing: bool) -> Tuple[float, Optional[Move]]:
"""Minimax algorithm with alpha-beta pruning"""
def evaluate_board(self, board: Board) -> float:
"""Heuristic evaluation of board position"""
# Consider:
# - Material balance
# - Piece positions (center control)
# - King safety
# - Pawn structure
class ChessGame:
"""Main game controller"""
def __init__(self, player1_ai: bool = False, player2_ai: bool = True):
self.board = Board()
self.board.setup_initial_position()
self.ai_players = {}
if player1_ai:
self.ai_players[Color.WHITE] = ChessAI(Color.WHITE)
if player2_ai:
self.ai_players[Color.BLACK] = ChessAI(Color.BLACK)
def play(self):
"""Main game loop"""
def display_board(self):
"""ASCII or Unicode board display"""
Requirements:
- Implement all piece movement rules including special moves
- Add check, checkmate, and stalemate detection
- Create minimax AI with alpha-beta pruning
- Implement move validation and board evaluation
- Add opening book for first few moves
- Create position analysis (material count, threats, etc.)
- Support PGN notation for saving/loading games
Exercise 4: Debugging Utilities
Create a debugging toolkit:
import functools
import time
import traceback
from typing import Any, Callable
class Debugger:
"""Debugging utility class"""
def __init__(self, verbose=True):
self.verbose = verbose
self.call_count = {}
self.execution_times = {}
self.errors = []
def trace(self, func):
"""Decorator to trace function calls"""
@functools.wraps(func)
def wrapper(*args, **kwargs):
# Log entry, exit, arguments, return value
pass
return wrapper
def time_it(self, func):
"""Decorator to time function execution"""
@functools.wraps(func)
def wrapper(*args, **kwargs):
# Time the execution
pass
return wrapper
def catch_errors(self, func):
"""Decorator to catch and log errors"""
@functools.wraps(func)
def wrapper(*args, **kwargs):
# Catch exceptions, log them, optionally re-raise
pass
return wrapper
def validate_types(self, func):
"""Decorator to validate argument types"""
@functools.wraps(func)
def wrapper(*args, **kwargs):
# Check types match function annotations
pass
return wrapper
def memoize(self, func):
"""Decorator to cache function results"""
cache = {}
@functools.wraps(func)
def wrapper(*args):
# Cache results for pure functions
pass
return wrapper
def get_report(self):
"""Generate debugging report"""
pass
Exercise 5: Custom Exception Hierarchy
Design exceptions for a web application:
class ApplicationError(Exception):
"""Base exception for application"""
def __init__(self, message, error_code=None, details=None):
super().__init__(message)
self.error_code = error_code
self.details = details or {}
self.timestamp = self._get_timestamp()
def _get_timestamp(self):
"""Get current timestamp"""
from datetime import datetime
return datetime.now().isoformat()
def to_dict(self):
"""Convert to dictionary for API responses"""
pass
class ValidationError(ApplicationError):
"""Data validation errors"""
def __init__(self, field, value, message):
self.field = field
self.value = value
super().__init__(message, error_code="VALIDATION_ERROR")
class AuthenticationError(ApplicationError):
"""Authentication failed"""
pass
class AuthorizationError(ApplicationError):
"""User not authorized"""
pass
class ResourceNotFoundError(ApplicationError):
"""Requested resource not found"""
pass
class RateLimitError(ApplicationError):
"""Rate limit exceeded"""
def __init__(self, limit, reset_time):
self.limit = limit
self.reset_time = reset_time
super().__init__(f"Rate limit {limit} exceeded", error_code="RATE_LIMIT")
# Error handler
class ErrorHandler:
def __init__(self):
self.handlers = {}
def register_handler(self, error_class, handler_func):
"""Register handler for specific error type"""
def handle(self, error):
"""Handle error with appropriate handler"""
Exercise 6: Design Patterns Implementation
Implement common design patterns:
# Singleton Pattern
class DatabaseConnection:
"""Singleton database connection"""
_instance = None
def __new__(cls):
# Implement singleton pattern
pass
def query(self, sql):
"""Execute SQL query"""
pass
# Observer Pattern
class Subject:
"""Observable subject"""
def __init__(self):
self._observers = []
def attach(self, observer):
"""Attach an observer"""
def detach(self, observer):
"""Detach an observer"""
def notify(self, event):
"""Notify all observers"""
class Observer(ABC):
"""Observer interface"""
@abstractmethod
def update(self, event):
pass
# Factory Pattern
class AnimalFactory:
"""Factory for creating animals"""
@staticmethod
def create_animal(animal_type, **kwargs):
"""Create animal based on type"""
animals = {
'dog': Dog,
'cat': Cat,
'bird': Bird
}
# Implementation here
# Strategy Pattern
class SortStrategy(ABC):
"""Sorting strategy interface"""
@abstractmethod
def sort(self, data):
pass
class QuickSort(SortStrategy):
def sort(self, data):
# Implement quicksort
pass
class MergeSort(SortStrategy):
def sort(self, data):
# Implement mergesort
pass
class Sorter:
def __init__(self, strategy: SortStrategy):
self.strategy = strategy
def sort_data(self, data):
return self.strategy.sort(data)
Exercise 7: Advanced OOP Features
Implement advanced Python OOP features:
class Vector:
"""N-dimensional vector with operator overloading"""
def __init__(self, *components):
self.components = list(components)
def __add__(self, other):
"""Vector addition"""
def __sub__(self, other):
"""Vector subtraction"""
def __mul__(self, scalar):
"""Scalar multiplication"""
def __truediv__(self, scalar):
"""Scalar division"""
def __abs__(self):
"""Magnitude of vector"""
def __getitem__(self, index):
"""Get component by index"""
def __setitem__(self, index, value):
"""Set component by index"""
def __len__(self):
"""Number of dimensions"""
def __repr__(self):
"""String representation"""
def dot(self, other):
"""Dot product"""
def normalize(self):
"""Return unit vector"""
class Matrix:
"""Matrix with operator overloading"""
def __init__(self, data):
# data is list of lists
self.data = data
def __add__(self, other):
"""Matrix addition"""
def __mul__(self, other):
"""Matrix multiplication (handles both matrix and scalar)"""
def __pow__(self, n):
"""Matrix power"""
@property
def T(self):
"""Transpose property"""
def determinant(self):
"""Calculate determinant"""
def inverse(self):
"""Calculate inverse if exists"""
Exercise 8: Debugging Challenge
Debug and fix these problematic classes:
# Bug 1: Memory leak in circular reference
class Node:
def __init__(self, value):
self.value = value
self.parent = None
self.children = []
def add_child(self, child):
self.children.append(child)
child.parent = self # Creates circular reference
# Bug 2: Mutable default argument
class TaskList:
def __init__(self, tasks=[]): # Bug: mutable default
self.tasks = tasks
def add_task(self, task):
self.tasks.append(task)
# Bug 3: Incorrect inheritance
class Bird:
def fly(self):
return "Flying"
class Penguin(Bird): # Penguins can't fly!
pass
# Bug 4: Property setter infinite recursion
class Temperature:
def __init__(self, celsius=0):
self.celsius = celsius
@property
def fahrenheit(self):
return self.celsius * 9/5 + 32
@fahrenheit.setter
def fahrenheit(self, value):
self.celsius = (value - 32) * 5/9 # This might cause issues
# Fix all bugs and explain the problems
Exercise 9: Performance Debugging
Create a performance profiler:
import cProfile
import pstats
import io
from memory_profiler import profile # If available
class PerformanceProfiler:
"""Profile code performance"""
def __init__(self):
self.profiles = {}
def profile_function(self, func, *args, **kwargs):
"""Profile a single function call"""
profiler = cProfile.Profile()
profiler.enable()
result = func(*args, **kwargs)
profiler.disable()
# Process and store results
def compare_implementations(self, implementations, test_data):
"""
Compare performance of different implementations
implementations: dict of {name: function}
"""
def find_bottlenecks(self, func, *args, **kwargs):
"""Identify performance bottlenecks"""
def memory_usage(self, func, *args, **kwargs):
"""Track memory usage"""
def generate_report(self):
"""Generate performance report"""
# Example: Compare different fibonacci implementations
def fib_recursive(n):
if n <= 1:
return n
return fib_recursive(n-1) + fib_recursive(n-2)
def fib_iterative(n):
if n <= 1:
return n
a, b = 0, 1
for _ in range(n-1):
a, b = b, a + b
return b
def fib_memoized(n, cache={}):
if n in cache:
return cache[n]
if n <= 1:
return n
cache[n] = fib_memoized(n-1, cache) + fib_memoized(n-2, cache)
return cache[n]
Submission Instructions
- Submit all solutions in a single Python file or module
- Include comprehensive docstrings
- Add unit tests for at least 3 classes
- Include examples demonstrating polymorphism and inheritance
- Due: Before Week 7 lecture