- C++ 82.8%
- CMake 9.7%
- JavaScript 5.3%
- HTML 0.8%
- CSS 0.7%
- Other 0.7%
Both images ran as root. The graph image now creates a rose system account and chowns /app/shared before the VOLUME declaration, so an anonymous volume inherits that ownership; the web image hands /usr/src/app to the node account the base image already ships. Three latent defects surfaced alongside. The builder stage had no WORKDIR, so sources landed in / and cmake's output came out at /build by accident of that default, which the runtime COPY then depended on. Adding a WORKDIR later would have moved the binary and broken that COPY silently, so it is now pinned to /src and referenced explicitly. set -e does not cover a failure on the left of a pipe, so a failed fetch of the LLVM signing key wrote an empty keyring and surfaced later as an opaque apt error. The builder stage now runs under bash -o pipefail. The web entrypoint was npx live-server, but live-server was never a declared dependency, so npx fetched it from the registry on every container start. It is installed once at build time now, pinned to 1.2.2, and invoked directly. A global install rather than an entry in web/package.json: live-server is unmaintained and would have taken that lockfile from 175 packages to 360. |
||
|---|---|---|
| .github/workflows | ||
| doc | ||
| docker | ||
| include | ||
| src | ||
| tests | ||
| web | ||
| .clang-format | ||
| .dockerignore | ||
| .gitignore | ||
| CMakeLists.txt | ||
| CMakePresets.json | ||
| CPPLINT.cfg | ||
| docker-compose.yml | ||
| flake.lock | ||
| flake.nix | ||
| LICENCE | ||
| PLAN.md | ||
| PROFILING.md | ||
| README.md | ||
Rose
Visualiser for Solitaire game graphs. You can interact with it online here.
Run with Docker
Set the timeout to whatever you wish; the performance of the webpage is proportional to the graph size, so if it's too slow, drop this lower to get a smaller graph.
BFS_TIMEOUT_S=0.1 docker compose up
Access the web app on http://localhost:8080.
By default, the graph will be explored by a breadth-first search (BFS) until the timeout. Alternatively, by setting WITH_DFS=true as below, it will first explore down a single path, depth-first, using a simple heuristic for the best move in each state. Once no more moves can be made, the BFS is done from every node in the path. Using this heuristic makes it possible to find the winning state, which will be labelled if found.
WITH_DFS=true BFS_TIMEOUT_S=0.1 docker compose up
Search algorithm
Set ALGORITHM to choose how the graph is explored (within the same depth/timeout budget):
bfs(default) — breadth-first search; explores every state level by level.bestfirst— greedy best-first search ordered by a state heuristic (foundation progress, face-down cards, blocked cards, and free columns), biasing exploration toward promising states.astar— A* search ordered by depth plus an admissible foundation-distance heuristic, reaching winning states along shorter paths first.
ALGORITHM=astar BFS_TIMEOUT_S=0.1 docker compose up
WITH_DFS=true takes precedence over ALGORITHM. All three algorithms build the complete state graph for visualisation; they differ only in which states they expand first when the budget is limited.
Build locally
With Nix (recommended)
A flake.nix provides a dev shell with every tool the build needs
(cmake, clang, cppcheck, cpplint, node/npm, fmt), pinned via flake.lock
for reproducibility.
$ nix develop
Run any of the commands below inside that shell.
Without Nix
Depends on:
cmake- a C++23 compiler (gcc or clang)
npmcpplintandcppcheck(for thelinttarget)clang-format(for theformattarget)
Build
$ mkdir build
$ cd build
$ cmake ..
$ cmake --build .
Or with CMake presets (also used by CI):
$ cmake --preset release
$ cmake --build --preset release
Run Tests
cmake --build . --target tests
Lint
cmake --build . --target lint
Format
cmake --build . --target format
