Skip to content

Commit e31bc44

Browse files
authored
Update documentation for improved clarity and structure; add formatter CLI details and usage examples (#28)
Fixes #27
1 parent c052d25 commit e31bc44

3 files changed

Lines changed: 86 additions & 59 deletions

File tree

DEVELOPMENT.md

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,15 @@ It has been structured as a C-API library (`jres_solver`) and a simple CLI clien
1818
| ├── jres_solver_utils.cpp # Shared utilities and Base Class
1919
| ├── jres_standard_solver.cpp # Optimized Strict Solver
2020
| ├── jres_diagnostic_solver.cpp # Relaxed Diagnostic Solver
21-
| └── jres_solver_types.cpp # JSON serialization logic
21+
| ├── jres_solver_types.cpp # JSON serialization logic
22+
| ├── formatter/ # Formatter implementation
23+
| └── utils/ # Utility functions
2224
├── lib/
2325
│ ├── cxxopts/ # (Git Submodule) cxxopts header-only library
2426
│ └── json/ # (Git Submodule) nlohmann/json header-only library
2527
├── cmd/
26-
│ ├── /solver/ # A CLI solver
27-
│ └── /formatter/ # A CLI formatter
28+
│ ├── solver/ # A CLI solver
29+
│ └── formatter/ # A CLI formatter
2830
├── test/ # Google Test suite
2931
└── CMakeLists.txt # The main build script
3032
```
@@ -112,25 +114,37 @@ We use a standard out-of-source CMake build.
112114
cmake --build .
113115
```
114116
115-
This will create two main products in the `build/` directory:
117+
This will create the main products in the `build/` directory:
116118
117-
* `libjres_solver.dylib` (or `.so` on Linux, `.dll` on Windows): The shared library.
118-
* `jres_solver` (or `jres_solver.exe`): The CLI executable.
119+
* `libjres_solver.a` (or `.lib` on Windows): The static library.
120+
* `jres_solver` (or `jres_solver.exe`): The solver CLI executable.
121+
* `jres_formatter` (or `jres_formatter.exe`): The formatter CLI executable.
119122
120-
## Running the CLI Client
123+
## Running the CLI Clients
124+
125+
### Solver
121126
122127
The `jres_solver` executable is a client that uses the `jres_solver` library.
123128
124129
```
125130
# Run with a file
126-
./jres_solver -i ../data/race_data.json -s integrated
131+
./jres_solver -i ../data/short_race.json -s integrated
127132

128133
# Pipe from stdin
129-
cat ../data/race_data.json | ./jres_solver -s sequential --allow-no-spotter
134+
cat ../data/24h_race.json | ./jres_solver -s sequential --allow-no-spotter
130135

131136
# Run diagnostics on a failing schedule
132137
./jres_solver -i ../data/infeasible.json --diagnose
133138

134139
# Get help
135140
./jres_solver --help
136141
```
142+
143+
### Formatter
144+
145+
The `jres_formatter` executable formats solver output into various report formats.
146+
147+
```
148+
# Get help
149+
./jres_formatter --help
150+
```

README.md

Lines changed: 47 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -18,44 +18,56 @@ This library can be used to solve for optimal driver and spotter schedules for e
1818

1919
The library exposes a C-compatible API and can be bound to languages like C, C++, Go, Python, or Rust.
2020

21-
#### Header: `jres_solver.h`
21+
#### Basic Usage Example
2222

2323
```cpp
24-
// Structure defining solver configuration
25-
struct JresSolverOptions {
26-
int timeLimit; // Max runtime in seconds (e.g., 30)
27-
JresSpotterMode spotterMode; // 0=None, 1=Integrated, 2=Sequential
28-
bool allowNoSpotter; // If true, stints can go without a spotter
29-
double optimalityGap; // Stop when solution is within this % of optimal (e.g., 0.05)
30-
bool quiet; // If true, suppresses stdout logging
31-
};
32-
33-
// Enum for spotter modes
34-
enum JresSpotterMode {
35-
JRES_SPOTTER_MODE_NONE = 0, // No Spotter schedule is solved for
36-
JRES_SPOTTER_MODE_INTEGRATED = 1, // Driver and Spotter schedule is solved together
37-
JRES_SPOTTER_MODE_SEQUENTIAL = 2 // Driver schedule is prioritized
38-
};
39-
40-
// Main Solver Function
41-
// Returns 0 on success, -1 on failure.
42-
// outputJson is allocated by the library and must be freed by the caller.
43-
int solve_race_schedule(const char* raceDataJson,
44-
const JresSolverOptions& options,
45-
char** outputJson);
46-
47-
// Diagnostic Solver Function
48-
// Runs a relaxed model to identify why a schedule is infeasible.
49-
// Returns 0 on success (diagnosis complete), -1 on internal failure.
50-
// outputJson will contain a "diagnosis" array with string explanations.
51-
int diagnose_race_schedule(const char* raceDataJson,
52-
const JresSolverOptions& options,
53-
char** outputJson);
54-
55-
// Memory Cleanup
56-
void free_solver_result(char* resultJson);
24+
#include "jres_solver/jres_solver.hpp"
25+
26+
// Configure solver options
27+
JresSolverOptions options;
28+
options.timeLimit = 5;
29+
options.spotterMode = JRES_SPOTTER_MODE_INTEGRATED;
30+
options.allowNoSpotter = false;
31+
options.optimalityGap = 0.2;
32+
options.quiet = false;
33+
34+
// Solve with race data JSON string
35+
char* resultJson = nullptr;
36+
int status = solve_race_schedule(raceDataJson, options, &resultJson);
37+
38+
// Always free the result
39+
free_solver_result(resultJson);
5740
```
5841
42+
For a complete working example, see [`cmd/solver/cli.cpp`](./cmd/solver/cli.cpp).
43+
44+
#### Solver vs. Diagnostic Mode
45+
46+
- **`solve_race_schedule()`**: Finds an optimal schedule satisfying all constraints. Returns an error if no feasible solution exists.
47+
48+
- **`diagnose_race_schedule()`**: When the solver fails, this runs a relaxed model to identify which constraints are causing the infeasibility.
49+
50+
#### Controlling Solve Time
51+
52+
The solver can take a very long time (or never complete) for complex schedules if not properly constrained. Use `timeLimit` and `optimalityGap` to prevent excessive runtimes:
53+
54+
**Recommended defaults:** `timeLimit = 5` seconds, `optimalityGap = 0.2` (20%)
55+
56+
- **`timeLimit`**: Maximum seconds the solver will run before returning the best solution found. This is a hard stop.
57+
58+
- **`optimalityGap`**: Allows the solver to stop early when it finds a "good enough" solution within this percentage of the theoretical optimum. For example, `0.2` means the solver stops once it finds a solution within 20% of optimal.
59+
60+
**Why a small optimality gap is expensive and unnecessary:**
61+
62+
Mixed Integer Programming problems like race scheduling are NP-hard. The solver may find a good feasible solution quickly (in seconds), but proving that solution is within 1% of optimal can take exponentially longer—hours or even days. For practical scheduling:
63+
64+
- A 20% gap solution is typically excellent and solves in seconds
65+
- A 5% gap might take 10-100x longer with minimal practical benefit
66+
- A 1% gap can be prohibitively expensive, often timing out
67+
- The "optimal" schedule and a 20% suboptimal schedule are often nearly identical in practice—swapping equivalent drivers or spotters
68+
69+
The solver prioritizes hard constraints (rest times, fuel, availability) first. The optimality gap only affects soft preferences like minimizing consecutive stints. A 20% gap on these preferences is imperceptible in real-world use.
70+
5971
-----
6072
6173
### Input JSON Specification
@@ -183,6 +195,7 @@ The function returns a JSON string containing the solution or error details.
183195
}
184196
```
185197

198+
186199
---
187200

188201
_Created by popmonkey, Gemini 2.5, Gemini 3.0, and ChatGPT 5.1_

TOOLS.md

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,15 @@ brew tap popmonkey/jres_solver
1818
brew install jres_solver
1919
```
2020

