Strings + Tic‑Tac‑Toe — Homework
This homework practices Python string basics using the Tic‑Tac‑Toe board representation from the strings lesson. Each part is worth up to 0.3 points, with up to 0.03 points exceptional credit for clear, pythonic solutions and docstrings.
Instructions: Replace the TODO stubs below with working code. Run the tests for each part, then run the final grader cell to get your score breakdown.
Quick reminder — board representation
- We use a list of 9 strings for the board, e.g.
[' ', 'X', 'O', ...]. - Helpful functions from the lesson (available when you run that notebook) include:
print_board(board)andcheck_winner(board, symbol).
If you want to import helpers from the lesson notebook, copy the helper cells or re-run that notebook kernel first.
Popcorn Hack (warmup)
Small, quick tasks to get started with strings.
1) Create stringify_board(board) that returns a single-line string representing the board using ''.join(...).
2) Create parse_board(s) that converts the one-line string back to a board list.
# Popcorn Hack: stringify_board / parse_board
def stringify_board(board):
"""Return a single-line string representation of the board.
Example: [' ', 'X', 'O', ...] -> ' XO ...'
"""
# TODO: implement using ''.join
raise NotImplementedError('Implement stringify_board')
def parse_board(s):
"""Convert a one-line board string back to a list of 9 single-character strings."""
# TODO: implement using list(...) or similar
raise NotImplementedError('Implement parse_board')
# quick warmup checks (you can run these after implementing)
print('Warmup: run stringify_board/parse_board checks after implementing')
Homework: Part A — String roundtrip (0.3 pts)
Implement stringify_board and parse_board (if you didn’t in the warmup). The grader checks that stringify -> parse returns the original board and that you used string operations (heuristic: presence of join or list( in source). Exceptional credit if your functions include clear docstrings and use join/split appropriately.
# Tests & grader for Part A
def _tests_A():
boards = [
[' ', 'X', 'O', ' ', 'X', ' ', 'O', ' ', ' '],
['X']*9,
[' ']*9
]
for b in boards:
s = stringify_board(b)
b2 = parse_board(s)
assert isinstance(s, str), 'stringify_board must return a str'
assert isinstance(b2, list) and len(b2) == 9, 'parse_board must return list of length 9'
assert b2 == b, f'roundtrip mismatch: {b} -> {b2}'
print('Part A tests passed')
def grade_part_A():
try:
_tests_A()
base = 0.3
except AssertionError as e:
print('Part A failed:', e)
return 0.0, 0.0
# exceptional credit heuristics
import inspect
extra = 0.0
try:
src = inspect.getsource(stringify_board) + inspect.getsource(parse_board)
if ('join' in src or 'split' in src or 'list(' in src) and '"""' in src:
extra = 0.03
except Exception:
pass
return base, extra
print('Run grade_part_A() after implementing the functions')
Homework: Part B — Find a two‑in‑a‑row (0.3 pts)
Implement find_best_move(board, player) that returns the index (0-8) of an empty square that would let player complete three-in-a-row, or -1 if none. Use readable string/list handling. Exceptional credit for concise solutions using comprehensions or any()/all() and a docstring.
# Part B: find_best_move stub
def find_best_move(board, player):
"""Return index 0-8 of empty square completing player's three-in-a-row, or -1 if none."""
# TODO: implement (readable, beginner-friendly)
raise NotImplementedError('Implement find_best_move')
# Tests & grader for Part B
def _tests_B():
cases = [
(['X','X',' ',' ',' ',' ',' ',' ',' '], 'X', 2),
(['O',' ',' ','O',' ',' ','O',' ',' '], 'O', 6),
(['X','O','X','O','X','O','O','X','O'], 'X', -1),
]
for b, p, expected in cases:
res = find_best_move(b, p)
assert isinstance(res, int), 'find_best_move must return an int'
assert res == expected, f'expected {expected}, got {res} for {b} {p}'
print('Part B tests passed')
def grade_part_B():
try:
_tests_B()
base = 0.3
except AssertionError as e:
print('Part B failed:', e)
return 0.0, 0.0
import inspect
extra = 0.0
try:
src = inspect.getsource(find_best_move)
if ('all(' in src or 'any(' in src or '[' in src) and '"""' in src:
extra = 0.03
except Exception:
pass
return base, extra
print('Run grade_part_B() after implementing find_best_move')
Homework: Part C — Pretty board string (0.3 pts)
Implement format_board_pretty(board) that returns a multi-line string showing the board in a human-friendly layout (like the print_board output, but return the string instead of printing). This practices string concatenation, f-strings, and multi-line strings. Exceptional credit for clean docstring and use of join or f-strings.
# Part C: format_board_pretty stub
def format_board_pretty(board):
"""Return a multi-line string representing the board layout.
Example output (with X/O and spaces):
X | O |
---+---+---
O | X | X
---+---+---
| |
"""
# TODO: build and return the formatted string
raise NotImplementedError('Implement format_board_pretty')
# Tests & grader for Part C
def _tests_C():
b = ['X','O',' ', ' ', 'X', ' ', 'O',' ',' ']
s = format_board_pretty(b)
assert isinstance(s, str), 'format_board_pretty must return a string'
# basic content checks
assert '|' in s and '---+---+---' in s, 'format should include separators'
# check that each board symbol appears in the string
for ch in ['X','O']:
assert ch in s, f'{ch} should appear in formatted board'
print('Part C tests passed')
def grade_part_C():
try:
_tests_C()
base = 0.3
except AssertionError as e:
print('Part C failed:', e)
return 0.0, 0.0
import inspect
extra = 0.0
try:
src = inspect.getsource(format_board_pretty)
if ('join' in src or 'f"' in src or '"""' in src) and '"""' in src:
extra = 0.03
except Exception:
pass
return base, extra
print('Run grade_part_C() after implementing format_board_pretty')
Submission
- Keep your implementations short and well-documented — add a docstring to earn exceptional credit.
- Use
''.join(board)andlist(s)/splitfor the string roundtrip (Part A). - For Part B, reuse the
win_patternsidea from the lesson and count marks in each pattern. - For Part C, build the multi-line string with f-strings or
joinfor clarity. - Submit the submission here: https://forms.gle/AAeLJGBXpMaU8Hf18