Popcorn Hack 1 — Book class
Context: A Book is a small object that stores identifying information. This hack helps you practice defining a class, adding an initializer (__init__), and a simple method that returns a description string.
What to do: Read the code cell below, run it, then try the following:
- Create another
Bookinstance with a different title and author and print its description. - Add a
yearattribute to the class, update__init__, and include the year in thedescription()output.
# Popcorn Hack 1: Create a simple class for a Book
class Book:
def __init__(self, title, author):
self.title = title
self.author = author
def description(self):
return f"'{self.title}' by {self.author}"
# Practice: Create a Book object and print its description
my_book = Book("1984", "George Orwell")
print(my_book.description())
'1984' by George Orwell
Popcorn Hack 2 — Rectangle class
Context: Practice creating a class that represents a simple geometric shape. This hack focuses on initializing numeric attributes and writing a method that computes and returns a derived value (area).
What to do: Run the code cell below, then try:
- Add a
perimeter()method that returns the perimeter. - Add validation in
__init__to ensure width and height are positive numbers (raise ValueError otherwise).
# Popcorn Hack 2: Create a class for a Rectangle with area calculation
class Rectangle:
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
# Practice: Create a Rectangle object and print its area
my_rectangle = Rectangle(5, 3)
print("Area:", my_rectangle.area())
Homework: Tic-Tac-Toe (3 parts)
This homework uses small, function-style pieces of the Tic-Tac-Toe code (board as a list of 9 strings).
Scoring: each part is worth up to 0.3 points, plus up to 0.03 points exceptional credit for elegant/pythonic solutions (e.g., use of all()/any() or clear docstrings).
Instructions: For each part below, replace the TODO implementation with working code and run the cell’s tests. When you’re ready, run the final grader cell which will print your score breakdown.
Part A — Win detection (0.3 pts)
Implement check_winner(board, symbol) which returns True when symbol (e.g. ‘X’) appears in any winning 3-in-a-row pattern on board. board is a list of 9 strings where an empty square is ' '.
Exceptional credit (0.01 pts): your implementation uses all() or any() and includes a short docstring explaining the approach.
Part B — Find winning/blocking move (0.3 pts)
Implement find_best_move(board, player) which returns the index (0-8) of an empty square that completes player to three-in-a-row, or -1 if none exists. This is the helper used by simple Tic-Tac-Toe AIs.
Exceptional credit (0.01 pts): your implementation is concise (uses comprehensions) and includes a short docstring.
# Part A: implement check_winner(board, symbol)
win_patterns = [
[0, 1, 2], [3, 4, 5], [6, 7, 8], # Rows
[0, 3, 6], [1, 4, 7], [2, 5, 8], # Columns
[0, 4, 8], [2, 4, 6] # Diagonals
]
def check_winner(board, symbol):
"""
Check if the given symbol has a winning pattern on the board.
Args:
board (list): A list of 9 strings representing the Tic-Tac-Toe board.
symbol (str): The player's symbol ('X' or 'O') to check for a win.
Returns:
bool: True if the symbol has a winning pattern, False otherwise.
"""
# Ensure the board is valid
if not isinstance(board, list) or len(board) != 9:
raise ValueError("board must be a list of 9 elements")
# Check all win patterns
for pattern in win_patterns:
if all(board[i] == symbol for i in pattern):
return True
return False
# Tests for Part A
_tests_A = [
(['X', 'X', 'X', ' ', ' ', ' ', ' ', ' ', ' '], 'X', True),
(['O', ' ', ' ', 'O', ' ', ' ', 'O', ' ', ' '], 'O', True),
(['X', 'O', 'X', 'O', 'X', 'O', 'O', 'X', 'O'], 'X', False),
]
def grade_part_A():
passed = 0
for b, s, expected in _tests_A:
try:
result = check_winner(b, s)
except Exception as e:
print('Part A: error during execution:', e)
return 0.0, 0.0 # No points if it crashes
if result == expected:
passed += 1
score = 0.3 * (passed / len(_tests_A))
# Small heuristic for exceptional credit: check source for 'all(' or 'any(' and presence of docstring
import inspect
extra = 0.0
try:
src = inspect.getsource(check_winner)
if ('all(' in src or 'any(' in src) and check_winner.__doc__:
extra = 0.03
except Exception:
pass
return score, extra
# Run quick self-check (you'll see failures until you implement the function)
print('Part A quick check: call grade_part_A() after implementing check_winner')
Part C — Safe move placement (0.3 pts)
Implement make_move(board, position, player) which attempts to place player at position (0-8). It should return True and update the board when the move is valid; return False (and leave board unchanged) when the position is invalid or occupied.
Exceptional credit (0.01 pts): your implementation includes input validation and a docstring describing edge cases.
# Part C: implement make_move(board, position, player)
def make_move(board, position, player):
"""
Attempt to place `player` at `position` (0-8) on `board`.
Args:
board (list): A list of 9 strings representing the Tic-Tac-Toe board.
position (int): The index (0-8) where the player wants to place their symbol.
player (str): The player's symbol ('X' or 'O').
Returns:
bool: True if the move is valid and the board is updated, False otherwise.
"""
# Validate the board
if not isinstance(board, list) or len(board) != 9:
raise ValueError("board must be a list of 9 elements")
# Validate the position
if not isinstance(position, int) or position < 0 or position > 8:
return False
# Check if the position is already occupied
if board[position] != ' ':
return False
# Make the move
board[position] = player
return True
# Tests for Part C
_tests_C = [
([' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '], 4, 'X', True),
(['X', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '], 0, 'O', False),
(['X', 'O', 'X', 'O', 'X', 'O', 'O', 'X', 'O'], 2, 'X', False),
]
def grade_part_C():
passed = 0
for b_init, pos, player, expected in _tests_C:
b = b_init.copy()
try:
result = make_move(b, pos, player)
except Exception as e:
print('Part C: error during execution:', e)
return 0.0, 0.0
# Check boolean result and that board updated only when expected
if result == expected:
if expected:
if b[pos] == player:
passed += 1
else:
# Board should be unchanged for failed move
if b == b_init:
passed += 1
score = 0.3 * (passed / len(_tests_C))
import inspect
extra = 0.0
try:
src = inspect.getsource(make_move)
# Exceptional credit: docstring present and no unnecessary raising
if make_move.__doc__ and 'return' in src:
extra = 0.03
except Exception:
pass
return score, extra
print('Part C quick check: call grade_part_C() after implementing make_move')
Submission
Submit the hack here: https://forms.gle/hH9g7Ja9JQvXmsp89