Inspiration

University registration is a mess. You have a list of courses you need, a handful of sections to choose from, and a deadline creeping up. So, you open a spreadsheet, start manually checking for time conflicts, realize you forgot to account for your commute or that you hate 8am classes, and eventually just pick something that works without really knowing if there's something better. We wanted to fix that. The idea was simple: what if your computer did all that heavy lifting for you? That's Forge.

What it does

Forge generates every valid, conflict-free schedule from a set of selected courses and ranks them based on user preferences. You pick your courses, set preferences (like compact days, spread across the week, or grouped by subject), and Forge returns the top options ordered by how well they match what you asked for.

The key design decision was treating preferences as soft constraints and time conflicts as hard constraints. A conflict eliminates a schedule entirely. A preference just affects its score. That means Forge always returns something useful, even when the ideal isn't possible.

The scoring model is a weighted sum:

$$S = \sum_{i=1}^{n} w_i \cdot f_i(schedule)$$

Where \(w_i\) is the user-defined weight for preference \(i\), and \(f_i\) is the scoring function for that preference (compactness, day balance, etc.).

How we built it

Backend: Python + FastAPI

The core is a backtracking algorithm that builds schedules incrementally, pruning any branch the moment a time conflict appears. Time blocks are stored as minutes from midnight, so overlap detection is a simple interval check:

def overlaps_with(self, other: 'TimeBlock') -> bool:
    if self.day != other.day:
        return False
    return self.start < other.end and other.start < self.end

We scraped the full Colorado School of Mines course catalog using BeautifulSoup to pull course codes, names, prerequisites, and coreqs across every department. That data feeds into the sample section pool the optimizer runs against.

FastAPI sits on top with two endpoints: one to return available courses, one to accept selected courses plus preference weights and return ranked schedules.

Frontend: React + Tailwind

Course selector, preference sliders, and a weekly calendar grid that renders the top results as colored time blocks. React Query handles the API calls. Users can edit their generated schedules sections with an easy-to-use, intuitive interface.

Challenges we ran into

Combinatorial explosion. With enough courses and sections, the number of possible combinations grows fast. For \(n\) courses each with \(k\) sections, worst case is \(k^n\) combinations. Backtracking with early pruning keeps this manageable in practice, but it is something we will need to handle more carefully at scale.

$$\text{combinations} \leq k^n$$

Scoring is subjective. "Compact" means different things to different people. We settled on gap time between classes on a given day as the measure, but calibrating the scoring functions so that preference weights actually feel meaningful to users took more iteration than expected.

The catalog doesn't have section times. The scraper pulls from the course catalog, which has descriptions and prereqs but no actual meeting times. For the POC, we used hard-coded sample data. Getting real section times requires pulling from the Banner registration system, which is a separate scraping problem we punted on.

Accomplishments that we're proud of

Getting the optimizer to actually work end-to-end in under 24 hours was difficult, but we achieved it. The backtracking implementation is clean, the conflict detection is correct, and the scoring returns results that make intuitive sense. The scraper also successfully pulled the entire Mines course catalog across all departments, which gives us a real data foundation to build on.

What we learned

Constraint satisfaction problems look simple until you sit down to implement them. The hard part isn't the algorithm, it's the data model underneath it. Getting TimeBlock, Section, and Course right before writing a single line of optimizer logic made everything else much easier.

We also learned that "soft" vs "hard" constraints is not just an academic distinction. It's the difference between a tool that occasionally fails to find a result and one that always gives you something to work with.

What's next for Forge

  • Pull live section data from the Mines Banner registration system so users are working with real availability, not sample data
  • Connect to students' DegreeWorks or transcript to handle prerequisites within the application
  • User accounts so schedules can be saved across sessions
  • A watchlist feature that monitors seat availability and sends a notification when a section opens up
  • Expand beyond Mines to other universities that use Banner or similar systems

Built With

Share this project:

Updates