21-
### Windows Specific Requirement
22-
23-
On Windows, the `jres_solver` application is built as a thin executable that relies on a shared library. You must ensure the following DLL is present in the same directory as the executable (or in your system `%PATH%`):
24-
25-
* **`jres_solver.dll`**
26-
27-
The `jres_formatter` tool is statically linked and does not require this DLL.
28-
2921
---
3022

3123
## Solver (`jres_solver`)
3224

3325
The **Solver** is the core engine. It accepts raw race data (track info, driver constraints, car specs) and calculates the optimal driver schedule.
3426

27+
> [!TIP]
28+
> See the [README](./README.md#controlling-solve-time) for guidance on controlling solve time and understanding the difference between [Solver vs. Diagnostic Mode](./README.md#solver-vs-diagnostic-mode).
29+
3530
### Usage
3631

3732
```sh
@@ -101,11 +96,18 @@ If a schedule fails to solve, run with `-d` to get a plain English explanation o
10196
./jres_solver -i race_config.json --diagnose
10297
```
10398

104-
**Advanced Optimization:**
105-
Run for up to 5 minutes (`300s`), use `integrated` spotter logic, and allow a 1% margin of error for faster results.
99+
**With Spotter Scheduling:**
100+
Use `integrated` spotter logic with a reasonable optimality gap for faster results.
106101

107102
```sh
108-
./jres_solver -i race_config.json -o solution.json -t 300 --spotter-mode integrated --optimality-gap 0.01
103+
./jres_solver -i race_config.json -o solution.json --spotter-mode integrated --optimality-gap 0.2
104+
```
105+
106+
**Extended Solve Time:**
107+
For complex schedules, allow more time while maintaining a practical optimality gap.
108+
109+
```sh
110+
./jres_solver -i race_config.json -o solution.json -t 30 --optimality-gap 0.2
109111
```
110112

111113
**Pipeline Usage:**
@@ -173,8 +175,8 @@ Here is how you would go from a raw configuration file to a final distributable
173175
### Linux / macOS
174176

175177
```sh
176-
# Step 1: Solve the schedule (allowing 60 seconds for calculation)
177-
./jres_solver -i race_24h_config.json -o solved_schedule.json -t 60
178+
# Step 1: Solve the schedule (allowing 30 seconds with 20% optimality gap)
179+
./jres_solver -i race_24h_config.json -o solved_schedule.json -t 30 -g 0.2
178180

179181
# Step 2: Format the solution into a ZIP file
180182
./jres_formatter -i solved_schedule.json -o team_schedule.zip
@@ -183,10 +185,8 @@ Here is how you would go from a raw configuration file to a final distributable
183185
### Windows (Command Prompt)
184186

185187
```cmd
186-
REM Ensure jres_solver.dll is in the current folder
187-
188188
REM Step 1: Solve
189-
jres_solver.exe -i race_24h_config.json -o solved_schedule.json -t 60
189+
jres_solver.exe -i race_24h_config.json -o solved_schedule.json -t 30 -g 0.2
190190
191191
REM Step 2: Format
192192
jres_formatter.exe -i solved_schedule.json -o team_schedule.zip

0 commit comments

Comments
 (0)