Skip to content

Latest commit

 

History

128 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

freecodecamp-python

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.

Python freeCodeCamp Projects Workshops Labs Certification Projects Status

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.


Table of Contents


Repository at a Glance

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

Current Learning Stage

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

Learning Progress

Workshops

# 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

Labs

# 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

Certification Projects

# 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

Repository Structure

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

Organization Principles

  • 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

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

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

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.

Completed Certification Sequence

User Configuration Manager
        ↓
Budget App
        ↓
Polygon Area Calculator
        ↓
Hash Table
        ↓
Tower of Hanoi Algorithm

Build a User Configuration Manager

A dictionary-based settings manager supporting add, update, delete, display, normalization, validation, and formatted output.

Build a Budget App

A class-based financial tracking application supporting deposits, withdrawals, transfers, balance checks, transaction ledgers, formatted reports, spending percentages, and text-based charts.

Build a Polygon Area Calculator

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.

Build a Hash Table

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.

Implement the Tower of Hanoi Algorithm

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.


Technical Competencies

Python Fundamentals

  • 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(), and isinstance()

Functions and Program Design

  • 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

Programming Logic

  • if, elif, and else
  • 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

Collections and Structured Data

  • 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 head reference
  • Connecting objects through .next references
  • 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

Data Structures

  • Custom linked lists
  • Nested Node classes
  • Node objects storing elements
  • Reference-based object chains
  • The head reference
  • The next reference
  • 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() and append()
  • Adjacency lists represented with dictionaries of neighbor lists
  • Adjacency matrices represented with square nested lists
  • Binary edge encoding with 0 and 1
  • 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

Algorithms

  • 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, and mid variables
  • 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() and append()
  • 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()

Loops and Iteration

  • for loops
  • while loops
  • range()
  • 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

Text Processing

  • 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(...)

Regular Expressions

  • re.search()
  • re.fullmatch()
  • Pattern matching
  • Structured extraction
  • Formatted-text validation
  • Multi-line record processing

Data Validation

  • 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

Error and Exception Handling

  • try and except
  • raise
  • TypeError
  • ValueError
  • AttributeError
  • IndexError
  • KeyError
  • SyntaxError
  • 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

Object-Oriented Programming

  • 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

Date and Time

  • datetime.datetime.now()
  • Timestamp storage
  • strftime()
  • Date-time format codes
  • Timestamp display in object representations

Formatting and Text-Based Visualization

  • 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

Debugging and Testing

  • 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 None with is None and is not None
  • Avoiding infinite traversal loops
  • Updating head correctly when removing the first node
  • Preventing KeyError by 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() and sorted() 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! and INVALID!
  • 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 than pop(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

Selected Project Highlights

Implement the Breadth-First Search Algorithm

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 result

Traversal 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

Implement the N-Queens Algorithm

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 92 solutions 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 solutions

Search 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.


Implement the Depth-First Search Algorithm

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 visited

Traversal 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.


Build an Adjacency List to Matrix Converter

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 matrix

Conversion 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

Implement the Shortest Path Algorithm

Implemented Dijkstra's shortest-path algorithm for a weighted graph represented by an adjacency matrix.

The completed workshop includes:

  • An INF constant representing unavailable direct connections
  • A two-dimensional adjacency matrix
  • A shortest_path() function
  • Distance initialization with positive infinity
  • A visited list for finalized nodes
  • A paths list 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] * n

Minimum-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_no

Relaxation 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

Implement the Tower of Hanoi Algorithm

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 - 1 moves
  • 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

Implement the Merge Sort Algorithm

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 = 0

Merge 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

Implement the Quicksort Algorithm

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

Implement the Selection Sort Algorithm

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() and sorted()

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_index

Selection 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

Implement the Luhn Algorithm

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! and INVALID!

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

Implement the Bisection Method

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 tolerance and max_iterations parameters
  • 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 * root

Bisection 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

Build a Binary Search

Implemented an iterative binary search algorithm for sorted lists.

The completed workshop includes:

  • A binary_search() function
  • A sorted input list
  • A target value
  • low and high search boundaries
  • mid midpoint 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

Build a Hash Table

Implemented a simplified hash table that stores key-value pairs using computed hash values and nested buckets.

