Hex Minesweeper
ABOUT THIS PROJECT
Minesweeper, but every cell has six neighbors.
A team project for CS 3110, Cornell's data structures and functional programming course. Moving minesweeper from a square grid to a hexagonal one sounds cosmetic and is not: Adjacency, the flood fill that opens empty regions, and the coordinate system all have to be rebuilt, and doing it in OCaml with a team meant agreeing on interfaces first and testing each module against the others.
Hexagonal coordinates, both directions
Wrote the GUI module: axial to pixel for flat-top hexagons, x = size * 3/2 * q and y = size * sqrt(3) * (r + q/2), plus the six-vertex calculation the filled and outlined cells are drawn from. Getting the y term right is what fixed the horizontal alignment of the whole board; the version before it looked correct in isolation and drifted further out the wider the board got. Clicks go the other way through pixel_to_axial: convert to fractional axial, derive the third cube axis, round all three, and correct whichever axis has the largest rounding error so q + r + s stays zero, which is what keeps a click on a cell edge from landing in the wrong hex.
Difficulty as config
The UI and board sizes were hardcoded, so I moved them into a config.json parsed by Yojson into typed records: colors, display, gameplay, and easy, medium and hard settings of board radius and mine percentage. It loads lazily, falls back to a default config when the file is missing, and reports a Json_error rather than crashing. A start screen picks the difficulty, and the random map generation reads its radius and mine count from that record instead of from constants scattered through the generator.
A hint power-up
Added a hint tile type to the board: is_hint_powerup and revealed_by_hint fields on the tile record, placement through the same random coordinate picker the mines use, and its own colors in the GUI. Revealing a mine through a hint does not lose the game, and the numbers on tiles adjacent to a hint render in blue so the player can tell hinted information from earned information.
Property-based tests
QCheck tests with oracles across every module, 31 tests passing, and Bisect coverage above 80%. The oracle approach is the point: rather than assert on hand-picked boards, the tests generate boards and compare behavior against a reference implementation, which catches adjacency and flood-fill bugs that example-based tests walk straight past. The coordinate round trip, pixel to axial and back, is one of the generated properties.