Skip to content

Advent of Code Overview

This section demonstrates how to solve Advent of Code problems using Hardcaml. These examples show how hardware design principles can be applied to algorithmic challenges.

Advent of Code is an annual programming competition with daily puzzles throughout December. Each problem presents an algorithmic challenge that can be solved with software, but we’re solving them with hardware circuits instead!

Solving AoC problems in Hardcaml demonstrates:

  • Sequential logic - State machines for processing streams of data
  • Arithmetic circuits - Adders, multipliers, comparators
  • Memory management - Registers and state tracking
  • Real-world applications - Beyond toy examples

Each AoC problem is implemented as a Hardcaml circuit with:

  • Input interface - Streams of data (digits, commands, etc.)
  • State registers - Track progress through the problem
  • Combinational logic - Process inputs and compute results
  • Output interface - Final answer or running totals

A dial with positions 0-99 rotates left or right based on commands. Count how many times it lands on position 0.

Key concepts: Modular arithmetic, state machines, counters

Find numbers where the first half of digits equals the second half (e.g., 1212).

Key concepts: Digit extraction, pattern matching, comparisons

Process lines of digits to find maximum values in specific ranges and compute a sum.

Key concepts: Sequential processing, maximum tracking, multi-cycle computations

Count regions where shapes of a given area can fit within a grid.

Key concepts: Area calculations, comparisons, accumulation

Most AoC problems use state machines to process input streams:

let spec = Reg_spec.create ~clock:i.clock ~clear:i.clear () in
let open Always in
let%hw_var state = Variable.reg spec ~width:state_bits in
let%hw_var accumulator = Variable.reg spec ~width:acc_bits in
compile
[ when_ i.start
[ state <-- initial_state
; accumulator <-- zero acc_bits
]
; when_ i.input_valid
[ (* Process input *)
state <-- next_state
; accumulator <-- accumulator.value +: computed_value
]
];

Many problems require modulo operations. Since hardware doesn’t have native division, use repeated subtraction:

let mod_100 ~width x =
let hundred = of_int_trunc ~width 100 in
let sub_if_ge y = mux2 (y >=: hundred) (y -: hundred) y in
Fn.apply_n_times ~n:9 sub_if_ge x
;;

When working with digits, extract them from packed representations:

let get_digit ~digits ~index =
let start_bit = (max_digits - 1 - index) * digit_bits in
select digits ~high:(start_bit + digit_bits - 1) ~low:start_bit
;;
  1. Open the IDE
  2. Select an Advent of Code example from the dropdown
  3. Read the problem description in the circuit file
  4. Study the test harness to understand the input format
  5. Run the circuit to see the solution
  • Read the comments - Each circuit file has detailed problem descriptions
  • Check the tests - Test files show expected input/output formats
  • Watch waveforms - Visualize how state changes over time
  • Start simple - Begin with Day 1 to understand the pattern