A structured Python learning portfolio built through the freeCodeCamp Python Certification, with a long-term focus on scientific computing, numerical modeling, environmental data processing, coastal engineering applications, and algorithmic problem solving.
This repository documents my progression from Python fundamentals to functions, validation, debugging, object-oriented programming, inheritance, polymorphism, custom exceptions, abstract base classes, strategy-based design, custom data structures, search algorithms, graph algorithms, shortest-path algorithms, divide-and-conquer sorting, in-place sorting, recursive algorithms, checksum validation, numerical root finding, formatted reporting, exact multi-line output generation, and larger certification projects.
The immediate goal is to complete the freeCodeCamp Python Certification with correct, readable, tested, and well-documented implementations. The long-term objective is to apply Python to coastal and environmental engineering workflows such as hydrodynamic modeling, salinity intrusion analysis, environmental data validation, numerical methods, tolerance-based approximation, scientific visualization, search/indexing utilities, sorting workflows, checksum-style validation, recursive workflow processing, and research automation.
- Repository at a Glance
- Learning Progress
- Repository Structure
- Workshops
- Labs
- Certification Projects
- Technical Competencies
- Selected Project Highlights
- Object-Oriented, Data-Structure, and Algorithm Progression
- Development Methodology
- Common Debugging Lessons
- Engineering-Oriented Direction
- Current Roadmap
- Project Status
- Python Certification Review
- Resources
- Acknowledgements
- Author
| Area | Purpose | Completed |
|---|---|---|
| Workshops | Guided projects introducing new Python concepts incrementally | 17 |
| Labs | Independent implementations based on user stories and automated tests | 15 |
| Certification Projects | Larger projects combining multiple programming concepts | 5 |
| Total | Documented Python projects | 37 |
Workshops █████████████████ 17 completed
Labs ███████████████ 15 completed
Certification Projects █████░░░░░░░░░ 5 completed
Overall █████████████████████████████████████ 37 completed
Python Fundamentals
↓
Functions and Validation
↓
Collections and Text Processing
↓
Debugging Existing Programs
↓
Classes and Object Construction
↓
Object Composition
↓
Properties and Encapsulation
↓
Validated Object State
↓
Inheritance and Subclassing
↓
Polymorphism and Method Overriding
↓
Custom Exceptions
↓
Abstract Base Classes
↓
Interface-Oriented Inheritance
↓
Reusable Class Hierarchies and Object Invariants
↓
Strategy Pattern and Polymorphic Algorithms
↓
Custom Data Structures
↓
Linked Lists, Nodes, and References
↓
Hash Tables, Buckets, and Collision Handling
↓
Binary Search and Algorithmic Thinking
↓
Merge Sort and Divide-and-Conquer Thinking
↓
Shortest Path Algorithm and Weighted Graph Thinking
↓
Quicksort and Recursive Partitioning
↓
Selection Sort and In-Place Mutation
↓
Luhn Algorithm and Checksum Validation
↓
Adjacency List to Matrix Conversion
↓
Depth-First Search and Graph Reachability
↓
N-Queens, Backtracking, and Constraint Solving
↓
Bisection Method and Numerical Root Finding
↓
Formatted Reports and Visualizations
↓
Tower of Hanoi and Recursive State Generation
| # | Project | Primary Concepts | Status |
|---|---|---|---|
| 1 | Report Card Printer | Variables, arithmetic, formatted output | ✅ |
| 2 | Employee Profile Generator | Functions, parameters, strings | ✅ |
| 3 | Bill Splitter | User input, calculations, validation | ✅ |
| 4 | Movie Ticket Booking Calculator | Conditional logic, pricing rules | ✅ |
| 5 | Build a Caesar Cipher | Translation tables, encryption, string processing | ✅ |
| 6 | Build a PIN Extractor | Regular expressions, structured text extraction | ✅ |
| 7 | Build a Medical Data Validator | Dictionaries, validation, error reporting | ✅ |
| 8 | Build a Musical Instrument Inventory | Classes, objects, attributes, methods | ✅ |
| 9 | Build an Email Simulator | Object composition, inbox management, timestamps | ✅ |
| 10 | Build a Salary Tracker | Properties, setters, encapsulation, class state | ✅ |
| 11 | Build a Media Catalogue | Inheritance, polymorphism, custom exceptions, collection filtering | ✅ |
| 12 | Build a Discount Calculator | Abstract base classes, strategy pattern, polymorphic pricing, type hints | ✅ |
| 13 | Build a Linked List | Nodes, references, traversal, insertion, removal, custom data structures | ✅ |
| 14 | Build a Binary Search | Sorted data, midpoint comparison, boundary updates, algorithm tracing | ✅ |
| 15 | Implement the Merge Sort Algorithm | Recursion, divide and conquer, slicing, merging sorted halves | ✅ |
| 16 | Implement the Shortest Path Algorithm | Weighted graphs, adjacency matrices, Dijkstra's algorithm, edge relaxation, path reconstruction | ✅ |
| 17 | Implement the Breadth-First Search Algorithm | BFS, FIFO queues, state-space exploration, balanced-parentheses generation | ✅ |
| # | Project | Primary Concepts | Status |
|---|---|---|---|
| 1 | Travel Weather Planner | Conditions, input, validation, decision logic | ✅ |
| 2 | Apply Discount Function | Functions, calculations, business rules | ✅ |
| 3 | Build an RPG Character | Dictionaries, constraints, structured validation | ✅ |
| 4 | Build a Number Pattern Generator | Loops, ranges, pattern generation | ✅ |
| 5 | Debug an ISBN Validator | Debugging, checksum logic, control flow | ✅ |
| 6 | Build a Planet Class | Classes, exceptions, methods, object representation | ✅ |
| 7 | Build a Game Character Stats Tracker | Properties, getters, setters, encapsulation | ✅ |
| 8 | Build a Player Interface | Abstract base classes, inheritance, random movement, path tracking | ✅ |
| 9 | Implement the Bisection Method | Numerical approximation, interval halving, tolerance, convergence | ✅ |
| 10 | Implement the Quicksort Algorithm | Recursion, pivot partitioning, new-list sorting, duplicate handling | ✅ |
| 11 | Implement the Selection Sort Algorithm | In-place sorting, minimum search, nested loops, controlled swaps | ✅ |
| 12 | Implement the Luhn Algorithm | Checksum validation, string cleaning, digit processing, modulo checks | ✅ |
| 13 | Build an Adjacency List to Matrix Converter | Graph representations, dictionaries, nested lists, matrix construction | ✅ |
| 14 | Implement the Depth-First Search Algorithm | DFS, stacks, LIFO traversal, reachability, cycle prevention | ✅ |
| 15 | Implement the N-Queens Algorithm | DFS, recursion, backtracking, constraint tracking, state restoration | ✅ |
| # | Project | Primary Concepts | Tests | Status |
|---|---|---|---|---|
| 1 | Build a User Configuration Manager | CRUD operations, dictionaries, configuration management | 27/27 | ✅ |
| 2 | Build a Budget App | Classes, ledgers, transfers, validation, reports, charts | 24/24 | ✅ |
| 3 | Build a Polygon Area Calculator | Inheritance, method overriding, geometry, object invariants | 22/22 | ✅ |
| 4 | Build a Hash Table | Hashing, nested dictionaries, collision handling, lookup and deletion | 22/22 | ✅ |
| 5 | Implement the Tower of Hanoi Algorithm | Recursion, list stacks, state recording, exact multi-line output | 8/8 | ✅ |
freecodecamp-python/
├── workshops/
│ ├── report-card-printer/
│ ├── employee-profile-generator/
│ ├── bill-splitter/
│ ├── movie-ticket-booking-calculator/
│ ├── build-a-caesar-cipher/
│ ├── build-a-pin-extractor/
│ ├── build-a-medical-data-validator/
│ ├── build-a-musical-instrument-inventory/
│ ├── build-an-email-simulator/
│ ├── build-a-salary-tracker/
│ ├── build-a-media-catalogue/
│ ├── build-a-discount-calculator/
│ ├── build-a-linked-list/
│ ├── build-a-binary-search/
│ ├── implement-the-merge-sort-algorithm/
│ ├── implement-the-shortest-path-algorithm/
│ ├── implement-the-breadth-first-search-algorithm/
│ └── README.md
│
├── labs/
│ ├── travel-weather-planner.py
│ ├── apply-discount-function.py
│ ├── build-an-rpg-character.py
│ ├── build-a-number-pattern-generator.py
│ ├── debug-an-isbn-validator.py
│ ├── build-a-planet-class.py
│ ├── build-a-game-character-stats-tracker.py
│ ├── build-a-player-interface.py
│ ├── implement-the-bisection-method.py
│ ├── implement-the-quicksort-algorithm.py
│ ├── implement-the-selection-sort-algorithm.py
│ ├── implement-the-luhn-algorithm.py
│ ├── build-an-adjacency-list-to-matrix-converter.py
│ ├── implement-the-depth-first-search-algorithm.py
│ ├── implement-the-n-queens-algorithm.py
│ └── README.md
│
├── certification-projects/
│ ├── build-a-user-configuration-manager/
│ │ ├── main.py
│ │ └── README.md
│ │
│ ├── build-a-budget-app/
│ │ ├── main.py
│ │ └── README.md
│ │
│ ├── build-a-polygon-area-calculator/
│ │ ├── main.py
│ │ └── README.md
│ │
│ ├── build-a-hash-table/
│ │ ├── main.py
│ │ └── README.md
│ │
│ ├── implement_the_tower_of_hanoi_algorithm/
│ │ ├── main.py
│ │ └── README.md
│ │
│ └── README.md
│
├── PYTHON_REVIEW.md
└── README.md
- Workshops use separate directories because they are developed through guided stages and often include dedicated documentation.
- Labs are stored as individual Python files because they are compact, independent exercises.
- Certification projects use dedicated directories because they combine multiple concepts and require project-level documentation.
- The root README provides the overall learning narrative, while subdirectory READMEs document category-specific progress.
Workshops introduce new concepts through guided implementation.
They are used to:
- Learn unfamiliar syntax
- Observe implementation patterns
- Practice one concept at a time
- Build confidence before independent work
- Develop reusable programming habits
- Connect syntax with algorithmic reasoning
The workshop sequence has progressed from basic formatting and functions to regular expressions, structured validation, object composition, properties, setters, controlled class state, inheritance, polymorphism, custom exceptions, abstract base classes, strategy-based software design, reference-based data structures, algorithmic search, divide-and-conquer sorting, weighted graph representation, shortest-path computation, breadth-first search, FIFO queues, and state-space exploration.
The latest completed workshop, Implement the Breadth-First Search Algorithm, introduced FIFO queue behavior, tuple-based state representation, level-by-level exploration, constrained successor generation, balanced-parentheses generation, and the distinction between complete and expandable states.
Detailed workshop documentation is maintained in workshops/README.md.
Labs require independent interpretation of requirements and user stories.
They are used to practice:
- Translating specifications into code
- Designing solutions without step-by-step instructions
- Mapping automated tests to implementation tasks
- Diagnosing failed tests
- Handling boundary conditions
- Matching exact output formats
- Refactoring final solutions for clarity
The latest completed lab, Implement the N-Queens Algorithm, introduced recursive depth-first search, backtracking, row-by-row state construction, column and diagonal conflict tracking, branch pruning, mutable-state snapshots, deterministic traversal order, and exact state restoration. The preceding Depth-First Search Algorithm lab introduced explicit stacks, LIFO traversal, graph reachability, adjacency-matrix traversal, visited-state tracking, and cycle prevention.
Detailed lab documentation is maintained in labs/README.md.
Certification projects combine multiple Python concepts into larger implementations.
The latest completed certification project, Implement the Tower of Hanoi Algorithm, introduced recursive problem solving, list-based stack behavior, state recording, exact multi-line output generation, and minimum-move algorithm design.
Detailed certification-project documentation is maintained in certification-projects/README.md.
User Configuration Manager
↓
Budget App
↓
Polygon Area Calculator
↓
Hash Table
↓
Tower of Hanoi Algorithm
A dictionary-based settings manager supporting add, update, delete, display, normalization, validation, and formatted output.
A class-based financial tracking application supporting deposits, withdrawals, transfers, balance checks, transaction ledgers, formatted reports, spending percentages, and text-based charts.
An inheritance-based geometry application featuring a reusable Rectangle parent class, a specialized Square subclass, area and perimeter calculations, diagonal calculation, method overriding, shape rendering, and object invariant preservation.
A data-structure implementation featuring a custom HashTable class, Unicode-based hashing with ord(), nested dictionary buckets, collision-safe storage, safe deletion, and lookup behavior.
The storage model is:
{
hashed_key: {
original_key: value
}
}This project made the internal mechanics of dictionary-style key-value lookup more explicit. It also clarified why a hash value alone is not enough: different original keys can produce the same hash, so the original key must still be stored and checked inside the bucket.
A recursive algorithm project that solves the classic Tower of Hanoi puzzle.
The project implements a function named hanoi_solver() that returns the complete sequence of puzzle states required to move all disks from the first rod to the third rod.
The project strengthened:
- Recursive decomposition
- Base-case design
- Helper functions
- Lists as stacks
- State-history recording
- Exact multi-line string formatting
- Minimum-move reasoning with
2^n - 1 - Debugging indentation and execution-order errors
The core recursive idea is:
Move n - 1 disks from source to auxiliary.
Move the largest remaining disk from source to target.
Move n - 1 disks from auxiliary to target.
Example starting state:
[3, 2, 1] [] []
Example final state:
[] [] [3, 2, 1]
This project made recursion more concrete by showing how a large problem can be solved by repeatedly solving smaller versions of the same problem.
- Variables and data types
- Assignment and arithmetic
- User input and output
- String formatting
- Multi-line output
- Naming conventions
- Indentation and syntax
- Type hints
- Docstrings
- Code comments
- Main execution guards
- Built-in functions such as
sum(),ord(),min(),len(),range(), andisinstance()
- Function definitions
- Positional and keyword arguments
- Default parameters
- Return values
- Reusable calculations
- Separation of responsibilities
- Guard clauses
- Early returns
- Small, testable units of logic
- Constructor-based dependency injection
- Strategy orchestration through dedicated engine classes
- Nested helper functions
- Method-level responsibilities for add, remove, lookup, validation, search behavior, and state recording
if,elif, andelse- Comparison and membership operators
- Boolean logic
- Nested conditions
- State-dependent behavior
- Boundary-condition handling
- Business-rule validation
- Controlled program flow
- Geometry-based condition handling
- Complete-fit calculations with floor division
- Safe mutation after existence checks
- Search-space reduction through boundary updates
- Recursive control flow through base cases and repeated subproblems
- Lists
- Dictionaries
- Nested dictionaries
- Lists of dictionaries
- Class-level dictionaries
- Membership tests
- Key lookup
- Data aggregation
- Filtering
- Summation
- Record-based modeling
- List comprehensions
- Mixed collections of related objects
- Filtering objects by exact class or subclass type
- Storing interchangeable strategy objects in a list
- Aggregating candidate numeric results
- Selecting an optimal result with
min() - Passing related objects into methods
- Comparing outer and inner object dimensions
- Storing two-dimensional movement vectors as tuples
- Recording position history in lists
- Extending movement sets with
list.extend() - Building custom node-based data structures
- Maintaining a linked-list
headreference - Connecting objects through
.nextreferences - Updating links during insertion and removal
- Maintaining a manual collection length
- Storing colliding hash keys in nested buckets
- Searching sorted collections efficiently
- Recording checked values during algorithm execution
- Recording recursive algorithm states after each operation
- Joining recorded states into exact multi-line output
- Creating square two-dimensional lists with nested comprehensions
- Converting adjacency-list dictionaries into adjacency matrices
- Mapping graph edges with row-column indexing
- Preserving directed and undirected connectivity information
- Using a list as a FIFO queue
- Removing the oldest state with
pop(0) - Appending valid successor states
- Unpacking queue states into synchronized variables
- Custom linked lists
- Nested
Nodeclasses - Node objects storing elements
- Reference-based object chains
- The
headreference - The
nextreference - Empty-list detection
- Appending nodes to the end of a linked list
- Removing the first matching node
- Bypassing removed nodes through reference reassignment
- Handling head-node removal
- Handling missing elements safely
- Hash tables
- Hash functions
- Unicode-based hashing with
ord() - Hash buckets
- Collision handling
- Key-value lookup
- Safe deletion
- Comparing custom data structures with built-in Python containers
- Rods represented as list stacks
- Stack-like operations with
pop()andappend() - Adjacency lists represented with dictionaries of neighbor lists
- Adjacency matrices represented with square nested lists
- Binary edge encoding with
0and1 - Sparse-to-dense graph representation conversion
- Explicit stacks for depth-first traversal
- Visited-state tracking for cycle prevention
- Recursive state construction for backtracking
- Set-based column and diagonal occupancy tracking
- Exact choose-explore-undo state restoration
- Binary search
- Merge sort
- Quicksort
- Selection sort
- Bisection method
- Luhn algorithm
- Tower of Hanoi
- Dijkstra's shortest-path algorithm
- Weighted graph traversal
- Adjacency-matrix processing
- Edge relaxation
- Path reconstruction
- Divide-and-conquer algorithms
- Recursive partitioning
- Recursive state generation
- Minimum-move recursive algorithms
- In-place sorting
- Checksum validation
- Numerical root finding
- Tolerance-based approximation
- Sorted input requirements
- Search boundaries
low,high, andmidvariables- Midpoint calculation with integer division
- Middle-value comparison
- Discarding half of the search range
- Early return when the target is found
- Not-found handling
- Search-path tracing
- Algorithmic reasoning about efficiency
- Recursive decomposition of lists
- Recursive decomposition of puzzle states
- Pivot selection and partitioning
- Partitioning values into less-than, equal-to, and greater-than groups
- Preserving duplicate values during sorting
- Merging sorted sublists
- Finding the minimum value in an unsorted portion
- Avoiding unnecessary swaps
- Iterative interval halving
- Convergence checks with tolerance
- Maximum-iteration safeguards
- Modulo-based validity checks
- Right-to-left checksum processing
- List-based stack operations with
pop()andappend() - Exact sequence generation for algorithmic puzzles
- Minimum-distance node selection
- Tracking visited and unvisited graph nodes
- Reconstructing shortest paths with nested lists
- Handling unreachable nodes with positive infinity
- Selecting one target node or all reachable nodes
- Determining graph size from adjacency-list keys
- Converting unweighted graph edges into matrix entries
- Printing matrix rows separately while returning the complete matrix
- Breadth-first search with FIFO queue behavior
- Tuple-based state-space representation
- Level-by-level state expansion
- Constraint-based pruning of invalid states
- Balanced-parentheses generation
- Depth-first search with explicit LIFO stacks
- Graph reachability from an arbitrary starting node
- Recursive backtracking for constraint-satisfaction problems
- N-Queens branch pruning with column and diagonal sets
- Mutable-state snapshots with
list.copy()
forloopswhileloopsrange()enumerate()- Nested loops
- Descending ranges
- Pattern generation
- Numbered output
- Iterative text construction
- Dynamic row and column generation
- String repetition for shape rendering
- Random selection with
random.choice() - Traversal until
None - Moving from one node to the next through object references
- Generator expressions such as
sum(ord(char) for char in string) - Generator expressions for path formatting such as
(str(node) for node in path) - Iterative narrowing of a search interval
- Recursive sorting of left and right partitions
- Copying remaining values after merge comparisons
- Partitioning lists into smaller groups
- Nested-loop minimum search for selection sort
- Iterating through reversed strings with
enumerate() - Processing alternating digits for checksum algorithms
- Recording recursive algorithm states after each operation
- Joining recorded states into exact multi-line output
- Creating square two-dimensional lists with nested comprehensions
- Converting adjacency-list dictionaries into adjacency matrices
- Mapping graph edges with row-column indexing
- Preserving directed and undirected connectivity information
- Using a list as a FIFO queue
- Removing the oldest state with
pop(0) - Appending valid successor states
- Unpacking queue states into synchronized variables
- String indexing
- String slicing
split()lower()capitalize()- Translation tables
- Caesar cipher logic
- Multi-line parsing
- Fixed-width formatting
- Alignment
- Truncation
- Exact whitespace control
- Removing dashes and spaces from structured identifiers
- Reversing strings with slicing
- Converting digit characters to integers for arithmetic validation
- Joining multiple recorded states with
"\n".join(...)
re.search()re.fullmatch()- Pattern matching
- Structured extraction
- Formatted-text validation
- Multi-line record processing
isinstance()- Multiple-type checks
hasattr()- Required-key validation
- Empty-string validation
- Range validation
- Sequence validation
- Object-state validation
- Invalid-record detection
- Minimum-value enforcement
- Duplicate-state prevention
- Downward-transition prevention
- Validation before dictionary deletion
- Defensive lookup of missing keys
- Boundary validation for index-based algorithms
- Checksum validation for identification numbers
- Normalizing formatted numeric strings before validation
- Fixed-order output validation for recursive algorithm states
tryandexceptraiseTypeErrorValueErrorAttributeErrorIndexErrorKeyErrorSyntaxError- Custom exception classes
- Inheriting from
Exception - Passing messages through
super().__init__() - Storing the invalid object inside an exception
- Catching multiple exception types
- Defensive programming
- Validation before mutation
- Explicit error messages
- Classes and instances
__init__()- Instance attributes
- Class attributes
- Instance methods
- Parent and child classes
- Inheritance and subclassing
- Reusing parent initialization with
super() - Method overriding
- Polymorphism
- Exact type checks with
type() is - Inheritance-aware checks with
isinstance() - Object composition
- Passing objects as arguments
- Object interaction
- Encapsulation
- Internal backing attributes
@property- Property setters
- Read-only properties
- Controlled state mutation
- Object invariants
__str__()__repr__()- Transaction-based object models
- State-aware validation
- Abstract base classes with
ABC - Required subclass interfaces with
@abstractmethod - Strategy-pattern implementations
- Runtime polymorphism through a shared contract
- Separation of algorithm selection from algorithm implementation
- Nested classes for implementation details
- Reference-based relationships between objects
- Object chains created through attributes
datetime.datetime.now()- Timestamp storage
strftime()- Date-time format codes
- Timestamp display in object representations
- Fixed-width columns
- Centered headings
- Left and right alignment
- Two-decimal-place formatting
- Vertical percentage axes
- Horizontal chart axes
- Character-based bars
- Vertical labels
- Precise newline handling
- Asterisk-based geometric rendering
- Shape dimensions represented through formatted strings
- Exact dictionary output expected by tests
- Structured tuple output for algorithm results
- Exact multi-line algorithm output
- Fixed-order rod-state formatting
- Reading tracebacks
- Syntax-error diagnosis
- Indentation-error correction
- Name-error diagnosis
- Missing-argument detection
- Attribute initialization issues
- Off-by-one detection
- Index-boundary debugging
- Dictionary-key debugging
- Exact-output test failures
- Automated test interpretation
- Edge-case testing
- Incremental correction
- Diagnosing parameter-name mismatches
- Distinguishing built-in functions from collection methods
- Debugging variable scope and execution-order problems
- Distinguishing tuple concatenation from coordinate arithmetic
- Diagnosing incomplete abstract subclass implementations
- Verifying inherited initialization and state
- Diagnosing misspelled method names
- Preserving subclass invariants after state changes
- Debugging exact newline requirements in generated pictures
- Debugging assignment direction in linked structures
- Checking
Nonewithis Noneandis not None - Avoiding infinite traversal loops
- Updating
headcorrectly when removing the first node - Preventing
KeyErrorby checking nested dictionary membership - Debugging hash collisions by checking the original key inside the bucket
- Debugging binary-search boundary updates
- Confirming whether a function return value is actually printed
- Debugging slice boundaries when splitting lists
- Debugging recursive base cases
- Debugging recursive helper functions
- Debugging indentation errors inside recursive functions
- Ensuring helper functions are defined before they are called
- Debugging merge indexes for left, right, and sorted positions
- Matching exact numerical-output messages in automated tests
- Distinguishing return values from printed output in numerical labs
- Distinguishing in-place sorting from new-list sorting
- Avoiding forbidden built-in sorting methods such as
.sort()andsorted()when required - Preserving input-list identity when a lab requires in-place mutation
- Preserving original input data when a lab requires returning a new list
- Handling duplicate values in partition-based sorting
- Debugging odd/even index logic in the Luhn algorithm
- Verifying exact string returns such as
VALID!andINVALID! - Distinguishing an empty string return from a complete formatted result
- Verifying fixed-order output when recursive parameter roles change
- Debugging incorrect function signatures and missing parameters
- Distinguishing an intermediate node count from the final matrix result
- Verifying row and column positions in nested lists
- Ensuring each adjacency-matrix row is printed separately
- Returning the completed matrix rather than a helper value
- Debugging incorrect queue conditions
- Distinguishing object identity from list truthiness
- Understanding that
append()accepts one object - Preventing invalid BFS branches through explicit constraints
- Separating returned results from demonstration output
- Debugging stack traversal with
pop()rather thanpop(0) - Preventing repeated DFS processing with visited-state checks
- Debugging recursive base cases and missing recursive calls
- Preserving symmetry between state reservation and cleanup
- Copying mutable recursive state before storing completed solutions
Implemented a breadth-first search solution that generates every valid combination of balanced parentheses for a requested number of pairs.
The completed workshop includes:
- A
gen_parentheses()function - Type and lower-bound validation
- A FIFO queue initialized with one empty state
- Tuple-based BFS state tracking
- Front-of-queue processing with
pop(0) - Opening- and closing-parenthesis constraints
- Completion detection through target string length
- A result list containing every valid combination
Core implementation:
def gen_parentheses(pairs):
if not isinstance(pairs, int):
return 'The number of pairs should be an integer'
if pairs < 1:
return 'The number of pairs should be at least 1'
queue = [('', 0, 0)]
result = []
while queue:
current, opens_used, closes_used = queue.pop(0)
if len(current) == 2 * pairs:
result.append(current)
else:
if opens_used < pairs:
queue.append(
(current + '(', opens_used + 1, closes_used)
)
if closes_used < opens_used:
queue.append(
(current + ')', opens_used, closes_used + 1)
)
return resultTraversal flow:
Initialize the queue with an empty state
↓
Remove the oldest queued state
↓
Check whether the state is complete
↓
Store complete states
↓
Generate valid successor states
↓
Append successors to the back of the queue
↓
Continue until the queue is empty
Example results:
gen_parentheses(2)
→ ['(())', '()()']
gen_parentheses(3)
→ ['((()))', '(()())', '(())()', '()(())', '()()()']
This workshop strengthened:
- Breadth-first search
- FIFO queue behavior
- State-space exploration
- Tuple unpacking
- Constraint-based branching
- Balanced-parentheses generation
- Depth-first search with explicit LIFO stacks
- Graph reachability from an arbitrary starting node
- Recursive backtracking for constraint-satisfaction problems
- N-Queens branch pruning with column and diagonal sets
- Mutable-state snapshots with
list.copy() - Validation before algorithm execution
Implemented a complete N-Queens solver using recursive depth-first search and backtracking.
The completed lab includes:
- A
dfs_n_queens()function - Guard handling for values below
1 - One queen placed per row
- Column-conflict tracking with a set
- Main-diagonal tracking with
row - column - Anti-diagonal tracking with
row + column - Branch pruning before recursive descent
- Exact state restoration after each recursive call
- Deterministic solution ordering through ascending column iteration
- Correct solution counts, including
92solutions for an 8×8 board
Core implementation:
def dfs_n_queens(n):
if n < 1:
return []
solutions = []
placement = []
columns = set()
main_diagonals = set()
anti_diagonals = set()
def backtrack(row):
if row == n:
solutions.append(placement.copy())
return
for column in range(n):
main_diagonal = row - column
anti_diagonal = row + column
if (
column in columns
or main_diagonal in main_diagonals
or anti_diagonal in anti_diagonals
):
continue
placement.append(column)
columns.add(column)
main_diagonals.add(main_diagonal)
anti_diagonals.add(anti_diagonal)
backtrack(row + 1)
placement.pop()
columns.remove(column)
main_diagonals.remove(main_diagonal)
anti_diagonals.remove(anti_diagonal)
backtrack(0)
return solutionsSearch invariant:
Choose a valid position
↓
Reserve its column and diagonals
↓
Explore the next row
↓
Undo every state change
↓
Try the next candidate
This lab strengthened recursive DFS, backtracking, constraint pruning, mutable-state snapshots, deterministic traversal, and reversible state transitions.
Implemented iterative depth-first search for an undirected graph represented by an adjacency matrix.
The completed lab includes:
- A
dfs()function - An explicit stack initialized with the starting node
- Last-In-First-Out traversal using
pop() - Visited-node tracking
- Cycle prevention
- Adjacency-matrix neighbor discovery
- Reachability analysis
- Correct handling of isolated nodes and disconnected graph components
Core implementation:
def dfs(undirected_adj_matrix, node_label):
stack = [node_label]
visited = []
while stack:
current = stack.pop()
if current not in visited:
visited.append(current)
for neighbor, connected in enumerate(
undirected_adj_matrix[current]
):
if connected == 1 and neighbor not in visited:
stack.append(neighbor)
return visitedTraversal flow:
Push the starting node
↓
Pop the most recently added node
↓
Record it if unvisited
↓
Push its unvisited neighbors
↓
Continue until the stack is empty
This lab strengthened explicit stack management, LIFO traversal, graph reachability, adjacency-matrix traversal, visited-state tracking, and cycle prevention.
Implemented a converter that transforms an unweighted graph from an adjacency-list representation into an adjacency matrix.
The completed lab includes:
- A function named
adjacency_list_to_matrix() - Automatic node-count detection with
len() - Square matrix construction using nested list comprehensions
- Edge conversion through dictionary and neighbor iteration
- Separate printing of every matrix row
- Returning the completed matrix
- Support for both directed and undirected graph descriptions
Core implementation:
def adjacency_list_to_matrix(adj_list):
number_of_nodes = len(adj_list)
matrix = [
[0 for _ in range(number_of_nodes)]
for _ in range(number_of_nodes)
]
for node, neighbors in adj_list.items():
for neighbor in neighbors:
matrix[node][neighbor] = 1
for row in matrix:
print(row)
return matrixConversion flow:
Read adjacency-list dictionary
↓
Determine the number of nodes
↓
Create an n × n zero matrix
↓
Visit every node and neighbor
↓
Set matrix[node][neighbor] to 1
↓
Print each row
↓
Return the completed matrix
Example output:
[0, 1, 1, 0]
[0, 0, 1, 0]
[1, 0, 0, 1]
[0, 0, 1, 0]
This lab strengthened:
- Graph representations
- Dictionaries and
.items() - Nested lists and comprehensions
- Nested iteration
- Matrix indexing
- Directed versus undirected edges
- Printing required output while returning reusable data
- Translating user stories into a complete implementation
Implemented Dijkstra's shortest-path algorithm for a weighted graph represented by an adjacency matrix.
The completed workshop includes:
- An
INFconstant representing unavailable direct connections - A two-dimensional adjacency matrix
- A
shortest_path()function - Distance initialization with positive infinity
- A
visitedlist for finalized nodes - A
pathslist for reconstructing complete routes - Selection of the nearest unvisited node
- Edge relaxation for reachable neighbors
- Optional output for one target node or every reachable node
- Generator expressions for converting node numbers to strings
- Readable route output with
" -> ".join(...)
Core implementation pattern:
def shortest_path(matrix, start_node, target_node=None):
n = len(matrix)
distances = [INF] * n
distances[start_node] = 0
paths = [[node_no] for node_no in range(n)]
visited = [False] * nMinimum-distance node selection:
for node_no in range(n):
if not visited[node_no] and distances[node_no] < min_distance:
min_distance = distances[node_no]
current = node_noRelaxation step:
new_distance = distances[current] + distance
if new_distance < distances[node_no]:
distances[node_no] = new_distance
paths[node_no] = paths[current] + [node_no]Algorithm flow:
Initialize distances and paths
↓
Select the nearest unvisited node
↓
Mark it as visited
↓
Inspect each reachable neighbor
↓
Calculate a candidate distance
↓
Update distance and path when shorter
↓
Repeat until no reachable node remains
Example result:
0-5 distance: 6
Path: 0 -> 2 -> 1 -> 5
This workshop strengthened:
- Weighted graph representation
- Adjacency matrices
- Dijkstra's algorithm
- Positive infinity as a sentinel value
- Boolean visited-state tracking
- Minimum-distance node selection
- Edge relaxation
- Path reconstruction
- Conditional expressions
- Generator expressions
- Optional function parameters
- Algorithm tracing and debugging
Implemented a recursive solver for the classic Tower of Hanoi puzzle.
The completed certification project includes:
- A
hanoi_solver()function - Three rods represented as Python lists
- Initial rod generation with
range() - A helper function for recording rod states
- A helper function for moving one disk
- A recursive
solve()function - Base-case handling for one disk
- Minimum-move solution using
2^n - 1moves - Exact multi-line string output required by automated tests
Core implementation pattern:
def hanoi_solver(number_of_disks):
rod_1 = list(range(number_of_disks, 0, -1))
rod_2 = []
rod_3 = []
moves = []
def record_state():
moves.append(f"{rod_1} {rod_2} {rod_3}")
def move_disk(source, target):
disk = source.pop()
target.append(disk)
record_state()Recursive flow:
Move n - 1 disks to the auxiliary rod
↓
Move the largest disk to the target rod
↓
Move n - 1 disks from the auxiliary rod to the target rod
Example:
[3, 2, 1] [] []
[3, 2] [] [1]
[3] [2] [1]
[3] [2, 1] []
[] [2, 1] [3]
[1] [2] [3]
[1] [] [3, 2]
[] [] [3, 2, 1]
This project strengthened:
- Recursion
- Base-case design
- Function nesting
- List stack behavior
- State recording
- Exact formatted output
- Mathematical reasoning about move counts
- Debugging recursive execution order
Implemented a recursive merge sort algorithm that sorts a list in ascending order by repeatedly splitting it into smaller parts and merging those parts back together in sorted order.
The completed workshop includes:
- A
merge_sort()function - A recursive base case for lists with zero or one element
- Middle-point calculation with integer division
- List slicing for left and right partitions
- Recursive sorting of both halves
- Index-based merging into the original list
- Copying leftover values after one half is exhausted
- A main execution guard for demonstration output
Core implementation pattern:
def merge_sort(array):
if len(array) <= 1:
return
middle_point = len(array) // 2
left_part = array[:middle_point]
right_part = array[middle_point:]
merge_sort(left_part)
merge_sort(right_part)
left_array_index = 0
right_array_index = 0
sorted_index = 0Merge flow:
Original list
↓
Split into left and right halves
↓
Recursively sort each half
↓
Compare the smallest remaining values
↓
Write the smaller value back into the original array
↓
Copy any remaining values
Example:
[4, 10, 6, 14, 2, 1, 8, 5]
↓
[1, 2, 4, 5, 6, 8, 10, 14]
This workshop strengthened:
- Recursion
- Base-case design
- Divide-and-conquer reasoning
- List slicing
- Index management
- In-place mutation
- Algorithm tracing
- Debugging merge loops
Implemented a recursive quicksort algorithm that returns a new sorted list without modifying the original input list.
The completed lab includes:
- A
quick_sort()function - A base case for empty and single-item lists
- Pivot selection using the first list element
- Partitioning into less-than, equal-to, and greater-than groups
- Recursive sorting of lower and higher partitions
- Correct handling of duplicate values
- Concatenation of sorted partitions into a new list
- Compliance with the requirement to avoid built-in sorting methods
Core implementation pattern:
def quick_sort(numbers):
if len(numbers) <= 1:
return numbers[:]
pivot = numbers[0]
less_than_pivot = []
equal_to_pivot = []
greater_than_pivot = []Quicksort flow:
Choose a pivot
↓
Partition values into less, equal, and greater groups
↓
Recursively sort the less and greater groups
↓
Concatenate sorted less + equal + sorted greater
↓
Return a new sorted list
This lab strengthened:
- Recursion
- Base-case design
- Pivot selection
- Partitioning logic
- Duplicate handling
- New-list construction
- Avoiding mutation when the specification requires preserving the input list
Implemented an in-place selection sort algorithm that repeatedly finds the minimum value in the unsorted portion of a list and swaps it into the current position.
The completed lab includes:
- A
selection_sort()function - In-place mutation of the input list
- A current-position loop
- A nested minimum-search loop
- Index tracking for the smallest remaining value
- Conditional swapping only when needed
- Returning the same list object after sorting
- Compliance with the requirement to avoid
sort()andsorted()
Core implementation pattern:
def selection_sort(array):
for current_index in range(len(array)):
minimum_index = current_index
for search_index in range(current_index + 1, len(array)):
if array[search_index] < array[minimum_index]:
minimum_index = search_indexSelection sort flow:
Start at the first unsorted position
↓
Find the smallest value in the remaining list
↓
Swap it into the current position if needed
↓
Move to the next position
↓
Repeat until the list is sorted
This lab strengthened:
- In-place sorting
- Nested-loop reasoning
- Index management
- Controlled swaps
- Avoiding unnecessary mutation
- Understanding quadratic-time sorting behavior
Implemented a card-number validator using the Luhn checksum algorithm.
The completed lab includes:
- A
verify_card_number()function - Removal of spaces and dashes from formatted card numbers
- Right-to-left digit processing
- Alternating-digit doubling
- Reducing doubled values greater than 9 by subtracting 9
- Checksum accumulation
- Modulo-10 validity checking
- Exact string returns of
VALID!andINVALID!
Core implementation pattern:
def verify_card_number(numbers):
cleaned_number = numbers.replace("-", "")
cleaned_number = cleaned_number.replace(" ", "")
total = 0
reversed_number = cleaned_number[::-1]Luhn validation flow:
Normalize the card number
↓
Reverse the digits
↓
Double every other digit after the check digit
↓
Subtract 9 from doubled values greater than 9
↓
Sum all processed digits
↓
Return VALID! when the total is divisible by 10
This lab strengthened:
- Checksum validation
- String normalization
- String slicing
enumerate()with index-based rules- Numeric conversion with
int() - Modulo checks
- Exact return-value matching
Implemented a numerical root-finding function that approximates the square root of a non-negative number by repeatedly halving an interval.
The completed lab includes:
- A
square_root_bisection()function - Default
toleranceandmax_iterationsparameters - Validation for negative inputs
- Exact handling for 0 and 1
- Lower and upper interval bounds
- Iterative midpoint calculation
- Bound updates based on midpoint squared
- Tolerance-based stopping
- Non-convergence reporting
Core implementation pattern:
def square_root_bisection(square_target, tolerance=1e-7, max_iterations=100):
if square_target < 0:
raise ValueError(
"Square root of negative number is not defined in real numbers"
)
lower_bound = 0
upper_bound = square_target if square_target > 1 else 1
for _ in range(max_iterations):
root = (lower_bound + upper_bound) / 2
root_squared = root * rootBisection flow:
Start with an interval
↓
Calculate the midpoint
↓
Square the midpoint
↓
Compare with the target number
↓
Keep the half that contains the square root
↓
Stop when the interval is within tolerance
This lab strengthened:
- Numerical approximation
- Interval halving
- Tolerance-based stopping
- Maximum-iteration safeguards
- Boundary-condition handling
- Exact
print()output matching - Difference between returning a value and printing a message
Implemented an iterative binary search algorithm for sorted lists.
The completed workshop includes:
- A
binary_search()function - A sorted input list
- A target value
lowandhighsearch boundariesmidmidpoint calculation- Middle-value comparison
- Path tracing through
path_to_target - Early return when the target is found
- Not-found handling when the search range becomes invalid
Core implementation:
def binary_search(search_list, value):
path_to_target = []
low = 0
high = len(search_list) - 1
while low <= high:
mid = (low + high) // 2
value_at_middle = search_list[mid]
path_to_target.append(value_at_middle)
if value == value_at_middle:
return path_to_target, f'Value found at index {mid}'
elif value > value_at_middle:
low = mid + 1
else:
high = mid - 1
return [], 'Value not found'Search flow:
Sorted list
↓
Set low and high boundaries
↓
Calculate middle index
↓
Compare target with middle value
↓
Discard half of the search range
↓
Repeat until found or range is empty
Example output:
([3], 'Value found at index 2')
([3, 5, 4], 'Value found at index 3')
([], 'Value not found')
This workshop strengthened:
- Algorithmic thinking
- Search boundaries
- Integer division
- Index management
- Conditional branching
- Iterative search
- Search-path tracing
- Debugging off-by-one and direction errors
Implemented a simplified hash table that stores key-value pairs using computed hash values and nested buckets.
The completed certification project includes:
HashTableclasscollectiondictionaryhash()methodadd()methodremove()methodlookup()method- Collision-safe nested dictionaries
- Safe handling of missing keys
- Exact collection structures required by tests
This project strengthened hash-table concepts, Unicode-based hashing, nested dictionaries, collision handling, safe deletion, lookup design, defensive programming, and exact dictionary-structure matching.
Implemented a custom linked list using a nested Node class and object references.
The completed workshop includes:
- A
LinkedListclass - A nested
Nodeclass - A
lengthcounter - A
headreference - An
is_empty()method - An
add()method - A
remove()method - Node traversal through
.next - Safe handling of missing elements
- Removal from the head of the list
- Removal from the middle or end of the list
Class relationship:
LinkedList
├── length = 2
└── head ──▶ Node(element=1)
└── next ──▶ Node(element=2)
└── next ──▶ None
This workshop strengthened reference-based thinking, traversal with while loops, and safe link updates.
Implemented a strategy-driven pricing engine that evaluates multiple discount algorithms and returns the lowest valid price.
The completed workshop includes:
- A
Productmodel - An abstract
DiscountStrategyinterface PercentageDiscountFixedAmountDiscountPremiumUserDiscount- A
DiscountEngineresponsible for evaluating strategies - Type hints for products, prices, user tiers, and strategy collections
- Currency output formatted to two decimal places
This workshop strengthened abstract interface design, runtime polymorphism, strategy-pattern architecture, and exact monetary formatting.
Implemented an inheritance-based geometry application with a reusable Rectangle parent class and a specialized Square subclass.
The completed certification project includes area calculation, perimeter calculation, diagonal calculation, shape rendering, shape containment, method overriding, and object invariant preservation.
Developed a reusable Category class with transaction ledgers, deposits, withdrawals, transfers, balance calculations, fund checks, fixed-width output, spending percentages, and vertical text-based charts.
Object-oriented, data-structure, and algorithmic concepts were introduced incrementally:
MusicalInstrument
↓
One class with attributes and methods
↓
Planet
↓
Validation, exceptions, and __str__()
↓
Email + User + Inbox
↓
Object composition and interaction
↓
Employee
↓
Properties, setters, and coordinated attributes
↓
GameCharacter
↓
Read-only properties and clamped state
↓
Player + Pawn
↓
Abstract inheritance, shared movement logic, and required subclass behavior
↓
Movie + TVSeries + MediaCatalogue
↓
Inheritance, polymorphism, filtering, and custom exceptions
↓
DiscountStrategy + DiscountEngine
↓
Abstract interfaces, strategy objects, and best-result selection
↓
LinkedList + Node
↓
Reference-based data structures, traversal, and link updates
↓
HashTable
↓
Hashing, buckets, collision handling, safe lookup, and deletion
↓
Binary Search
↓
Sorted data, midpoint comparison, range reduction, and algorithm tracing
↓
Merge Sort
↓
Recursive splitting, sorted merging, and divide-and-conquer design
↓
Shortest Path Algorithm
↓
Weighted graphs, Dijkstra's algorithm, edge relaxation, and path reconstruction
↓
Breadth-First Search Algorithm
↓
FIFO queues, state-space exploration, and valid-combination generation
↓
Quick Sort
↓
Pivot partitioning, recursive sorting, and new-list construction
↓
Selection Sort
↓
In-place minimum selection, controlled swaps, and nested-loop sorting
↓
Luhn Algorithm
↓
Checksum validation, formatted-number normalization, and modulo checks
↓
Adjacency List to Matrix Converter
↓
Graph representation conversion, nested lists, and matrix indexing
↓
Depth-First Search Algorithm
↓
Explicit stacks, LIFO traversal, reachability, and cycle prevention
↓
N-Queens Algorithm
↓
Recursive DFS, backtracking, constraint pruning, and state restoration
↓
Bisection Method
↓
Interval halving, tolerance-based approximation, and convergence checks
↓
Rectangle + Square
↓
Reusable geometry, method overriding, and subclass invariants
↓
Category
↓
Ledgers, transfers, reporting, and visualization
↓
Tower of Hanoi Algorithm
↓
Recursive decomposition, list stacks, state recording, and exact sequence generation
| Stage | New Capability |
|---|---|
| MusicalInstrument | Basic class construction |
| Planet | Validation and readable representation |
| Email Simulator | Multiple interacting classes |
| Salary Tracker | Encapsulation and controlled state transitions |
| Game Character Tracker | Read-only properties and bounded attributes |
| Player Interface | Abstract inheritance, shared behavior, movement vectors, and path tracking |
| Media Catalogue | Inheritance, polymorphism, custom exceptions, and collection filtering |
| Discount Calculator | Abstract interfaces, strategy pattern, dependency injection, and runtime polymorphism |
| Linked List | Custom node objects, references, traversal, insertion, and removal |
| Hash Table | Hashing, nested dictionaries, collision handling, lookup, and deletion |
| Binary Search | Sorted data, midpoint comparison, boundary updates, and logarithmic search thinking |
| Merge Sort | Recursive splitting, sorted merging, divide-and-conquer reasoning, and in-place mutation |
| Shortest Path Algorithm | Weighted graphs, adjacency matrices, Dijkstra's algorithm, relaxation, and route reconstruction |
| Breadth-First Search Algorithm | FIFO queues, state-space exploration, constrained branching, and combination generation |
| Quicksort | Pivot partitioning, recursive sorting, duplicate handling, and new-list construction |
| Selection Sort | In-place minimum selection, nested-loop scanning, and controlled swaps |
| Luhn Algorithm | Checksum validation, formatted-number cleaning, alternating-digit processing, and modulo checks |
| Adjacency List to Matrix Converter | Graph representation conversion, nested lists, matrix construction, and edge mapping |
| Depth-First Search Algorithm | Explicit stacks, LIFO traversal, graph reachability, and cycle prevention |
| N-Queens Algorithm | Recursive DFS, backtracking, constraint pruning, and exact state restoration |
| Bisection Method | Interval halving, numerical approximation, convergence, and tolerance-based stopping |
| Polygon Area Calculator | Reusable geometry, method overriding, object invariants, and containment logic |
| Budget App | Transaction systems, cross-object transfers, reporting |
| Tower of Hanoi Algorithm | Recursive decomposition, list stacks, state recording, exact multi-line output, and minimum-move reasoning |
This progression establishes a foundation for maintainable engineering software. The linked-list and hash-table projects add lower-level data-structure thinking, binary search introduces algorithmic efficiency and systematic range reduction, the shortest-path workshop adds weighted graph processing and route optimization, the breadth-first search workshop adds FIFO traversal and constrained state-space exploration, merge sort and quicksort add divide-and-conquer sorting, selection sort clarifies in-place quadratic sorting, the Luhn algorithm introduces checksum validation, the adjacency converter adds graph-representation transformation and matrix construction, the depth-first search lab adds explicit LIFO graph traversal and reachability analysis, the N-Queens lab adds recursive backtracking and constraint-state management, the bisection method introduces numerical approximation through interval halving, and the Tower of Hanoi project makes recursive state generation more concrete.
Each project follows a repeatable implementation process.
- Read the full specification.
- Identify required classes, functions, inputs, outputs, and constraints.
- Convert user stories into implementation tasks.
- Build the smallest working version.
- Run automated tests.
- Inspect the first failing test.
- Identify the exact technical cause.
- Apply a focused correction.
- Test boundary values and invalid inputs.
- Refactor for readability.
- Preserve required behavior while improving readability.
- Add comments and README documentation.
- Commit the completed project.
Requirement
↓
Implementation
↓
Automated test
↓
Failure analysis
↓
Focused correction
↓
Regression check
- Prefer descriptive names.
- Keep indentation and formatting consistent.
- Separate data validation from presentation.
- Return reusable values instead of printing unnecessarily.
- Preserve valid object state after every public operation.
- Reuse property setters when appropriate.
- Avoid duplicate logic.
- Match required output exactly.
- Add comments that explain intent, not obvious syntax.
- Refactor only after the behavior is correct.
- Preserve the original workshop or project contract when documenting completed code.
- Prefer interfaces that allow new behavior without modifying existing orchestration logic.
- Keep shared behavior in parent classes and subclass-specific behavior in concrete classes.
- Use abstract methods when every subclass must provide a required operation.
- Validate keys before accessing or deleting nested dictionary values.
- Maintain algorithm boundaries carefully when working with index-based search.
- Respect whether a specification requires mutating the original list or returning a new list.
- Avoid built-in shortcuts when an exercise is designed to teach the underlying algorithm.
- Keep recursive base cases explicit.
- Record algorithm states only after meaningful state changes.
- Preserve fixed output order even when helper-function roles change.
| Error | Typical Cause | Practical Lesson |
|---|---|---|
SyntaxError |
Invalid syntax or copied instruction text | Check whether the failing line is executable Python |
IndentationError |
Incorrect block alignment | Verify class, method, branch, and recursive-helper indentation |
NameError |
Undefined variable or function | Check scope, spelling, and whether helper functions are defined before use |
TypeError |
Wrong argument count or data type | Compare the call with the function signature |
AttributeError |
Attribute or method does not exist | Review constructor, method spelling, and initialization |
ValueError |
Valid type but invalid content | Separate type validation from value validation |
IndexError |
Invalid sequence position | Review zero-based indexing and boundaries |
KeyError |
Missing dictionary key | Validate keys before access or deletion |
| Test mismatch | Exact output differs | Inspect whitespace, punctuation, newlines, dictionary structure, and output order |
| Logic failure | Conditions overlap or execute incorrectly | Use focused condition checks and test one behavior at a time |
- A small whitespace difference can fail an exact-output test.
- Two independent
ifstatements are not equivalent toif/elif/else. - Initialization order matters when setters depend on existing attributes.
- Constructors must be named exactly
__init__(). - Instance methods require
selfas their first parameter. - A method must explicitly
returna value when its result is needed. isinstance()includes subclasses, whiletype() ischecks the exact class.- Custom exceptions must receive every argument declared by their constructor.
- Automated tests often check both behavior and implementation structure.
- Parameter names must match the identifiers used inside the method body.
- Built-in functions such as
min(prices)are different from collection methods. - Variables must be defined before they are passed into constructors.
- Abstract subclasses must implement every required abstract method before they can be instantiated.
- Tests for parent-class behavior may rely on a concrete subclass being instantiable.
- Shared initialization should be reused through
super().__init__()when required. - Method names must match the specification exactly;
set_heigthandset_heightare different identifiers. - Floor division is appropriate when only complete contained objects should be counted.
- Generated text pictures may require a newline after the final row.
- Assignment direction matters in reference-based structures:
self.head = nodeandnode = self.headdo not mean the same thing. Noneshould be checked withis Noneoris not None.- Traversal loops must advance to the next node to avoid infinite loops.
- Removing the head node requires updating
self.head. - A hash table should store original keys inside buckets because different keys can share one hash value.
remove()should delete only the requested key-value pair, not the entire collision bucket.lookup()must check both the hash bucket and the original key.- Binary search requires sorted input data.
- The midpoint should be calculated with integer division:
(low + high) // 2. - If the target is greater than the middle value, update the lower boundary with
low = mid + 1. - If the target is less than the middle value, update the upper boundary with
high = mid - 1. - A function call must be inside
print(...)when the returned value needs to be displayed. - Returning the checked path is useful for explaining and debugging the search process.
- Merge sort requires a base case; otherwise recursion will never stop.
- Slice syntax such as
array[:middle_point]andarray[middle_point:]is different from theslice()constructor. - Merge logic requires separate indexes for the left half, right half, and original array.
- Remaining values from either half must be copied after the main merge loop finishes.
- Bisection works by shrinking an interval, not by guessing the exact answer immediately.
- For numbers between 0 and 1, the square root is larger than the original number, so the upper bound should be 1.
- Exact punctuation in
print()output can decide whether an automated test passes. - Quicksort should preserve duplicate values by keeping an equal-to-pivot partition.
- A recursive sorting function needs a base case for empty and single-item lists.
- Selection sort should only swap when the minimum value is not already in the current position.
- In-place sorting requirements mean the same list object must be modified and returned.
- Some labs require returning a new list instead of modifying the input list.
- The Luhn algorithm starts from the right and excludes the rightmost check digit from doubling.
- Removing spaces and dashes before checksum validation prevents formatted card numbers from failing incorrectly.
- A function that returns an empty string can pass a return-type test while still failing all behavior tests.
- Recursive calls must be indented inside the recursive helper function.
- Helper functions such as
move_disk()must exist before they are called. - The Tower of Hanoi output must always display physical rods in fixed order, even when recursive parameters change roles.
- Recording only the initial state is not enough; every move must be recorded after it happens.
- A two-dimensional list requires outer square brackets when the specification requires a list rather than a tuple.
- An unvisited-node condition uses
not visited[node_no]. - Optional values should be checked with
is not None. - A conditional expression requires the full
value_if_true if condition else value_if_falsestructure. - A reconstructed path must extend the current path with
[node_no]; adding integer node indexes does not create a route. - A generator expression converts each node individually:
(str(node) for node in paths[node_no]). - Values inside f-strings require braces, such as
{distances[node_no]}and{path}. - Dijkstra's algorithm requires non-negative edge weights.
INFis a sentinel for an unavailable edge or unreachable node, not an ordinary large weight.- A graph-conversion function must return the complete matrix, not only the number of nodes.
- Matrix rows represent source nodes and columns represent destination nodes.
- Nested list comprehensions should create independent rows.
- Directed edges must be preserved exactly as written in the adjacency list.
- Printing each matrix row is distinct from printing the whole matrix on one line.
while queue:is the correct condition for processing a non-empty queue.queue is not []is incorrect becauseischecks object identity.queue.pop(0)removes and returns the oldest queued state.- A BFS state should be appended as one tuple because
append()accepts one object. - Opening-parenthesis states are valid only while
opens_used < pairs. - Closing-parenthesis states are valid only while
closes_used < opens_used. - DFS uses
stack.pop()to process the most recently added node. - A visited-state check prevents graph cycles from causing repeated traversal.
- In backtracking, every state change made before recursion must be undone afterward.
- Completed recursive solutions must store
placement.copy(), not the mutable working list. - N-Queens diagonal conflicts are identified by
row - columnandrow + column.
This repository is the programming foundation for future coastal and environmental engineering software.
- Hydrodynamic model pre-processing
- Salinity-intrusion analysis
- Water-level and tide processing
- Environmental dataset validation
- Numerical-model result analysis
- Calibration and validation metrics
- Scientific visualization
- Engineering automation
- Sorting and indexing engineering records
- Checksum-style validation for structured identifiers
- Numerical root finding for engineering calculations
- Recursive workflow processing
- Research reproducibility
- Graph-based route and network analysis
- Minimum-cost path computation through connected systems
- Graph representation conversion for engineering networks
- Connectivity-matrix construction
- Breadth-first traversal of unweighted networks
- Depth-first traversal of connected engineering networks
- Reachability and connectivity analysis
- Constraint-based arrangement and scheduling search
- Backtracking for discrete engineering design spaces
- AI-assisted technical workflows
coastal-engineering-python/
├── tide-data-analysis/
├── water-level-processing/
├── salinity-analysis/
├── hydrodynamic-result-processing/
├── model-validation-metrics/
├── environmental-data-cleaning/
├── numerical-methods/
└── scientific-visualization/
class MonitoringStation:
pass
class Observation:
pass
class WaterLevelObservation(Observation):
pass
class SalinityObservation(Observation):
pass
class SimulationScenario:
pass
class HydrodynamicModel:
pass
class ResultIndex:
pass
class RecursiveWorkflow:
passA property-based validation pattern can protect engineering parameters:
class MonitoringStation:
def __init__(self, station_id):
self._station_id = station_id
self._salinity = 0.0
@property
def salinity(self):
return self._salinity
@salinity.setter
def salinity(self, value):
if not isinstance(value, (int, float)):
raise TypeError("'salinity' must be numeric.")
if value < 0:
raise ValueError("'salinity' cannot be negative.")
self._salinity = valueThe Strategy pattern from the Discount Calculator maps naturally to engineering software. Different calibration metrics, numerical solvers, boundary-condition treatments, or model-selection rules can implement a shared interface and be evaluated by one orchestration engine.
class ValidationMetric(ABC):
@abstractmethod
def calculate(self, observed, simulated):
passThe Linked List workshop maps to workflows where data or operations form a chain.
class ProcessingStepNode:
def __init__(self, step):
self.step = step
self.next = NoneExample workflow:
Read raw data
↓
Clean missing values
↓
Interpolate time series
↓
Compare with model output
↓
Generate validation metrics
The Hash Table project maps to engineering tasks that require fast key-based access.
Possible uses include:
- Station ID lookup
- Scenario-name indexing
- Model-result indexing
- Parameter-set lookup
- Cached validation metrics
- Fast retrieval of processed observations
- Duplicate detection in environmental records
The same collision-handling principle applies: computed identifiers are useful for indexing, but original keys must still be preserved and checked.
Binary search maps directly to sorted engineering datasets such as timestamps, station lists, ordered result files, and model-output time steps.
def find_time_index(times, target_time):
low = 0
high = len(times) - 1
while low <= high:
mid = (low + high) // 2
if times[mid] == target_time:
return mid
elif target_time > times[mid]:
low = mid + 1
else:
high = mid - 1
return NonePossible uses include:
- Searching sorted observation timestamps
- Locating model-output time steps
- Matching calibration windows
- Finding ordered scenario identifiers
- Reducing lookup time before interpolation
- Supporting efficient time-series processing
The key engineering lesson is that algorithm choice matters. When data is sorted, binary search can avoid checking every element one by one. When data is unsorted, sorting algorithms such as merge sort can prepare records for efficient search, comparison, reporting, or time-series analysis.
Adjacency lists and adjacency matrices are both useful for representing engineering connectivity.
Possible uses include:
- Representing links between monitoring stations
- Describing river, estuary, or drainage-network topology
- Building computational-mesh connectivity maps
- Converting sparse connection definitions into dense matrix form
- Preparing graph inputs for shortest-path and network-analysis algorithms
- Supporting connectivity diagnostics in preprocessing workflows
Conceptual example:
station_connections = {
0: [1, 2],
1: [2],
2: [0, 3],
3: [2],
}
connection_matrix = adjacency_list_to_matrix(station_connections)The adjacency list is compact for sparse networks, while the adjacency matrix provides direct row-column lookup and can support later matrix-based analysis.
Shortest-path algorithms map naturally to connected engineering systems in which links carry distance, time, cost, resistance, or another non-negative weight.
Possible uses include:
- Finding the lowest-cost route between monitoring stations
- Tracing minimum-distance paths through observation networks
- Selecting efficient inspection routes between coastal structures
- Representing river, estuary, drainage, or channel connections as weighted graphs
- Finding minimum-cost data-transfer routes between computational nodes
- Supporting graph-based mesh or network preprocessing
- Comparing alternative routes through coastal and environmental systems
- Modeling travel time or hydraulic resistance as edge weights
Conceptual example:
def find_station_route(network_matrix, start_station, target_station):
return shortest_path(
network_matrix,
start_station,
target_station,
)The core engineering lesson is that a network can be represented explicitly as nodes, links, and weights. Dijkstra's algorithm then identifies the route with the minimum accumulated cost when all weights are non-negative.
Breadth-first search is useful for unweighted engineering networks, layered connectivity checks, and discrete state exploration.
Possible uses include:
- Finding the minimum number of links between monitoring stations
- Traversing computational cells by connectivity level
- Exploring river or drainage networks one layer at a time
- Detecting all nodes reachable from a starting station
- Building unweighted shortest-path utilities
- Validating network connectivity before numerical analysis
Conceptual example:
def reachable_stations(network, start_station):
queue = [start_station]
visited = {start_station}
while queue:
current = queue.pop(0)
for neighbor in network[current]:
if neighbor not in visited:
visited.add(neighbor)
queue.append(neighbor)
return visitedBFS explores all nodes at one depth before moving to the next, which makes it suitable for unweighted shortest paths, reachability checks, and level-by-level processing.
Merge sort is useful when engineering records need to be ordered before later analysis.
Possible uses include:
- Sorting observation timestamps
- Ordering station records
- Sorting scenario results before comparison
- Preparing data for binary search
- Organizing model-output records before reporting
- Building reproducible pre-processing pipelines
Conceptual example:
def sort_observations_by_time(observations):
# Future implementation could sort observation records
# before validation, interpolation, or model comparison.
passThe quicksort and selection sort labs reinforce two different approaches to ordering data.
Quicksort is useful for understanding recursive partitioning and new-list construction, while selection sort is useful for understanding in-place mutation and controlled swaps.
Possible engineering uses include:
- Ordering measurement values before statistical analysis
- Sorting station metadata for reporting
- Preparing records for search or comparison
- Understanding when mutation is acceptable and when original data should be preserved
- Building intuition about algorithmic trade-offs
Conceptual example:
def sort_station_records(records):
# Future implementation could sort stations by ID,
# location, timestamp, or measured variable.
passThe Luhn algorithm introduces checksum-style validation, which is useful whenever an identifier should be checked for transcription or formatting errors.
Possible engineering-related uses include:
- Validating structured station codes
- Checking imported equipment identifiers
- Detecting simple data-entry mistakes
- Normalizing formatted identifiers before validation
- Designing lightweight integrity checks before data processing
Conceptual example:
def validate_station_code_checksum(station_code):
# Future implementation could apply a checksum rule
# to structured engineering identifiers.
passThe bisection method maps directly to engineering calculations where a value must be found iteratively within a specified tolerance.
Possible uses include:
- Solving simple nonlinear equations
- Finding water-level thresholds
- Estimating parameter values from target criteria
- Locating roots in calibration equations
- Supporting introductory numerical-method utilities
- Demonstrating convergence and stopping criteria
Conceptual example:
def find_parameter_root(function, lower_bound, upper_bound, tolerance):
# Future implementation could use interval halving
# to solve engineering equations numerically.
passThe Tower of Hanoi project strengthens recursive decomposition, which is useful when a large technical problem can be divided into smaller problems of the same type.
Possible engineering-related uses include:
- Recursive processing of nested model folders
- Recursive traversal of file trees
- Recursive subdivision of computational domains
- Recursive refinement of search intervals
- Recursive state tracking in algorithm demonstrations
- Recording intermediate states during numerical procedures
Conceptual example:
def process_model_directory(directory):
# Future implementation could recursively process
# nested folders containing model inputs, outputs, and reports.
passThe key lesson is that recursion is appropriate when the problem structure repeats at smaller scales.
- Continue the freeCodeCamp Python Certification
- Complete additional workshops, labs, and certification projects
- Deepen object-oriented programming skills
- Strengthen inheritance and polymorphism
- Improve custom exception design
- Strengthen abstract interface design
- Practice reusable class hierarchies and parent-child contracts
- Strengthen object invariants and method overriding
- Apply design patterns to larger applications
- Practice automated testing
- Strengthen custom data-structure understanding
- Practice linked-list traversal and reference updates
- Practice hash-table lookup, deletion, and collision handling
- Practice binary search and search-boundary management
- Practice recursive divide-and-conquer sorting
- Practice weighted graph representation with adjacency matrices
- Practice shortest-path computation with Dijkstra's algorithm
- Practice edge relaxation and path reconstruction
- Practice adjacency-list and adjacency-matrix representations
- Practice graph conversion with nested list comprehensions
- Practice row-column indexing for connectivity data
- Practice breadth-first search
- Practice FIFO queue behavior
- Practice tuple-based state representation
- Practice constrained successor generation
- Practice depth-first search
- Practice explicit stack and LIFO behavior
- Practice visited-state tracking and cycle prevention
- Practice recursive backtracking
- Practice constraint pruning and exact state restoration
- Practice recursive algorithms and state tracking
- Practice in-place sorting and controlled mutation
- Practice checksum validation and formatted-identifier normalization
- Practice numerical root finding and convergence checks
- Strengthen documentation quality
| Technology or Concept | Intended Application |
|---|---|
| NumPy | Arrays, vectorized calculations, numerical workflows |
| pandas | Tabular and time-series data |
| Matplotlib | Scientific and engineering visualization |
| SciPy | Numerical methods and scientific algorithms |
| pytest | Automated testing |
| Jupyter | Research notebooks and exploratory analysis |
| openpyxl | Automated Excel processing |
| Git | Version control and project management |
| Search Algorithms | Efficient retrieval from sorted data |
| Graph Algorithms | Weighted networks, route optimization, shortest paths, representation conversion, breadth-first traversal, and depth-first reachability |
| Backtracking Algorithms | Constraint solving, recursive search, branch pruning, and reversible state transitions |
| Sorting Algorithms | Preparing ordered data for search, reporting, and analysis |
| Checksum Validation | Detecting simple errors in structured identifiers |
| Numerical Methods | Root finding, convergence checks, and tolerance-based calculations |
| Recursive Algorithms | File traversal, domain subdivision, nested workflows, and state tracking |
| Algorithmic Complexity | Choosing appropriate algorithms for data size |
Python Fundamentals
↓
Functions and Program Logic
↓
Validation and Data Processing
↓
Object-Oriented Programming
↓
Object Composition
↓
Encapsulation and Validated State
↓
Inheritance, Polymorphism, and Custom Exceptions
↓
Abstract Interfaces and Strategy-Based Design
↓
Reusable Class Hierarchies and Movement Models
↓
Custom Data Structures and Linked References
↓
Hash-Based Storage and Lookup
↓
Search Algorithms and Algorithmic Efficiency
↓
Graph Algorithms and Shortest-Path Computation
↓
Graph Representation Conversion and Connectivity Matrices
↓
Breadth-First Search and Unweighted Network Traversal
↓
Depth-First Search and Graph Reachability
↓
Backtracking and Constraint Solving
↓
Sorting Algorithms, Partitioning, and In-Place Mutation
↓
Checksum Validation and Data Integrity Checks
↓
Numerical Root Finding and Tolerance-Based Approximation
↓
Recursive Algorithms and State Tracking
↓
Geometric Models and Object Invariants
↓
Testing and Documentation
↓
NumPy and pandas
↓
Scientific Visualization
↓
Numerical Methods
↓
Hydrodynamic and Salinity Analysis
↓
Research Automation
↓
AI-Assisted Engineering Workflows
This repository is actively maintained as part of an ongoing learning process.
Workshops: 17
Labs: 15
Certification Projects: 5
Total Projects: 37
Latest completed workshop:
Implement the Breadth-First Search Algorithm
Latest completed lab:
Implement the N-Queens Algorithm
Latest completed certification project:
Implement the Tower of Hanoi Algorithm
Automated tests: 8/8 passed
A dedicated theory and exam-preparation guide is available in
PYTHON_REVIEW.md.
The guide consolidates the concepts covered across the certification and this repository, including:
- Python syntax, types, mutability, strings, collections, and control flow
- Functions, scope, modules, exceptions, and file handling
- Object-oriented programming, inheritance, abstraction, and polymorphism
- Linked lists, hash tables, stacks, queues, graphs, trees, and heaps
- Searching, sorting, BFS, DFS, Dijkstra, recursion, and backtracking
- Dynamic programming, numerical methods, algorithmic complexity, and exam traps
- A direct project-to-concept map based on the completed portfolio
The guide is independently written and does not reproduce the freeCodeCamp review page verbatim.
- freeCodeCamp
- Python Documentation
- NumPy Documentation
- pandas Documentation
- Matplotlib Documentation
- SciPy Documentation
- pytest Documentation
The workshop, lab, and certification-project requirements are provided through the freeCodeCamp Python Certification.
The implementations, comments, documentation, repository organization, and engineering-oriented extensions reflect my independent learning process and technical portfolio development.
Duong Kim Cuong
Coastal Engineering · Scientific Computing · Python · Artificial Intelligence
GitHub: github.com/kcduong994