From 2b0602d56478f298ba93e7fa2622b734681cf4e9 Mon Sep 17 00:00:00 2001 From: Jan-Peter von Hunnius Date: Mon, 11 May 2026 16:58:01 +0200 Subject: [PATCH] docs: update documentation for normalized config and improve command - Add improve command to all command tables and usage sections - Add normalized_config field to BestCarRecord struct documentation - Update CarCounts.by_rid type from String to (String, String) - Update CarCounts.guid_to_rid type to HashMap> - Add owned_configs field to MyCarsData and CarCounts docs - Add normalize_to_final_build() to API docs - Update CSV format examples to include FinalBuild column - Update my_cars.json structure with engineMajor/weightMajor/chassisMajor - Update timing file example with v2c_ version prefix - Add version prefix stripping to data transformation docs - Update CHANGELOG.md with Unreleased section - Fix compare_records description to include normalized config sorting --- CHANGELOG.md | 25 ++++++++++++ README.md | 45 +++++++++++++-------- docs/API.md | 63 ++++++++++++++++++++--------- docs/ARCHITECTURE.md | 41 +++++++++++-------- docs/DATA_STRUCTURES.md | 26 ++++++++++-- docs/USER_GUIDE.md | 88 ++++++++++++++++++++++++++++------------- 6 files changed, 204 insertions(+), 84 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bc8021c..03aff7d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,31 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added +- New `improve` command that generates `improve.csv` — owned cars not yet at a final tune build, sorted by track count +- `normalized_config` field on `BestCarRecord` — maps any tune config to its closest final build (323/233/332) +- `normalize_to_final_build()` function — normalizes tune configs by incrementing the smallest digit until sum reaches 8 +- `owned_configs` field on `MyCarsData` — extracts actual player tune config (engineMajor/weightMajor/chassisMajor) from `my_cars.json` +- `owned_configs` field on `CarCounts` — passes player config data through to output functions +- `atomic_write()` helper — writes to a temporary file first, then renames on success (no partial CSV files) +- `empty_my_data()` test helper — creates an empty owned configs map for tests +- Version prefix stripping (`v2c_`/`v1c_`) for config keys at parse time +- `FinalBuild` column in `best.csv`, `very_best.csv`, `wishlist.csv`, `add.csv`, `remove.csv` output files +- Dedup check in `update_best_car` now considers `normalized_config` to prevent same car+config duplicates + +### Changed +- `CarCounts.by_rid` key changed from `String` to `(String, String)` for per-config tracking +- `CarCounts.guid_to_rid` changed from `HashMap` to `HashMap>` +- `compute_car_counts()` now accepts `owned_configs` parameter +- `compare_records()` now sorts by normalized config as secondary key (after RQ, before brand) +- `load_my_cars_data()` now extracts tune config data for locked cards +- CSV output files now include `FinalBuild` column showing the normalized config +- `update_best_car()` tie detection now checks both `car_rid` and `normalized_config` +- Applied `rustfmt` formatting across the entire codebase + +### Removed +- `plans/code-review-v2.md` (completed action items) + ## [0.3.0] - 2026-05-07 ### Added diff --git a/README.md b/README.md index 437d141..301e13c 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ A comprehensive Rust-based tool for analyzing car performance data in the Top Dr - **Best Car Analysis**: Find the best performing cars across all tracks and conditions - **Wishlist Optimization**: Identify cars to add to or remove from your wishlist - **Collection Cleanup**: Identify cars that can be safely removed from your collection +- **Upgrade Prioritization**: Find owned cars not yet at their final tune build - **Track-Specific Analysis**: Get detailed performance data for specific tracks and conditions - **RQ-Based Filtering**: Filter results by car rarity (RQ) levels - **Multiple Surface Types**: Support for Dry, Wet, Dirt, Snow, and other track conditions @@ -75,6 +76,11 @@ A comprehensive Rust-based tool for analyzing car performance data in the Top Dr cargo run --bin records -- wishlist ``` +6. **Upgrade Prioritization**: Find cars that need upgrading + ```bash + cargo run --bin records -- improve + ``` + ## 📖 Usage Guide ### Basic Commands @@ -140,7 +146,7 @@ cargo run --bin records -- wishlist --max-wishlist 500 ```bash cargo run --bin records -- all ``` -Generates all 6 CSV files in a single parse pass. Equivalent to running `best`, `trash`, and `wishlist` together, but more efficient. +Generates all 7 CSV files in a single parse pass. Equivalent to running `best`, `trash`, `wishlist`, and `improve` together, but more efficient. **Output Files:** ``` @@ -150,6 +156,7 @@ trash.csv — Owned cars safe to remove wishlist.csv — Ideal wishlist (very_best unowned, capped) add.csv — Cars to add to wishlist remove.csv — Cars to remove from wishlist +improve.csv — Owned cars not yet at final build config ``` Supports both `--min-count N` (default 10) and `--max-wishlist M` (default 300): @@ -197,9 +204,10 @@ The analyzer supports multiple track conditions: | `best` | Generate `best.csv` and `very_best.csv` | `[--min-count N]` | | `trash` | Generate `trash.csv` (owned cars safe to remove) | None | | `wishlist` | Generate `wishlist.csv`, `add.csv`, `remove.csv` | `[--min-count N] [--max-wishlist M]` | -| `all` | Generate all 6 CSV files in a single pass | `[--min-count N] [--max-wishlist M]` | +| `all` | Generate all 7 CSV files in a single pass | `[--min-count N] [--max-wishlist M]` | | `details` | Print detailed timing data for all tracks (stdout) | None | | `track` | Print best car for specific track (stdout) | ` ` | +| `improve` | Generate `improve.csv` (owned cars not at final build) | None | ### Flags @@ -226,12 +234,13 @@ This generates the following output files (all sorted by car RQ): | File | Format | Description | |------|--------|-------------| -| `best.csv` | `RQ,Brand,Model,Year,Count` | All cars best on at least one track | -| `very_best.csv` | `RQ,Brand,Model,Year,Count` | Cars best on ≥ min_count tracks | +| `best.csv` | `RQ,Brand,Model,Year,FinalBuild,Count` | All cars best on at least one track | +| `very_best.csv` | `RQ,Brand,Model,Year,FinalBuild,Count` | Cars best on ≥ min_count tracks | | `trash.csv` | `RQ,Brand,Model,Year` | Owned cars not best on any track (RQ<80, not prize) | -| `wishlist.csv` | `RQ,Brand,Model,Year,Count` | Ideal wishlist (very_best unowned, capped) | -| `add.csv` | `RQ,Brand,Model,Year,Count` | Cars to add to wishlist | -| `remove.csv` | `RQ,Brand,Model,Year,Count` | Cars to remove from wishlist | +| `wishlist.csv` | `RQ,Brand,Model,Year,FinalBuild,Count` | Ideal wishlist (very_best unowned, capped) | +| `add.csv` | `RQ,Brand,Model,Year,FinalBuild,Count` | Cars to add to wishlist | +| `remove.csv` | `RQ,Brand,Model,Year,FinalBuild,Count` | Cars to remove from wishlist | +| `improve.csv` | `RQ,Brand,Model,Year,Target,Count` | Owned cars not yet at final build config | ### Workflow Steps @@ -241,6 +250,7 @@ This generates the following output files (all sorted by car RQ): 4. **Check** `add.csv` for cars worth adding to your wishlist 5. **Clean up** `remove.csv` entries from your wishlist 6. **Sell** cars listed in `trash.csv` that are no longer useful +7. **Upgrade** cars listed in `improve.csv` to their target final build ## 📊 Data Sources @@ -318,16 +328,17 @@ struct Car { #### Performance Records ```rust struct BestCarRecord { - car_rid: String, // Car identifier - brand: String, // Manufacturer - only_name: String, // Model name - year: u16, // Year - rq: u8, // Rarity level - config: String, // Tune configuration - time: f64, // Track time - owned: bool, // Whether you own this car - prize: bool, // Whether this is a prize car - guid: String, // Game identifier + car_rid: String, // Car identifier + brand: String, // Manufacturer + only_name: String, // Model name + year: u16, // Year + rq: u8, // Rarity level + config: String, // Original tune config from JSON + normalized_config: String, // Normalized final build (323, 233, or 332) + time: f64, // Track time + owned: bool, // Whether you own this car + prize: bool, // Whether this is a prize car + guid: String, // Game identifier } ``` diff --git a/docs/API.md b/docs/API.md index ac80cfa..c4a558a 100644 --- a/docs/API.md +++ b/docs/API.md @@ -25,6 +25,7 @@ src/ │ ├── wishlist.rs # Wishlist command: wishlist.csv, add.csv, remove.csv │ ├── details.rs # Details command: details.csv │ ├── track.rs # Track command: stdout query +│ ├── improve.rs # Improve command: improve.csv (owned cars not at final build) │ └── all.rs # All command: generates all output files ├── parsing/ │ ├── mod.rs # Core parsing: parse_car_times, update_best_car, parse_all_times @@ -69,16 +70,17 @@ pub struct Car { ```rust #[derive(Debug, Clone)] pub struct BestCarRecord { - pub car_rid: String, // Car identifier - pub brand: String, // Manufacturer - pub only_name: String, // Model name - pub year: u16, // Year - pub rq: u8, // RQ level - pub config: String, // Tune config (111, 233, etc.) - pub time: f64, // Track time in seconds - pub owned: bool, // User ownership status - pub prize: bool, // Prize car status - pub guid: String, // Game identifier + pub car_rid: String, // Car identifier + pub brand: String, // Manufacturer + pub only_name: String, // Model name + pub year: u16, // Year + pub rq: u8, // RQ level + pub config: String, // Original tune config from JSON (111, 221, 233, etc.) + pub normalized_config: String, // Normalized final build (323, 233, or 332) + pub time: f64, // Track time in seconds + pub owned: bool, // User ownership status + pub prize: bool, // Prize car status + pub guid: String, // Game identifier } ``` @@ -109,21 +111,25 @@ pub struct AnalysisContext { ```rust pub struct CarCounts { - pub by_rid: HashMap, - pub guid_to_rid: HashMap, + pub by_rid: HashMap<(String, String), (BestCarRecord, usize)>, + pub guid_to_rid: HashMap>, pub car_map: HashMap, pub car_list: Vec, + pub owned_configs: HashMap, } ``` +**Key grouping**: `by_rid` is keyed by `(car_rid, normalized_config)` so different tune configs of the same car are tracked separately. `guid_to_rid` maps each GUID to all `(rid, config)` pairs it appears under. + #### `MyCarsData` **Parsed user data from `my_cars.json`** ```rust #[derive(Debug, Default)] pub struct MyCarsData { - pub owned: HashSet, // Owned car GUIDs - pub wishlist: HashSet, // Wishlist car GUIDs + pub owned: HashSet, // Owned car GUIDs + pub wishlist: HashSet, // Wishlist car GUIDs + pub owned_configs: HashMap, // GUID → actual tune config (e.g., "233") } ``` @@ -183,11 +189,12 @@ pub enum AppError { - Sorts by: RQ → Brand → Model name -#### `compute_car_counts(ctx: &AnalysisContext, car_list: Vec) -> CarCounts` +#### `compute_car_counts(ctx: &AnalysisContext, car_list: Vec, owned_configs: HashMap) -> CarCounts` **Aggregates per-car win counts from analysis context** - Iterates all entries in the `DashMap` -- Returns `CarCounts` with by_rid, guid_to_rid, car_map, and car_list +- Groups by `(car_rid, normalized_config)` for per-config tracking +- Returns `CarCounts` with by_rid, guid_to_rid, car_map, car_list, and owned_configs #### `escape_csv_field(field: &str) -> String` **Escapes a field for safe CSV inclusion** @@ -195,6 +202,14 @@ pub enum AppError { - Wraps in quotes if field contains comma, quote, newline, or carriage return - Doubles internal quotes per CSV spec +#### `normalize_to_final_build(config: &str) -> String` +**Normalizes a tune configuration to the closest "final build"** + +- The three final builds are `"323"`, `"233"`, and `"332"` (permutations of digits summing to 8) +- For non-standard configs, repeatedly increments the smallest digit until sum reaches 8 +- Returns the original string unchanged if config cannot be parsed or sum already ≥ 8 +- Version prefixes (`v2c_`, `v1c_`) are stripped at parse time before reaching this function + --- ## Records Binary (`src/records.rs`) @@ -221,6 +236,7 @@ CLI entry point. Defines command-line arguments and dispatches to command handle | `all` | Generate all output files in a single pass | `--min-count N`, `--max-wishlist M` | | `details` | Generate `details.csv` with all timing data | None | | `track` | Show best car for specific track/condition/RQ | ` ` | +| `improve` | Generate `improve.csv` (owned cars not at final build) | None | --- @@ -309,7 +325,7 @@ Loads car database, parses all timing files concurrently, and computes car count #### `write_best_csv(counts)`, `write_very_best_csv(counts, min_count)` **Generate best car CSV files** -- Format: `RQ,Brand,Model,Year,Count` +- Format: `RQ,Brand,Model,Year,FinalBuild,Count` - Fields are CSV-escaped #### `write_trash_csv(counts, owned)` @@ -323,7 +339,16 @@ Loads car database, parses all timing files concurrently, and computes car count - Computes ideal wishlist once - Derives all three files from it -- Format: `RQ,Brand,Model,Year,Count` +- Format: `RQ,Brand,Model,Year,FinalBuild,Count` + +#### `write_improve_csv(counts) -> Result` +**Generate improve CSV file** + +- Lists owned cars whose actual player config is not yet a final build (323/233/332) +- Uses actual player config from `my_cars.json`, not timing data configs +- Format: `RQ,Brand,Model,Year,Target,Count` +- Sorted by track count descending (most impactful upgrades first) +- Returns the number of cars written #### `write_details_csv(ctx)` **Generate comprehensive timing data CSV** @@ -347,4 +372,4 @@ Loads car database, parses all timing files concurrently, and computes car count #### `compare_records(a: &BestCarRecord, b: &BestCarRecord) -> std::cmp::Ordering` **Comparison for BestCarRecord sorting** -- Sorts by: RQ → Brand → Model name +- Sorts by: RQ → Normalized Config → Brand → Model name diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index a7a6870..713918a 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -14,13 +14,13 @@ The Top Drives Records Analyzer is a Rust CLI tool for analyzing car performance | Module | File | Key Exports | |--------|------|-------------| -| `types` | `src/types.rs` | `AppError`, `BestCarRecord`, `Car`, `TripletKey` | +| `types` | `src/types.rs` | `AppError`, `BestCarRecord`, `Car`, `TripletKey`, `normalize_to_final_build` | | `context` | `src/context.rs` | `AnalysisContext`, `CarCounts`, `compute_car_counts` | | `user_data` | `src/user_data.rs` | `MyCarsData`, `load_my_cars_data` | | `translate` | `src/translate.rs` | `translate_track_name`, `translate_track_type` | | `http` | `src/http.rs` | `get_file` | | `csv_util` | `src/csv_util.rs` | `compare_cars`, `escape_csv_field` | -| `test_utils` | `src/test_utils.rs` | `make_record`, `make_car` (test helpers) | +| `test_utils` | `src/test_utils.rs` | `make_record`, `make_car`, `empty_my_data` (test helpers) | #### Error Handling All public functions return `Result` where `AppError` is a unified error type: @@ -58,6 +58,7 @@ All public functions return `Result` where `AppError` is a unified | `wishlist.rs` | `wishlist` | Generates `wishlist.csv`, `add.csv`, `remove.csv` | | `details.rs` | `details` | Generates `details.csv` with all timing data | | `track.rs` | `track` | Prints best car for a specific track/condition/RQ | +| `improve.rs` | `improve` | Generates `improve.csv` (owned cars not yet at final build) | | `all.rs` | `all` | Generates all output files in a single analysis pass | #### Shared Helpers (`src/commands/mod.rs`) @@ -86,14 +87,16 @@ All public functions return `Result` where `AppError` is a unified **CSV output utilities** and sorting helpers. -- `write_sorted_csv()` — Generic sorted CSV writer (index-based sorting to avoid cloning) +- `atomic_write()` — Writes to a temporary file first, then renames on success (no partial files) +- `write_sorted_csv()` — Generic sorted CSV writer (index-based sorting to avoid cloning, uses `atomic_write`) - `write_best_csv()`, `write_very_best_csv()` — Best car CSV output - `write_trash_csv()` — Trash car CSV output - `write_details_csv()` — Comprehensive timing data CSV output - `write_wishlist_files()` — Wishlist, add, and remove CSV output +- `write_improve_csv()` — Improve car CSV output (owned cars not yet at final build) - `print_best_car()` — Stdout output for track-specific query - `compute_ideal_wishlist()` — Ideal wishlist computation -- `compare_records()` — Sorting comparator for `BestCarRecord` +- `compare_records()` — Sorting comparator for `BestCarRecord` (by RQ, normalized config, brand, model) #### Data Flow @@ -146,16 +149,17 @@ struct Car { #### BestCarRecord ```rust struct BestCarRecord { - car_rid: String, // Car identifier - brand: String, // Manufacturer - only_name: String, // Model name - year: u16, // Year - rq: u8, // RQ level - config: String, // Tune config (111, 233, etc.) - time: f64, // Track time (seconds) - owned: bool, // User ownership status - prize: bool, // Prize car status - guid: String, // Game identifier + car_rid: String, // Car identifier + brand: String, // Manufacturer + only_name: String, // Model name + year: u16, // Year + rq: u8, // RQ level + config: String, // Original tune config from JSON (111, 221, 233, etc.) + normalized_config: String, // Normalized final build (323, 233, or 332) + time: f64, // Track time (seconds) + owned: bool, // User ownership status + prize: bool, // Prize car status + guid: String, // Game identifier } ``` @@ -175,15 +179,17 @@ struct AnalysisContext { ### CarCounts ```rust struct CarCounts { - by_rid: HashMap, - guid_to_rid: HashMap, + by_rid: HashMap<(String, String), (BestCarRecord, usize)>, + guid_to_rid: HashMap>, car_map: HashMap, car_list: Vec, + owned_configs: HashMap, } ``` **Purpose**: Aggregated per-car win counts computed once from `AnalysisContext` **Usage**: Shared across all output functions to avoid redundant computation +**Key grouping**: `by_rid` is keyed by `(car_rid, normalized_config)` so different tune configs of the same car are tracked separately. ## Algorithm Details @@ -241,7 +247,8 @@ Multiple validation layers ensure data quality: ├── trash.csv # Owned cars safe to remove ├── wishlist.csv # Ideal wishlist (very_best unowned, capped) ├── add.csv # Cars to add to wishlist -└── remove.csv # Cars to remove from wishlist +├── remove.csv # Cars to remove from wishlist +└── improve.csv # Owned cars not yet at final build config ``` ## Performance Characteristics diff --git a/docs/DATA_STRUCTURES.md b/docs/DATA_STRUCTURES.md index 71abdc9..4713387 100644 --- a/docs/DATA_STRUCTURES.md +++ b/docs/DATA_STRUCTURES.md @@ -56,11 +56,17 @@ This document describes the JSON data structures used by the Top Drives Records "playerDeck": [ { "cardId": "card_ferrari_f40_1987", - "locked": true + "locked": true, + "engineMajor": 3, + "weightMajor": 2, + "chassisMajor": 3 }, { "cardId": "card_mclaren_f1_1993", - "locked": false + "locked": false, + "engineMajor": 2, + "weightMajor": 3, + "chassisMajor": 3 } ] } @@ -73,11 +79,16 @@ This document describes the JSON data structures used by the Top Drives Records | `playerDeck` | Array | List of user's cards | | `cardId` | String | Unique card identifier | | `locked` | Boolean | Whether card is owned (true = owned) | +| `engineMajor` | Number | Engine tune level (optional) | +| `weightMajor` | Number | Weight tune level (optional) | +| `chassisMajor` | Number | Chassis tune level (optional) | **Processing Logic**: - Only `locked: true` cards are considered owned - `cardId` values are matched against car database GUIDs - Used to mark cars as "owned" in analysis results +- Tune config extracted as `"{engineMajor}{weightMajor}{chassisMajor}"` (e.g., `"323"`) +- Cards with incomplete tune data are skipped for the improve command with a warning ### 3. Individual Car Timing Files (`times/{rid}.json`) @@ -90,7 +101,7 @@ This document describes the JSON data structures used by the Top Drives Records { "users": ["Player1", "Player2"], "data": { - "111": { + "v2c_111": { "times": { "laguna_a00": { "t": 112.45, @@ -246,7 +257,12 @@ let cars: Vec = serde_json::from_str(&content)?; // Parse individual car timing file let json_data: Value = serde_json::from_str(&file_content)?; if let Some(configurations) = json_data["data"].as_object() { - for (config, entry) in configurations { + for (raw_config, entry) in configurations { + // Strip version prefix (e.g., "v2c_" from "v2c_233") + let config = raw_config + .strip_prefix("v2c_") + .or_else(|| raw_config.strip_prefix("v1c_")) + .unwrap_or(raw_config); if let Some(times) = entry["times"].as_object() { for (track_id, details) in times { // Process each track time @@ -290,8 +306,10 @@ BestCarRecord { year: car.year, rq: car.rq, config: config.clone(), + normalized_config: normalize_to_final_build(&config), time, owned: my_cars.contains(&car.guid), + prize: car.prize, guid: car.guid.clone(), } ``` diff --git a/docs/USER_GUIDE.md b/docs/USER_GUIDE.md index 085b776..38a3a57 100644 --- a/docs/USER_GUIDE.md +++ b/docs/USER_GUIDE.md @@ -86,18 +86,18 @@ cargo run --bin records -- best --min-count 5 `best.csv`: ```csv -RQ,Brand,Model,Year,Count -85,McLaren,F1,1993,15 -82,Ferrari,250 GTO,1962,12 -80,Lamborghini,Countach,1974,8 -75,Porsche,911 Turbo,1975,6 +RQ,Brand,Model,Year,FinalBuild,Count +85,McLaren,F1,1993,323,15 +82,Ferrari,250 GTO,1962,332,12 +80,Lamborghini,Countach,1974,233,8 +75,Porsche,911 Turbo,1975,332,6 ``` `very_best.csv`: ```csv -RQ,Brand,Model,Year,Count -85,McLaren,F1,1993,15 -82,Ferrari,250 GTO,1962,12 +RQ,Brand,Model,Year,FinalBuild,Count +85,McLaren,F1,1993,323,15 +82,Ferrari,250 GTO,1962,332,12 ``` **Understanding the output:** @@ -105,6 +105,7 @@ RQ,Brand,Model,Year,Count - **Brand**: Car manufacturer - **Model**: Car model name - **Year**: Model year +- **FinalBuild**: Normalized tune configuration (323, 233, or 332) - **Count**: How many times this car was the fastest across different tracks **Interpretation:** @@ -164,24 +165,24 @@ cargo run --bin records -- wishlist --min-count 5 --max-wishlist 500 `wishlist.csv`: ```csv -RQ,Brand,Model,Year,Count -90,Pagani,Zonda R,2010,18 -88,Ferrari,F40,1987,15 -87,McLaren,Senna,2018,12 +RQ,Brand,Model,Year,FinalBuild,Count +90,Pagani,Zonda R,2010,323,18 +88,Ferrari,F40,1987,332,15 +87,McLaren,Senna,2018,233,12 ``` `add.csv`: ```csv -RQ,Brand,Model,Year,Count -90,Pagani,Zonda R,2010,18 -88,Ferrari,F40,1987,15 +RQ,Brand,Model,Year,FinalBuild,Count +90,Pagani,Zonda R,2010,323,18 +88,Ferrari,F40,1987,332,15 ``` `remove.csv`: ```csv -RQ,Brand,Model,Year,Count -75,BMW,M3,2003,3 -72,Audi,RS6,2002,2 +RQ,Brand,Model,Year,FinalBuild,Count +75,BMW,M3,2003,332,3 +72,Audi,RS6,2002,233,2 ``` **Action items:** @@ -196,7 +197,7 @@ RQ,Brand,Model,Year,Count cargo run --bin records -- all ``` -**Purpose:** Generates all 6 CSV files in a single parse pass. Equivalent to running `best`, `trash`, and `wishlist` together, but more efficient since data is only parsed once. +**Purpose:** Generates all 7 CSV files in a single parse pass. Equivalent to running `best`, `trash`, `wishlist`, and `improve` together, but more efficient since data is only parsed once. **Flags:** - `--min-count N` — Minimum number of tracks for "very best" qualification (default: 10) @@ -214,6 +215,38 @@ cargo run --bin records -- all --min-count 5 --max-wishlist 500 - `wishlist.csv` — Ideal wishlist (very_best unowned, capped) - `add.csv` — Cars to add to wishlist - `remove.csv` — Cars to remove from wishlist +- `improve.csv` — Owned cars not yet at final build config + +#### Improve Analysis + +**Command:** +```bash +cargo run --bin records -- improve +``` + +**Purpose:** Generates `improve.csv` — owned cars whose actual tune configuration is not yet a final build (323, 233, or 332). Sorted by number of best tracks descending, so the most impactful upgrades appear first. + +**Output File:** + +`improve.csv`: +```csv +RQ,Brand,Model,Year,Target,Count +90,Pagani,Zonda R,2010,323,18 +85,McLaren,F1,1993,332,15 +``` + +**Understanding the output:** +- **RQ**: Car's rarity level +- **Brand**: Car manufacturer +- **Model**: Car model name +- **Year**: Model year +- **Target**: The final build config the player should upgrade to +- **Count**: How many tracks this car is best on (across all its configs) + +**Interpretation:** +- Focus on cars with the highest Count first — upgrading them benefits the most tracks +- The Target column shows which of the three final builds (323, 233, 332) to aim for +- Cars already at a final build or maxed config are automatically excluded #### Detailed Performance Data @@ -285,19 +318,20 @@ cargo run --bin records -- fetch && cargo run --bin records -- all ### What `all` Does 1. **Parses** all timing data in a single pass -2. **Generates** all 6 output CSV files -3. **Sorts** all files by car RQ +2. **Generates** all 7 output CSV files +3. **Sorts** all files by car RQ (and FinalBuild where applicable) ### Output Files | File | Format | Description | |------|--------|-------------| -| `best.csv` | `RQ,Brand,Model,Year,Count` | All cars best on at least one track | -| `very_best.csv` | `RQ,Brand,Model,Year,Count` | Cars best on ≥ min_count tracks | +| `best.csv` | `RQ,Brand,Model,Year,FinalBuild,Count` | All cars best on at least one track | +| `very_best.csv` | `RQ,Brand,Model,Year,FinalBuild,Count` | Cars best on ≥ min_count tracks | | `trash.csv` | `RQ,Brand,Model,Year` | Owned cars not best on any track (RQ<80, not prize) | -| `wishlist.csv` | `RQ,Brand,Model,Year,Count` | Ideal wishlist (very_best unowned, capped) | -| `add.csv` | `RQ,Brand,Model,Year,Count` | Cars to add to wishlist | -| `remove.csv` | `RQ,Brand,Model,Year,Count` | Cars to remove from wishlist | +| `wishlist.csv` | `RQ,Brand,Model,Year,FinalBuild,Count` | Ideal wishlist (very_best unowned, capped) | +| `add.csv` | `RQ,Brand,Model,Year,FinalBuild,Count` | Cars to add to wishlist | +| `remove.csv` | `RQ,Brand,Model,Year,FinalBuild,Count` | Cars to remove from wishlist | +| `improve.csv` | `RQ,Brand,Model,Year,Target,Count` | Owned cars not yet at final build config | ### Typical Daily Routine @@ -421,7 +455,7 @@ The tool generates CSV files for external analysis. All files are sorted by car #### "Please provide a command" **Problem:** No arguments provided -**Solution:** Use `best`, `trash`, `wishlist`, `all`, `details`, `track`, or `fetch` +**Solution:** Use `best`, `trash`, `wishlist`, `all`, `details`, `track`, `improve`, or `fetch` #### "Could not read my cars file" / "Missing data file" **Problem:** `my_cars.json` (or another data file) not found or not readable