The completed certification project includes:

  • HashTable class
  • collection dictionary
  • hash() method
  • add() method
  • remove() method
  • lookup() 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.


Build a Linked List

Implemented a custom linked list using a nested Node class and object references.

The completed workshop includes:

  • A LinkedList class
  • A nested Node class
  • A length counter
  • A head reference
  • 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.


Build a Discount Calculator

Implemented a strategy-driven pricing engine that evaluates multiple discount algorithms and returns the lowest valid price.

The completed workshop includes:

  • A Product model
  • An abstract DiscountStrategy interface
  • PercentageDiscount
  • FixedAmountDiscount
  • PremiumUserDiscount
  • A DiscountEngine responsible 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.


Build a Polygon Area Calculator

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.


Build a Budget App

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 Algorithm Progression

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

Concepts Added at Each Stage

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.


Development Methodology

Each project follows a repeatable implementation process.

Standard Workflow

  1. Read the full specification.
  2. Identify required classes, functions, inputs, outputs, and constraints.
  3. Convert user stories into implementation tasks.
  4. Build the smallest working version.
  5. Run automated tests.
  6. Inspect the first failing test.
  7. Identify the exact technical cause.
  8. Apply a focused correction.
  9. Test boundary values and invalid inputs.
  10. Refactor for readability.
  11. Preserve required behavior while improving readability.
  12. Add comments and README documentation.
  13. Commit the completed project.

Test-Driven Feedback Loop

Requirement
    ↓
Implementation
    ↓
Automated test
    ↓
Failure analysis
    ↓
Focused correction
    ↓
Regression check

Code Quality Principles

  • 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.

Common Debugging Lessons

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

Important Lessons Reinforced

  • A small whitespace difference can fail an exact-output test.
  • Two independent if statements are not equivalent to if / elif / else.
  • Initialization order matters when setters depend on existing attributes.
  • Constructors must be named exactly __init__().
  • Instance methods require self as their first parameter.
  • A method must explicitly return a value when its result is needed.
  • isinstance() includes subclasses, while type() is checks 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_heigth and set_height are 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 = node and node = self.head do not mean the same thing.
  • None should be checked with is None or is 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] and array[middle_point:] is different from the slice() 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_false structure.
  • 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.
  • INF is 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 because is checks 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 - column and row + column.

Engineering-Oriented Direction

This repository is the programming foundation for future coastal and environmental engineering software.

Intended Applications

  • 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

Planned Engineering Project Structure

coastal-engineering-python/
├── tide-data-analysis/
├── water-level-processing/
├── salinity-analysis/
├── hydrodynamic-result-processing/
├── model-validation-metrics/
├── environmental-data-cleaning/
├── numerical-methods/
└── scientific-visualization/

Potential Engineering Classes

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:
    pass

Validation Pattern

A 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 = value

Strategy Pattern for Engineering Metrics

The 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):
        pass

Data Structures for Engineering Workflows

The Linked List workshop maps to workflows where data or operations form a chain.

class ProcessingStepNode:
    def __init__(self, step):
        self.step = step
        self.next = None

Example workflow:

Read raw data
        ↓
Clean missing values
        ↓
Interpolate time series
        ↓
Compare with model output
        ↓
Generate validation metrics

Hash-Based Indexing

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 for Sorted Engineering Data

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 None

Possible 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.

Graph Representation Conversion for Engineering Networks

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 for Engineering Networks

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 for Engineering Networks

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 visited

BFS 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 for Engineering Records

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.
    pass

Quicksort and Selection Sort for Engineering Data Organization

The 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.
    pass

Luhn-Style Checksum Validation for Structured Identifiers

The 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.
    pass

Bisection Method for Numerical Engineering Calculations

The 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.
    pass

Recursive Algorithms for Engineering Workflows

The 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.
    pass

The key lesson is that recursion is appropriate when the problem structure repeats at smaller scales.


Current Roadmap

Immediate Priorities

  • 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

Next Technical Stage

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

Long-Term Progression

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

Project Status

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

Python Certification Review

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.


Resources


Acknowledgements

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.


Author

Duong Kim Cuong

Coastal Engineering · Scientific Computing · Python · Artificial Intelligence

GitHub: github.com/kcduong994

About

Python learning portfolio focused on scientific computing, numerical modeling, and engineering applications.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages