Added documentation
Added test cases Added comments
This commit is contained in:
parent
7a8d9beedc
commit
32c4d3c10e
10 changed files with 3852 additions and 63 deletions
654
docs/API.md
Normal file
654
docs/API.md
Normal file
|
|
@ -0,0 +1,654 @@
|
|||
# API Documentation
|
||||
|
||||
## Overview
|
||||
|
||||
This document provides comprehensive API documentation for the Top Drives Records Analyzer, including all public functions, data structures, and their usage patterns.
|
||||
|
||||
## Core Modules
|
||||
|
||||
### Records Module (`src/records.rs`)
|
||||
|
||||
Primary analysis engine for processing JSON-based timing data.
|
||||
|
||||
#### Public Functions
|
||||
|
||||
##### `main()`
|
||||
**Entry point for the records analyzer**
|
||||
|
||||
```rust
|
||||
#[tokio::main]
|
||||
async fn main()
|
||||
```
|
||||
|
||||
**Purpose**: Command-line interface and operation dispatcher
|
||||
|
||||
**Arguments**: Command line arguments
|
||||
- `args[1]`: Command (`fetch`, `best`, `missing`, `trash`, `details`, `track`)
|
||||
|
||||
**Commands**:
|
||||
- `fetch`: Download all data files
|
||||
- `best`: Show best owned cars
|
||||
- `missing`: Show best unowned cars
|
||||
- `trash`: Show removable cars
|
||||
- `details`: Export all timing data
|
||||
- `track`: Track-specific analysis
|
||||
|
||||
**Example**:
|
||||
```bash
|
||||
cargo run --bin records -- best
|
||||
```
|
||||
|
||||
##### `print_best_cars(filter_out_owned: bool)`
|
||||
**Displays ranked list of best performing cars**
|
||||
|
||||
```rust
|
||||
fn print_best_cars(filter_out_owned: bool)
|
||||
```
|
||||
|
||||
**Purpose**: Aggregates performance data across all tracks and conditions
|
||||
|
||||
**Arguments**:
|
||||
- `filter_out_owned`: If true, excludes owned cars from output
|
||||
|
||||
**Output Format**:
|
||||
```csv
|
||||
RQ,Brand,Model,Year,Count
|
||||
85,McLaren,F1,1993,15
|
||||
82,Ferrari,250 GTO,1962,12
|
||||
```
|
||||
|
||||
**Algorithm**:
|
||||
1. Count occurrences of each car across all track/RQ/condition combinations
|
||||
2. Sort by RQ level, then by car identifier
|
||||
3. Apply ownership filter if requested
|
||||
|
||||
##### `print_best_times()`
|
||||
**Exports comprehensive timing data for all combinations**
|
||||
|
||||
```rust
|
||||
fn print_best_times()
|
||||
```
|
||||
|
||||
**Purpose**: Detailed performance export for external analysis
|
||||
|
||||
**Output Format**:
|
||||
```csv
|
||||
RQ,TrackID,Condition,CarRQ,Brand,Model,Year,Config,Time
|
||||
85,laguna,Dry,85,McLaren,F1,1993,233,112.45
|
||||
```
|
||||
|
||||
**Data Volume**: Large output file with all track/condition/RQ combinations
|
||||
|
||||
##### `print_best_car(track: &String, condition: &String, rq: &String)`
|
||||
**Finds best car for specific track conditions**
|
||||
|
||||
```rust
|
||||
fn print_best_car(track: &String, condition: &String, rq: &String)
|
||||
```
|
||||
|
||||
**Purpose**: Targeted analysis for specific racing scenarios
|
||||
|
||||
**Arguments**:
|
||||
- `track`: Track identifier (e.g., "laguna", "monaco")
|
||||
- `condition`: Surface condition (e.g., "Dry", "Wet")
|
||||
- `rq`: RQ level limit
|
||||
|
||||
**Example**:
|
||||
```rust
|
||||
print_best_car(&"laguna".to_string(), &"Dry".to_string(), &"85".to_string());
|
||||
```
|
||||
|
||||
##### `print_trash()`
|
||||
**Identifies cars safe for removal from collection**
|
||||
|
||||
```rust
|
||||
fn print_trash()
|
||||
```
|
||||
|
||||
**Purpose**: Collection optimization recommendations
|
||||
|
||||
**Safety Criteria**:
|
||||
- Car not among best performers on any track
|
||||
- RQ level below 80 (preserves high-end cars)
|
||||
- Not a prize car (may have collection value)
|
||||
|
||||
**Output Format**:
|
||||
```csv
|
||||
RQ,Brand,Model,Year
|
||||
45,Toyota,Corolla,1995
|
||||
```
|
||||
|
||||
##### `parse_car_times(car: Car)`
|
||||
**Processes timing data for a single car**
|
||||
|
||||
```rust
|
||||
async fn parse_car_times(car: Car)
|
||||
```
|
||||
|
||||
**Purpose**: Core parsing logic for individual car performance data
|
||||
|
||||
**Process Flow**:
|
||||
1. Load car's JSON timing file
|
||||
2. Parse each configuration (111, 233, 323, etc.)
|
||||
3. Extract timing data for each track/condition
|
||||
4. Apply quality filters (downvotes, time validation)
|
||||
5. Update global best cars cache
|
||||
|
||||
**Quality Filters**:
|
||||
- Minimum time: 1.2 seconds
|
||||
- No downvotes from community
|
||||
- Special handling for Test Bowl track
|
||||
|
||||
##### `parse_all_times()`
|
||||
**Orchestrates concurrent processing of all cars**
|
||||
|
||||
```rust
|
||||
async fn parse_all_times()
|
||||
```
|
||||
|
||||
**Purpose**: Coordinates parallel processing of car timing files
|
||||
|
||||
**Concurrency**: One async task per car file
|
||||
**Synchronization**: Waits for all tasks to complete
|
||||
|
||||
##### `update_best_car(track_id: &str, car_rq: u8, surface: &str, new_record: BestCarRecord)`
|
||||
**Updates global best cars database with new performance data**
|
||||
|
||||
```rust
|
||||
fn update_best_car(track_id: &str, car_rq: u8, surface: &str, new_record: BestCarRecord)
|
||||
```
|
||||
|
||||
**Purpose**: Implements core best-car selection algorithm
|
||||
|
||||
**RQ Range Analysis**:
|
||||
- Tests car from base RQ up to maximum (115)
|
||||
- Compares against existing best times
|
||||
- Updates records for improved performance
|
||||
|
||||
**Logic**:
|
||||
1. For each RQ level from car_rq to 115:
|
||||
2. If new time is better → replace record
|
||||
3. If new time ties → add to list (if not duplicate)
|
||||
4. If new time is worse → ignore
|
||||
|
||||
##### `get_car_list() -> Vec<Car>`
|
||||
**Loads complete car database from repository file**
|
||||
|
||||
```rust
|
||||
fn get_car_list() -> Vec<Car>
|
||||
```
|
||||
|
||||
**Purpose**: Provides access to all known cars in the database
|
||||
|
||||
**Source**: `cars_repo.json` file
|
||||
**Returns**: Vector of all cars with complete metadata
|
||||
|
||||
##### `get_my_car_list() -> Vec<String>`
|
||||
**Loads user's car collection from game export**
|
||||
|
||||
```rust
|
||||
fn get_my_car_list() -> Vec<String>
|
||||
```
|
||||
|
||||
**Purpose**: Identifies which cars the user owns
|
||||
|
||||
**Source**: `my_cars.json` game export
|
||||
**Filter**: Only `locked: true` cards
|
||||
**Returns**: Vector of owned car GUIDs
|
||||
|
||||
##### `fetch_times()`
|
||||
**Downloads timing files for all cars with rate limiting**
|
||||
|
||||
```rust
|
||||
async fn fetch_times()
|
||||
```
|
||||
|
||||
**Purpose**: Batch download of car timing data
|
||||
|
||||
**Rate Limiting**: 20 concurrent downloads maximum
|
||||
**Progress**: Shows download progress for each file
|
||||
|
||||
##### `fetch_car(rid: String)`
|
||||
**Downloads timing data for single car**
|
||||
|
||||
```rust
|
||||
async fn fetch_car(rid: String)
|
||||
```
|
||||
|
||||
**Purpose**: Individual car data fetching
|
||||
|
||||
**URL Format**: `https://api.topdrivesrecords.com/car/{rid}`
|
||||
**Storage**: `times/{rid}.json`
|
||||
|
||||
##### `get_cars_file()`
|
||||
**Downloads main car database**
|
||||
|
||||
```rust
|
||||
async fn get_cars_file()
|
||||
```
|
||||
|
||||
**Purpose**: Updates car metadata database
|
||||
|
||||
**Source**: GitHub repository cars database
|
||||
**Destination**: `cars_repo.json`
|
||||
|
||||
##### `get_tracks_file()`
|
||||
**Downloads track definitions**
|
||||
|
||||
```rust
|
||||
async fn get_tracks_file()
|
||||
```
|
||||
|
||||
**Purpose**: Updates track name mappings
|
||||
|
||||
**Source**: GitHub repository tracks database
|
||||
**Destination**: `tracks_repo.json`
|
||||
|
||||
##### `get_file(file_url: &str, local_file_path: &str)`
|
||||
**Generic file download utility**
|
||||
|
||||
```rust
|
||||
async fn get_file(file_url: &str, local_file_path: &str)
|
||||
```
|
||||
|
||||
**Purpose**: Downloads files from remote URLs
|
||||
|
||||
**Features**:
|
||||
- Automatic existing file removal
|
||||
- Error handling with graceful failure
|
||||
- Progress indication through errors
|
||||
|
||||
##### `translate_track_name(track_code: &str) -> &str`
|
||||
**Converts track codes to human-readable names**
|
||||
|
||||
```rust
|
||||
fn translate_track_name(track_code: &str) -> &str
|
||||
```
|
||||
|
||||
**Purpose**: User-friendly track name display
|
||||
|
||||
**Coverage**: 200+ track mappings
|
||||
**Fallback**: Returns original code if not found
|
||||
|
||||
##### `translate_track_type(track_code: &str) -> &str`
|
||||
**Converts condition codes to surface descriptions**
|
||||
|
||||
```rust
|
||||
fn translate_track_type(track_code: &str) -> &str
|
||||
```
|
||||
|
||||
**Purpose**: Human-readable surface condition names
|
||||
|
||||
**Mappings**:
|
||||
- `"00"` → `"Dry"`
|
||||
- `"01"` → `"Wet"`
|
||||
- `"10"` → `"Dirt"`
|
||||
- `"11"` → `"Dirt Wet"`
|
||||
|
||||
##### `split_prefix_postfix<'a>(input: &'a str, delimiter: &'a str) -> Option<(&'a str, &'a str)>`
|
||||
**String splitting utility for track ID parsing**
|
||||
|
||||
```rust
|
||||
fn split_prefix_postfix<'a>(input: &'a str, delimiter: &'a str) -> Option<(&'a str, &'a str)>
|
||||
```
|
||||
|
||||
**Purpose**: Parse track IDs with condition suffixes
|
||||
|
||||
**Example**:
|
||||
```rust
|
||||
split_prefix_postfix("laguna_a00", "_a") // Some(("laguna", "00"))
|
||||
```
|
||||
|
||||
### Club Module (`src/club.rs`)
|
||||
|
||||
Alternative data source using web scraping from topdrives.club.
|
||||
|
||||
#### Public Functions
|
||||
|
||||
##### `main()`
|
||||
**Entry point for club scraper**
|
||||
|
||||
```rust
|
||||
#[tokio::main]
|
||||
async fn main()
|
||||
```
|
||||
|
||||
**Purpose**: Web scraping interface for live data
|
||||
|
||||
**Arguments**:
|
||||
- Command line arguments for RQ and track filtering
|
||||
- `-d` flag for detailed output
|
||||
|
||||
##### `get_page(url: &str) -> String`
|
||||
**Fetches HTML content from URLs**
|
||||
|
||||
```rust
|
||||
async fn get_page(url: &str) -> String
|
||||
```
|
||||
|
||||
**Purpose**: Downloads web pages for scraping
|
||||
|
||||
**Security**: Accepts invalid SSL certificates for compatibility
|
||||
|
||||
##### `parse_best_car(text: String) -> Car`
|
||||
**Extracts car data from HTML content**
|
||||
|
||||
```rust
|
||||
fn parse_best_car(text: String) -> Car
|
||||
```
|
||||
|
||||
**Purpose**: Parses HTML tables to extract car information
|
||||
|
||||
**CSS Selectors**:
|
||||
- `.carList__item-manufacturer`: Car brand
|
||||
- `.carList__item-model`: Car model
|
||||
- `.carList__item-year`: Model year
|
||||
- `.carList__item-level`: Class and RQ (complex parsing)
|
||||
- `.carList__item-upgrade`: Upgrade configuration
|
||||
|
||||
##### `parse_tracks(file_name: &str) -> HashMap<u16, String>`
|
||||
**Loads track definitions from file**
|
||||
|
||||
```rust
|
||||
fn parse_tracks(file_name: &str) -> HashMap<u16, String>
|
||||
```
|
||||
|
||||
**Purpose**: Creates track ID to name mappings
|
||||
|
||||
**Format**: Text file with `ID NAME` format per line
|
||||
|
||||
##### `get_best_car(track: u16, track_name: String, max_level: u16, detailed: bool) -> String`
|
||||
**Scrapes best car data for specific track**
|
||||
|
||||
```rust
|
||||
async fn get_best_car(track: u16, track_name: String, max_level: u16, detailed: bool) -> String
|
||||
```
|
||||
|
||||
**Purpose**: Fetches and parses best car from topdrives.club
|
||||
|
||||
**API Integration**:
|
||||
- Constructs topdrives.club API URLs
|
||||
- Parses HTML response tables
|
||||
- Formats output based on detail level
|
||||
|
||||
##### `parse_argument(args: &Vec<String>, index: usize, min: u16, max: u16) -> u16`
|
||||
**Validates command line arguments**
|
||||
|
||||
```rust
|
||||
fn parse_argument(args: &Vec<String>, index: usize, min: u16, max: u16) -> u16
|
||||
```
|
||||
|
||||
**Purpose**: Input validation with range checking
|
||||
|
||||
**Panics**: On invalid values with descriptive messages
|
||||
|
||||
## Data Structures
|
||||
|
||||
### Records Module Data Types
|
||||
|
||||
#### `Card`
|
||||
**User's car collection entry**
|
||||
|
||||
```rust
|
||||
struct Card {
|
||||
cardId: String, // Unique card identifier
|
||||
locked: bool, // Ownership status
|
||||
}
|
||||
```
|
||||
|
||||
#### `Car`
|
||||
**Car metadata from database**
|
||||
|
||||
```rust
|
||||
struct Car {
|
||||
rid: String, // Repository ID
|
||||
brand: String, // Manufacturer
|
||||
only_name: String, // Model name
|
||||
year: u16, // Model year
|
||||
rq: u8, // Rarity level
|
||||
prize: bool, // Prize car status
|
||||
guid: String, // Game GUID
|
||||
}
|
||||
```
|
||||
|
||||
#### `BestCarRecord`
|
||||
**Performance record for best cars**
|
||||
|
||||
```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 configuration
|
||||
time: f64, // Track time
|
||||
owned: bool, // User ownership
|
||||
guid: String, // Game identifier
|
||||
}
|
||||
```
|
||||
|
||||
#### `TripletKey`
|
||||
**Composite key for track conditions**
|
||||
|
||||
```rust
|
||||
type TripletKey = (String, u8, String);
|
||||
// Track ID RQ Level Surface Condition
|
||||
```
|
||||
|
||||
### Club Module Data Types
|
||||
|
||||
#### `Car`
|
||||
**Scraped car data from website**
|
||||
|
||||
```rust
|
||||
struct Car {
|
||||
class: String, // Class letter (S, A, B, etc.)
|
||||
rq: u8, // Rarity level
|
||||
manufacturer: String, // Brand name
|
||||
model: String, // Model name
|
||||
year: u16, // Model year
|
||||
upgrade: String, // Upgrade config
|
||||
}
|
||||
```
|
||||
|
||||
## Global State
|
||||
|
||||
### `BEST_CARS` Cache
|
||||
**Thread-safe global performance database**
|
||||
|
||||
```rust
|
||||
static BEST_CARS: Lazy<Mutex<HashMap<TripletKey, Vec<BestCarRecord>>>>
|
||||
```
|
||||
|
||||
**Initialization**: One-time setup with `once_cell::Lazy`
|
||||
**Thread Safety**: Protected by `Mutex` for concurrent access
|
||||
**Structure**: HashMap with composite keys for efficient lookups
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Network Errors
|
||||
- **HTTP Failures**: Logged but don't crash application
|
||||
- **Invalid JSON**: Graceful fallback to defaults
|
||||
- **Missing Files**: Clear error messages for user
|
||||
|
||||
### Parse Errors
|
||||
- **Malformed Data**: Skip invalid entries
|
||||
- **Type Conversion**: Use safe defaults
|
||||
- **Missing Fields**: Provide sensible fallbacks
|
||||
|
||||
### User Input Errors
|
||||
- **Invalid Commands**: Clear usage instructions
|
||||
- **Missing Arguments**: Descriptive error messages
|
||||
- **Out of Range**: Validation with helpful feedback
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Basic Analysis Workflow
|
||||
|
||||
```rust
|
||||
// 1. Load user's car collection
|
||||
let my_cars = get_my_car_list();
|
||||
|
||||
// 2. Process all car timing data
|
||||
parse_all_times().await;
|
||||
|
||||
// 3. Generate best cars report
|
||||
print_best_cars(false);
|
||||
|
||||
// 4. Find collection optimization opportunities
|
||||
print_trash();
|
||||
```
|
||||
|
||||
### Track-Specific Analysis
|
||||
|
||||
```rust
|
||||
// Analyze specific track conditions
|
||||
let track = "laguna".to_string();
|
||||
let condition = "Dry".to_string();
|
||||
let rq = "85".to_string();
|
||||
|
||||
print_best_car(&track, &condition, &rq);
|
||||
```
|
||||
|
||||
### Data Fetching
|
||||
|
||||
```rust
|
||||
// Download fresh data
|
||||
get_cars_file().await;
|
||||
get_tracks_file().await;
|
||||
fetch_times().await;
|
||||
```
|
||||
|
||||
## Performance Characteristics
|
||||
|
||||
### Time Complexity
|
||||
- **Parsing**: O(n) where n = number of cars × tracks
|
||||
- **Best Car Selection**: O(1) HashMap operations
|
||||
- **Sorting**: O(n log n) for output ordering
|
||||
|
||||
### Memory Usage
|
||||
- **Peak**: ~200-300MB during full analysis
|
||||
- **Baseline**: ~50MB application overhead
|
||||
- **Per Car**: ~1-2KB metadata + timing data
|
||||
|
||||
### Concurrency
|
||||
- **Downloads**: 20 concurrent connections max
|
||||
- **Processing**: One task per car file
|
||||
- **Thread Safety**: Mutex-protected shared state
|
||||
|
||||
## Extension Points
|
||||
|
||||
### Adding New Commands
|
||||
1. Add command parsing in `main()`
|
||||
2. Implement analysis function
|
||||
3. Update help documentation
|
||||
|
||||
### Adding New Data Sources
|
||||
1. Implement new module (similar to `club.rs`)
|
||||
2. Add data source selection logic
|
||||
3. Update configuration system
|
||||
|
||||
### Custom Analysis Types
|
||||
1. Create new output formatting functions
|
||||
2. Add aggregation algorithms
|
||||
3. Extend data structures as needed
|
||||
|
||||
## Testing
|
||||
|
||||
### Unit Tests
|
||||
```rust
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_parse_car_times() {
|
||||
// Test implementation
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Integration Tests
|
||||
- Full workflow testing
|
||||
- File I/O validation
|
||||
- Network request mocking
|
||||
|
||||
### Benchmarking
|
||||
- Parse performance measurement
|
||||
- Memory usage profiling
|
||||
- Concurrent operation testing
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### Network Security
|
||||
- HTTPS-only external requests
|
||||
- Certificate validation (with fallback)
|
||||
- No sensitive data transmission
|
||||
|
||||
### File System Security
|
||||
- Read-only access to game files
|
||||
- No modification of user data
|
||||
- Safe file path handling
|
||||
|
||||
### Input Validation
|
||||
- Command injection prevention
|
||||
- Path traversal protection
|
||||
- Input sanitization
|
||||
|
||||
## Troubleshooting API Issues
|
||||
|
||||
### Common Problems
|
||||
|
||||
**"Function not found"**
|
||||
- Check module imports
|
||||
- Verify function signatures
|
||||
- Ensure proper visibility
|
||||
|
||||
**"Type mismatch"**
|
||||
- Review struct field types
|
||||
- Check function signatures
|
||||
- Verify generic parameters
|
||||
|
||||
**"Deadlock"**
|
||||
- Avoid nested Mutex locks
|
||||
- Check lock ordering
|
||||
- Use timeout where possible
|
||||
|
||||
### Debug Strategies
|
||||
|
||||
**Logging**:
|
||||
```rust
|
||||
println!("Processing car: {}", car.rid);
|
||||
eprintln!("Error: {}", error);
|
||||
```
|
||||
|
||||
**Tracing**:
|
||||
```rust
|
||||
// Add at function entry
|
||||
let _span = tracing::span!(Level::INFO, "function_name");
|
||||
let _enter = _span.enter();
|
||||
```
|
||||
|
||||
**Profiling**:
|
||||
```bash
|
||||
cargo flamegraph --bin records -- best
|
||||
```
|
||||
|
||||
## Migration Guide
|
||||
|
||||
### Version Compatibility
|
||||
- **Current**: 0.2.0 API stable
|
||||
- **Previous**: 0.1.0 fully compatible
|
||||
- **Future**: Semantic versioning maintained
|
||||
|
||||
### Breaking Changes
|
||||
- None currently planned
|
||||
- Advance notice provided for API changes
|
||||
- Deprecation warnings for removed features
|
||||
|
||||
---
|
||||
|
||||
**API Version**: 0.2.0
|
||||
**Last Updated**: 2024-12-XX
|
||||
**Compatibility**: Rust 1.70.0+
|
||||
242
docs/ARCHITECTURE.md
Normal file
242
docs/ARCHITECTURE.md
Normal file
|
|
@ -0,0 +1,242 @@
|
|||
# Architecture Documentation
|
||||
|
||||
## System Overview
|
||||
|
||||
The Top Drives Records Analyzer consists of two main components working together to provide comprehensive car performance analysis for the Top Drives racing game.
|
||||
|
||||
## Component Architecture
|
||||
|
||||
### 1. Records Module (`src/records.rs`)
|
||||
|
||||
**Primary analysis engine** that processes pre-downloaded JSON timing data.
|
||||
|
||||
#### Key Responsibilities
|
||||
- **Data Fetching**: Downloads car and track databases from GitHub repository
|
||||
- **File Processing**: Parses JSON timing files for each car configuration
|
||||
- **Performance Analysis**: Compares timing data across RQ ranges and track conditions
|
||||
- **Collection Management**: Identifies owned cars and optimization opportunities
|
||||
|
||||
#### Data Flow
|
||||
|
||||
```
|
||||
GitHub Repository
|
||||
↓
|
||||
cars_repo.json + tracks_repo.json
|
||||
↓
|
||||
times/*.json (1000+ car files)
|
||||
↓
|
||||
JSON Parsing → BestCarRecord Analysis
|
||||
↓
|
||||
User Collection (my_cars.json)
|
||||
↓
|
||||
Output: CSV/Terminal Results
|
||||
```
|
||||
|
||||
### 2. Club Module (`src/club.rs`)
|
||||
|
||||
**Alternative data source** that scrapes live data from topdrives.club website.
|
||||
|
||||
#### Key Responsibilities
|
||||
- **Web Scraping**: Fetches HTML content from topdrives.club
|
||||
- **HTML Parsing**: Extracts car performance data using CSS selectors
|
||||
- **Real-time Data**: Provides fresh data without pre-downloaded files
|
||||
|
||||
#### Data Flow
|
||||
|
||||
```
|
||||
topdrives.club Website
|
||||
↓
|
||||
HTML Content
|
||||
↓
|
||||
CSS Selector Parsing
|
||||
↓
|
||||
Car Data Extraction
|
||||
↓
|
||||
Output: Terminal Results
|
||||
```
|
||||
|
||||
## Data Structures
|
||||
|
||||
### Core Types
|
||||
|
||||
#### TripletKey
|
||||
```rust
|
||||
type TripletKey = (String, u8, String);
|
||||
// ↓ ↓ ↓
|
||||
// Track ID RQ Level Surface Condition
|
||||
```
|
||||
|
||||
**Purpose**: Composite key for uniquely identifying track conditions
|
||||
**Usage**: HashMap key for storing best car records
|
||||
|
||||
#### Car (Records Module)
|
||||
```rust
|
||||
struct Car {
|
||||
rid: String, // Repository ID
|
||||
brand: String, // Manufacturer
|
||||
only_name: String, // Model name
|
||||
year: u16, // Model year
|
||||
rq: u8, // Rarity level
|
||||
prize: bool, // Prize car status
|
||||
guid: String, // Game GUID
|
||||
}
|
||||
```
|
||||
|
||||
#### 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
|
||||
guid: String, // Game identifier
|
||||
}
|
||||
```
|
||||
|
||||
#### Car (Club Module)
|
||||
```rust
|
||||
struct Car {
|
||||
class: String, // Class letter (S, A, B, etc.)
|
||||
rq: u8, // Rarity level
|
||||
manufacturer: String, // Brand
|
||||
model: String, // Model
|
||||
year: u16, // Year
|
||||
upgrade: String, // Upgrade config
|
||||
}
|
||||
```
|
||||
|
||||
## Global State Management
|
||||
|
||||
### BEST_CARS Cache
|
||||
```rust
|
||||
static BEST_CARS: Lazy<Mutex<HashMap<TripletKey, Vec<BestCarRecord>>>>
|
||||
```
|
||||
|
||||
**Thread Safety**: Protected by `Mutex` for concurrent access during parsing
|
||||
**Initialization**: Uses `once_cell::Lazy` for one-time setup
|
||||
**Structure**: HashMap with composite keys for efficient lookups
|
||||
|
||||
## Algorithm Details
|
||||
|
||||
### RQ Range Analysis
|
||||
|
||||
The system analyzes car performance across multiple RQ brackets:
|
||||
|
||||
1. **Base RQ**: Car's natural rarity level
|
||||
2. **RQ Range**: Tests from car's RQ up to maximum (115)
|
||||
3. **Cross-RQ Competition**: Allows cars to compete above their base level
|
||||
|
||||
**Example**: A RQ 85 car might perform well at RQ 90-95 levels against higher-rated cars
|
||||
|
||||
### Time Validation
|
||||
|
||||
Multiple validation layers ensure data quality:
|
||||
|
||||
1. **Minimum Time**: Times below 1.2 seconds are discarded
|
||||
2. **Downvote Filter**: Community-downvoted times are excluded
|
||||
3. **Special Cases**: Test Bowl track uses inverted time calculation (2000.0 / time)
|
||||
|
||||
### Concurrent Processing
|
||||
|
||||
#### Records Module
|
||||
- **Task Spawning**: One async task per car file
|
||||
- **Rate Limiting**: Semaphore limits concurrent downloads (20 max)
|
||||
- **Error Isolation**: Individual car failures don't stop batch processing
|
||||
|
||||
#### Synchronization
|
||||
- **Join Handles**: Waits for all parsing tasks to complete
|
||||
- **Mutex Protection**: Safe concurrent access to shared BEST_CARS cache
|
||||
|
||||
## File Organization
|
||||
|
||||
### Input Files
|
||||
```
|
||||
├── cars_repo.json # Car database from repository
|
||||
├── tracks_repo.json # Track definitions
|
||||
├── my_cars.json # User's car collection (game export)
|
||||
└── times/ # Individual car timing files
|
||||
├── Ferrari_F40_1987.json
|
||||
├── McLaren_F1_1993.json
|
||||
└── ...
|
||||
```
|
||||
|
||||
### Output Files
|
||||
```
|
||||
├── best.csv # Best cars analysis results
|
||||
├── missing.csv # Missing recommended cars
|
||||
└── details.csv # Detailed timing data
|
||||
```
|
||||
|
||||
## Performance Characteristics
|
||||
|
||||
### Records Module
|
||||
- **Startup Time**: Fast (uses cached JSON files)
|
||||
- **Memory Usage**: Moderate (loads all timing data into memory)
|
||||
- **Accuracy**: High (comprehensive dataset)
|
||||
- **Update Frequency**: Manual refresh required
|
||||
|
||||
### Club Module
|
||||
- **Startup Time**: Slow (web scraping required)
|
||||
- **Memory Usage**: Low (processes one track at a time)
|
||||
- **Accuracy**: Variable (depends on website structure)
|
||||
- **Update Frequency**: Real-time on each run
|
||||
|
||||
## Error Handling Strategy
|
||||
|
||||
### Graceful Degradation
|
||||
- **Missing Files**: Continue with available data
|
||||
- **Network Errors**: Log errors but don't crash
|
||||
- **Parse Failures**: Use default values or skip invalid entries
|
||||
|
||||
### User Feedback
|
||||
- **Progress Indication**: Shows fetching/download progress
|
||||
- **Error Messages**: Clear descriptions of what went wrong
|
||||
- **Fallback Options**: Multiple data sources available
|
||||
|
||||
## Extension Points
|
||||
|
||||
### Adding New Tracks
|
||||
1. Update `tracks_repo.json` with new track definitions
|
||||
2. Add track name mappings in `translate_track_name()`
|
||||
3. Add condition mappings in `translate_track_type()`
|
||||
|
||||
### Adding New Analysis Types
|
||||
1. Create new output functions (similar to `print_best_cars()`)
|
||||
2. Add command parsing in main function
|
||||
3. Update help text and documentation
|
||||
|
||||
### Custom Data Sources
|
||||
1. Implement new scraping module (similar to club.rs)
|
||||
2. Add data source selection logic
|
||||
3. Update configuration system
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### Network Access
|
||||
- **HTTPS Only**: All external requests use HTTPS
|
||||
- **Certificate Validation**: Accepts invalid certificates for compatibility
|
||||
- **Rate Limiting**: Respects server limits with controlled concurrency
|
||||
|
||||
### Local File Access
|
||||
- **Read Only**: No modification of game files
|
||||
- **User Data**: Only reads exported collection data
|
||||
- **No Persistence**: Analysis results are output-only
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
### Potential Improvements
|
||||
1. **Caching Layer**: Cache web scraping results
|
||||
2. **Database Backend**: Replace in-memory HashMap with persistent storage
|
||||
3. **Configuration Files**: External configuration for analysis parameters
|
||||
4. **GUI Interface**: Web-based interface for non-technical users
|
||||
5. **Export Formats**: Multiple output formats (JSON, XML, etc.)
|
||||
|
||||
### Scalability Considerations
|
||||
1. **Pagination**: Handle large datasets with pagination
|
||||
2. **Incremental Updates**: Only fetch changed data
|
||||
3. **Compression**: Compress large timing files
|
||||
4. **Streaming**: Process data without loading everything into memory
|
||||
407
docs/DATA_STRUCTURES.md
Normal file
407
docs/DATA_STRUCTURES.md
Normal file
|
|
@ -0,0 +1,407 @@
|
|||
# JSON Data Structures Documentation
|
||||
|
||||
## Overview
|
||||
|
||||
This document describes the JSON data structures used by the Top Drives Records Analyzer, including their purpose, format, and usage within the application.
|
||||
|
||||
## Core Data Files
|
||||
|
||||
### 1. Car Database (`cars_repo.json`)
|
||||
|
||||
**Purpose**: Master database of all cars in Top Drives with their metadata.
|
||||
|
||||
**Source**: Downloaded from `https://github.com/TiagoXavi/topdrives-records/raw/refs/heads/main/src/database/cars_final.json`
|
||||
|
||||
**Structure**:
|
||||
```json
|
||||
[
|
||||
{
|
||||
"rid": "Ferrari_F40_1987",
|
||||
"brand": "Ferrari",
|
||||
"onlyName": "F40",
|
||||
"year": 1987,
|
||||
"rq": 95,
|
||||
"prize": false,
|
||||
"guid": "card_ferrari_f40_1987"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
**Field Descriptions**:
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `rid` | String | Repository identifier (unique) |
|
||||
| `brand` | String | Car manufacturer/brand name |
|
||||
| `onlyName` | String | Car model name (without brand) |
|
||||
| `year` | Number | Model year |
|
||||
| `rq` | Number | Rarity level (10-113) |
|
||||
| `prize` | Boolean | Whether car is prize-only |
|
||||
| `guid` | String | Game-specific card identifier |
|
||||
|
||||
**Usage in Application**:
|
||||
- Loaded by `get_car_list()` function
|
||||
- Used to identify cars for timing file processing
|
||||
- Provides metadata for analysis output
|
||||
|
||||
### 2. User's Car Collection (`my_cars.json`)
|
||||
|
||||
**Purpose**: Contains the user's owned cars exported from the Top Drives game.
|
||||
|
||||
**Source**: Exported from Top Drives game (Settings → Account → Export Data)
|
||||
|
||||
**Structure**:
|
||||
```json
|
||||
{
|
||||
"playerDeck": [
|
||||
{
|
||||
"cardId": "card_ferrari_f40_1987",
|
||||
"locked": true
|
||||
},
|
||||
{
|
||||
"cardId": "card_mclaren_f1_1993",
|
||||
"locked": false
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Field Descriptions**:
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `playerDeck` | Array | List of user's cards |
|
||||
| `cardId` | String | Unique card identifier |
|
||||
| `locked` | Boolean | Whether card is owned (true = owned) |
|
||||
|
||||
**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
|
||||
|
||||
### 3. Individual Car Timing Files (`times/{rid}.json`)
|
||||
|
||||
**Purpose**: Contains detailed timing data for a specific car across all tracks and conditions.
|
||||
|
||||
**Source**: Downloaded from `https://api.topdrivesrecords.com/car/{rid}`
|
||||
|
||||
**Example Structure**:
|
||||
```json
|
||||
{
|
||||
"users": ["Player1", "Player2"],
|
||||
"data": {
|
||||
"111": {
|
||||
"times": {
|
||||
"laguna_a00": {
|
||||
"t": 112.45,
|
||||
"u": "Player1"
|
||||
},
|
||||
"monaco_a00": {
|
||||
"t": 45.23,
|
||||
"u": "Player2",
|
||||
"down": ["Player3"]
|
||||
}
|
||||
},
|
||||
"info": {
|
||||
"topSpeed": "201",
|
||||
"acel": "3.2",
|
||||
"hand": "92"
|
||||
}
|
||||
}
|
||||
},
|
||||
"rid": "Ferrari_F40_1987"
|
||||
}
|
||||
```
|
||||
|
||||
**Field Descriptions**:
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `users` | Array | List of players who submitted times |
|
||||
| `data` | Object | Timing data organized by configuration |
|
||||
| `rid` | String | Car repository identifier |
|
||||
|
||||
**Configuration Structure** (`data.{config}`):
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `times` | Object | Track timing data |
|
||||
| `info` | Object | Car performance stats |
|
||||
|
||||
**Track Timing Structure** (`times.{track}_{condition}`):
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `t` | Number | Time in seconds |
|
||||
| `u` | String | Username who submitted the time |
|
||||
| `down` | Array | Usernames who downvoted this time (optional) |
|
||||
| `d` | String | Date/time of submission (optional) |
|
||||
|
||||
**Usage in Application**:
|
||||
- Processed by `parse_car_times()` function
|
||||
- Each track entry becomes a `BestCarRecord`
|
||||
- Downvoted times are filtered out for quality
|
||||
- Times below 1.2 seconds are considered invalid
|
||||
|
||||
### 4. Tracks Database (`tracks_repo.json`)
|
||||
|
||||
**Purpose**: Defines all tracks and their properties in Top Drives.
|
||||
|
||||
**Source**: Downloaded from `https://github.com/TiagoXavi/topdrives-records/raw/refs/heads/main/src/database/tracks_repo.json`
|
||||
|
||||
**Example Structure**:
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Laguna Seca",
|
||||
"conditions": ["00", "01", "10"],
|
||||
"type": "circuit"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
**Field Descriptions**:
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `id` | Number | Unique track identifier |
|
||||
| `name` | String | Human-readable track name |
|
||||
| `conditions` | Array | Supported surface conditions |
|
||||
| `type` | String | Track category (circuit, drag, rally, etc.) |
|
||||
|
||||
**Usage in Application**:
|
||||
- Provides track name mappings for output
|
||||
- Used for track-specific analysis queries
|
||||
- Supports condition filtering
|
||||
|
||||
## Data Processing Pipeline
|
||||
|
||||
### 1. Data Acquisition Phase
|
||||
|
||||
**Files Downloaded:**
|
||||
1. `cars_repo.json` - Complete car database
|
||||
2. `tracks_repo.json` - Track definitions
|
||||
3. `times/{rid}.json` - Individual car timing data (1000+ files)
|
||||
|
||||
**Validation:**
|
||||
- HTTP response codes checked
|
||||
- JSON parsing errors handled gracefully
|
||||
- File corruption detection
|
||||
|
||||
### 2. Data Parsing Phase
|
||||
|
||||
**Car Database Processing:**
|
||||
```rust
|
||||
// Parse cars_repo.json
|
||||
let cars_data: Vec<Value> = serde_json::from_str(&file_content)?;
|
||||
for car in cars_data {
|
||||
let car = Car {
|
||||
rid: car["rid"].as_str().unwrap_or(""),
|
||||
brand: car["brand"].as_str().unwrap_or(""),
|
||||
// ... other fields
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
**Timing Data Processing:**
|
||||
```rust
|
||||
// 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 {
|
||||
if let Some(times) = entry["times"].as_object() {
|
||||
for (track_id, details) in times {
|
||||
// Process each track time
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Data Transformation Phase
|
||||
|
||||
**Track ID Parsing:**
|
||||
- Input: `"laguna_a00"` (track + condition)
|
||||
- Process: Split on `"_a"` delimiter
|
||||
- Output: `("laguna", "00")` (track name, condition code)
|
||||
|
||||
**Condition Code Translation:**
|
||||
- `"00"` → `"Dry"`
|
||||
- `"01"` → `"Wet"`
|
||||
- `"10"` → `"Dirt"`
|
||||
- `"11"` → `"Dirt Wet"`
|
||||
|
||||
**Time Validation:**
|
||||
1. Check for downvotes: `details["down"].as_array()`
|
||||
2. Validate time range: `time > 1.2`
|
||||
3. Special handling: Test Bowl inversion `2000.0 / time`
|
||||
|
||||
### 4. Data Aggregation Phase
|
||||
|
||||
**Best Car Records Creation:**
|
||||
```rust
|
||||
BestCarRecord {
|
||||
car_rid: car.rid.clone(),
|
||||
brand: car.brand.clone(),
|
||||
only_name: car.only_name.clone(),
|
||||
year: car.year,
|
||||
rq: car.rq,
|
||||
config: config.clone(),
|
||||
time,
|
||||
owned: my_cars.contains(&car.guid),
|
||||
guid: car.guid.clone(),
|
||||
}
|
||||
```
|
||||
|
||||
**RQ Range Analysis:**
|
||||
- Test each car from its base RQ up to 115
|
||||
- Compare against existing best times
|
||||
- Update records if better performance found
|
||||
|
||||
## Data Quality Measures
|
||||
|
||||
### Validation Rules
|
||||
|
||||
1. **Time Thresholds**:
|
||||
- Minimum time: 1.2 seconds
|
||||
- Test Bowl: Inverted calculation (2000.0 / time)
|
||||
|
||||
2. **Community Validation**:
|
||||
- Downvoted times excluded
|
||||
- Multiple submissions required for reliability
|
||||
|
||||
3. **Data Consistency**:
|
||||
- Track name normalization
|
||||
- Condition code standardization
|
||||
- Car identifier validation
|
||||
|
||||
### Error Handling
|
||||
|
||||
**Network Errors**:
|
||||
- Failed downloads logged but don't stop processing
|
||||
- Existing cached data used as fallback
|
||||
- Clear error messages for user
|
||||
|
||||
**Parse Errors**:
|
||||
- Malformed JSON handled gracefully
|
||||
- Missing fields use default values
|
||||
- Invalid times filtered out
|
||||
|
||||
**File System Errors**:
|
||||
- Missing files reported clearly
|
||||
- Permission issues identified
|
||||
- Corrupt files detected and skipped
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
### Memory Usage
|
||||
|
||||
**Large Data Structures**:
|
||||
- `BEST_CARS` HashMap: Contains all best records across RQ ranges
|
||||
- Individual timing files: Loaded and parsed sequentially
|
||||
- User collection: Small, loaded once
|
||||
|
||||
**Optimization Strategies**:
|
||||
- Concurrent processing of car files
|
||||
- Rate-limited downloads (20 concurrent max)
|
||||
- Streaming JSON parsing where possible
|
||||
|
||||
### Processing Speed
|
||||
|
||||
**Bottlenecks**:
|
||||
1. **Network I/O**: 1000+ small files downloaded
|
||||
2. **JSON Parsing**: Each timing file parsed individually
|
||||
3. **HashMap Operations**: Frequent insertions with RQ range checking
|
||||
|
||||
**Optimizations Applied**:
|
||||
1. **Concurrency**: One task per car file
|
||||
2. **Connection Pooling**: Reuse HTTP connections
|
||||
3. **Early Filtering**: Invalid times rejected quickly
|
||||
|
||||
## Data Evolution
|
||||
|
||||
### Version Compatibility
|
||||
|
||||
**Backwards Compatibility**:
|
||||
- Old timing files still parseable
|
||||
- New fields added gracefully
|
||||
- Deprecated fields handled
|
||||
|
||||
**Schema Evolution**:
|
||||
- New track additions supported
|
||||
- Condition code extensions possible
|
||||
- Car metadata fields expandable
|
||||
|
||||
### Update Strategy
|
||||
|
||||
**Incremental Updates**:
|
||||
- Only changed cars need re-downloading
|
||||
- Track database updated periodically
|
||||
- User collection refreshed as needed
|
||||
|
||||
**Data Freshness**:
|
||||
- Community timing data updates frequently
|
||||
- Car database relatively stable
|
||||
- Track definitions rarely change
|
||||
|
||||
## Troubleshooting Data Issues
|
||||
|
||||
### Common Problems
|
||||
|
||||
**Missing Timing Data**:
|
||||
- **Symptom**: Cars show no performance data
|
||||
- **Cause**: Timing file not downloaded or corrupted
|
||||
- **Solution**: Re-run `fetch` command
|
||||
|
||||
**Incorrect Ownership**:
|
||||
- **Symptom**: Wrong cars marked as owned/missing
|
||||
- **Cause**: Outdated `my_cars.json` export
|
||||
- **Solution**: Re-export from Top Drives game
|
||||
|
||||
**Track Name Issues**:
|
||||
- **Symptom**: Unknown track codes in output
|
||||
- **Cause**: Outdated track definitions
|
||||
- **Solution**: Update `tracks_repo.json`
|
||||
|
||||
### Data Validation Tools
|
||||
|
||||
**Integrity Checks**:
|
||||
1. **File Count**: Verify expected number of timing files
|
||||
2. **JSON Validity**: Parse all files for syntax errors
|
||||
3. **Reference Integrity**: Cross-check car and track references
|
||||
|
||||
**Diagnostic Commands**:
|
||||
```bash
|
||||
# Check for missing timing files
|
||||
ls times/ | wc -l # Should be 1000+
|
||||
|
||||
# Validate JSON syntax
|
||||
find times/ -name "*.json" -exec jq empty {} \;
|
||||
|
||||
# Check for common issues
|
||||
grep -r "No RID" times/
|
||||
```
|
||||
|
||||
## Future Data Structure Enhancements
|
||||
|
||||
### Potential Improvements
|
||||
|
||||
1. **Compression**: Gzip timing files to reduce storage
|
||||
2. **Indexing**: Add database indexes for faster queries
|
||||
3. **Caching**: Cache parsed data to avoid re-processing
|
||||
4. **Incremental Updates**: Only download changed files
|
||||
|
||||
### Schema Extensions
|
||||
|
||||
**Additional Fields**:
|
||||
- Car weight and dimensions
|
||||
- Performance curves
|
||||
- Weather condition effects
|
||||
- Driver skill impact
|
||||
|
||||
**New Analysis Types**:
|
||||
- Historical performance trends
|
||||
- Meta change impact analysis
|
||||
- Community submission quality metrics
|
||||
- Regional performance differences
|
||||
532
docs/DEVELOPMENT_SETUP.md
Normal file
532
docs/DEVELOPMENT_SETUP.md
Normal file
|
|
@ -0,0 +1,532 @@
|
|||
# Development Setup Guide
|
||||
|
||||
## Prerequisites
|
||||
|
||||
### System Requirements
|
||||
|
||||
**Operating System**: Windows 11, macOS 10.15+, or Linux (Ubuntu 18.04+, CentOS 7+)
|
||||
|
||||
**Architecture**: x86_64 (Intel/AMD) or ARM64 (Apple Silicon)
|
||||
|
||||
**Disk Space**: ~500MB free space (includes Rust toolchain and project data)
|
||||
|
||||
**Memory**: 2GB RAM minimum, 4GB recommended
|
||||
|
||||
**Network**: Broadband internet connection for downloading dependencies and data
|
||||
|
||||
### Required Software
|
||||
|
||||
1. **Rust Toolchain** (Latest Stable)
|
||||
2. **Git** (for version control)
|
||||
3. **Text Editor/IDE** (VS Code recommended)
|
||||
|
||||
## Installation Steps
|
||||
|
||||
### Step 1: Install Rust
|
||||
|
||||
**Windows:**
|
||||
```powershell
|
||||
# Download and run rustup installer from https://rustup.rs/
|
||||
# Or use Winget:
|
||||
winget install Rustlang.Rustup
|
||||
```
|
||||
|
||||
**macOS:**
|
||||
```bash
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||
```
|
||||
|
||||
**Linux (Ubuntu/Debian):**
|
||||
```bash
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||
sudo apt install build-essential # Required for compilation
|
||||
```
|
||||
|
||||
**Verify Installation:**
|
||||
```bash
|
||||
rustc --version
|
||||
cargo --version
|
||||
```
|
||||
|
||||
**Expected Output:**
|
||||
```
|
||||
rustc 1.82.0 (f6e511eec 2024-10-15)
|
||||
cargo 1.82.0 (f6e511eec 2024-10-15)
|
||||
```
|
||||
|
||||
### Step 2: Configure Rust
|
||||
|
||||
**Add Rust to PATH** (if not automatically added):
|
||||
```bash
|
||||
# Add to ~/.bashrc, ~/.zshrc, or system PATH
|
||||
export PATH="$HOME/.cargo/bin:$PATH"
|
||||
source ~/.bashrc
|
||||
```
|
||||
|
||||
**Update Rust** (recommended):
|
||||
```bash
|
||||
rustup update stable
|
||||
```
|
||||
|
||||
**Add Nightly Toolbox** (optional, for advanced features):
|
||||
```bash
|
||||
rustup toolchain install nightly
|
||||
rustup component add rustfmt --toolchain nightly
|
||||
rustup component add clippy --toolchain nightly
|
||||
```
|
||||
|
||||
### Step 3: Install Project Dependencies
|
||||
|
||||
**Clone the Repository:**
|
||||
```bash
|
||||
git clone <repository-url>
|
||||
cd top_drives
|
||||
```
|
||||
|
||||
**Build the Project:**
|
||||
```bash
|
||||
cargo build
|
||||
```
|
||||
|
||||
**Expected Output:**
|
||||
```
|
||||
Compiling top_drives v0.2.0
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 1m 30s
|
||||
```
|
||||
|
||||
**Verify Build:**
|
||||
```bash
|
||||
cargo check # Fast compilation check
|
||||
cargo build --release # Optimized build
|
||||
```
|
||||
|
||||
## Dependency Analysis
|
||||
|
||||
### Core Dependencies
|
||||
|
||||
#### 1. `tokio` - Asynchronous Runtime
|
||||
```toml
|
||||
tokio = { version = "1.44.1", features = ["full"] }
|
||||
```
|
||||
|
||||
**Purpose**: Provides async/await functionality for concurrent operations
|
||||
|
||||
**Features Used**:
|
||||
- `fs`: File system operations
|
||||
- `net`: Network I/O
|
||||
- `rt-multi-thread`: Multi-threaded runtime
|
||||
- `macros`: `#[tokio::main]` and `#[tokio::test]`
|
||||
- `sync`: Async primitives (Semaphore, Mutex)
|
||||
- `task`: Task spawning and management
|
||||
|
||||
**Why Needed**:
|
||||
- Concurrent processing of 1000+ car files
|
||||
- Rate-limited HTTP downloads
|
||||
- Non-blocking file I/O operations
|
||||
|
||||
**Memory Impact**: ~5-10MB overhead for runtime
|
||||
|
||||
#### 2. `reqwest` - HTTP Client
|
||||
```toml
|
||||
reqwest = "0.12.15"
|
||||
```
|
||||
|
||||
**Purpose**: HTTP client for downloading car data and track definitions
|
||||
|
||||
**Features Used**:
|
||||
- JSON parsing from HTTP responses
|
||||
- Custom headers and user agents
|
||||
- Connection pooling and reuse
|
||||
- HTTPS support with certificate validation
|
||||
|
||||
**Why Needed**:
|
||||
- Download car database from GitHub
|
||||
- Fetch individual car timing files from API
|
||||
- Retrieve track definitions
|
||||
|
||||
**Network Behavior**:
|
||||
- Connection pool: 10-20 concurrent connections
|
||||
- Timeout: 30 seconds default
|
||||
- Retry logic: Built-in exponential backoff
|
||||
|
||||
#### 3. `serde` - Serialization Framework
|
||||
```toml
|
||||
serde = { version = "1.0.210", features = ["derive"] }
|
||||
serde_json = "1.0.140"
|
||||
```
|
||||
|
||||
**Purpose**: JSON parsing and serialization for data structures
|
||||
|
||||
**Features Used**:
|
||||
- `Deserialize` derive macro for structs
|
||||
- JSON parsing with `from_str()` and `from_reader()`
|
||||
- Error handling for malformed JSON
|
||||
- Custom field renaming with `#[serde(rename = "...")]`
|
||||
|
||||
**Why Needed**:
|
||||
- Parse `cars_repo.json` (car database)
|
||||
- Parse `my_cars.json` (user collection)
|
||||
- Parse individual car timing files
|
||||
- Parse `tracks_repo.json` (track definitions)
|
||||
|
||||
**Performance**: Zero-copy parsing where possible
|
||||
|
||||
#### 4. `futures` - Future Utilities
|
||||
```toml
|
||||
futures = "0.3.31"
|
||||
```
|
||||
|
||||
**Purpose**: Utilities for working with asynchronous operations
|
||||
|
||||
**Features Used**:
|
||||
- `join_all()`: Wait for multiple futures concurrently
|
||||
- `Future` trait implementations
|
||||
- Stream processing utilities
|
||||
|
||||
**Why Needed**:
|
||||
- Coordinate multiple async file downloads
|
||||
- Process multiple car timing files concurrently
|
||||
- Handle collections of futures
|
||||
|
||||
#### 5. `once_cell` - Lazy Initialization
|
||||
```toml
|
||||
once_cell = "1.20.2"
|
||||
```
|
||||
|
||||
**Purpose**: Thread-safe lazy initialization of global statics
|
||||
|
||||
**Features Used**:
|
||||
- `Lazy`: One-time initialization of expensive resources
|
||||
- Thread-safe access patterns
|
||||
- Interior mutability with `Mutex`
|
||||
|
||||
**Why Needed**:
|
||||
- Global `BEST_CARS` cache initialization
|
||||
- Thread-safe access during concurrent parsing
|
||||
- Avoid expensive computation on every access
|
||||
|
||||
#### 6. `scraper` - HTML Parsing (Club Module Only)
|
||||
```toml
|
||||
scraper = "0.23.1"
|
||||
```
|
||||
|
||||
**Purpose**: HTML parsing and CSS selector engine for web scraping
|
||||
|
||||
**Features Used**:
|
||||
- CSS selector parsing with `Selector::parse()`
|
||||
- HTML document parsing with `Html::parse_document()`
|
||||
- Element selection and text extraction
|
||||
|
||||
**Why Needed**:
|
||||
- Alternative data source for club.rs module
|
||||
- Parse car data from topdrives.club HTML tables
|
||||
- Extract performance information from web pages
|
||||
|
||||
### Development Dependencies
|
||||
|
||||
#### 7. Build Tools and Quality
|
||||
|
||||
**Recommended additions for development:**
|
||||
```toml
|
||||
[dev-dependencies]
|
||||
tokio-test = "0.4"
|
||||
criterion = "0.5" # Benchmarking
|
||||
|
||||
[[bench]]
|
||||
name = "parsing_benchmark"
|
||||
harness = false
|
||||
```
|
||||
|
||||
## Development Environment Setup
|
||||
|
||||
### VS Code Configuration
|
||||
|
||||
**Recommended Extensions:**
|
||||
1. **rust-analyzer**: Rust language support
|
||||
2. **Better TOML**: TOML file support
|
||||
3. **crates**: Rust package search
|
||||
4. **Rust Test Explorer**: Test discovery and execution
|
||||
|
||||
**Settings** (`settings.json`):
|
||||
```json
|
||||
{
|
||||
"rust-analyzer.check.command": "clippy",
|
||||
"rust-analyzer.cargo.loadOutDirsFromCheck": true,
|
||||
"rust-analyzer.procMacro.enable": true,
|
||||
"files.associations": {
|
||||
"*.rs": "rust"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Debugging Setup
|
||||
|
||||
**VS Code Launch Configuration** (`.vscode/launch.json`):
|
||||
```json
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Debug Records",
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/target/debug/records",
|
||||
"args": ["best"],
|
||||
"cwd": "${workspaceFolder}"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Build Configuration
|
||||
|
||||
### Cargo.toml Analysis
|
||||
|
||||
**Project Structure:**
|
||||
```toml
|
||||
[package]
|
||||
name = "top_drives"
|
||||
version = "0.2.0"
|
||||
edition = "2021"
|
||||
|
||||
[[bin]]
|
||||
name = "club"
|
||||
path = "src/club.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "records"
|
||||
path = "src/records.rs"
|
||||
```
|
||||
|
||||
**Binary Configuration:**
|
||||
- **records**: Main analysis tool (JSON-based)
|
||||
- **club**: Alternative scraper (web-based)
|
||||
|
||||
**Dependency Versions:**
|
||||
- All dependencies pinned to specific versions for reproducibility
|
||||
- Compatible versions tested together
|
||||
- Security updates monitored
|
||||
|
||||
## Performance Optimization
|
||||
|
||||
### Compilation Settings
|
||||
|
||||
**Debug Build:**
|
||||
```bash
|
||||
cargo build # Fast compilation, slow execution
|
||||
```
|
||||
|
||||
**Release Build:**
|
||||
```bash
|
||||
cargo build --release # Slow compilation, fast execution
|
||||
```
|
||||
|
||||
**Profile Configuration** (Optional `Cargo.toml`):
|
||||
```toml
|
||||
[profile.release]
|
||||
opt-level = 3
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
panic = "abort"
|
||||
```
|
||||
|
||||
### Runtime Performance
|
||||
|
||||
**Memory Settings:**
|
||||
```bash
|
||||
# Increase stack size for deep recursion (if needed)
|
||||
RUST_MIN_STACK=8388608 cargo run
|
||||
|
||||
# Enable backtraces for debugging
|
||||
RUST_BACKTRACE=1 cargo run
|
||||
```
|
||||
|
||||
**Concurrency Tuning:**
|
||||
- Default: 20 concurrent downloads (configurable via Semaphore)
|
||||
- Thread count: Automatic based on CPU cores
|
||||
- Memory usage: ~100-200MB for full dataset
|
||||
|
||||
## Testing Setup
|
||||
|
||||
### Running Tests
|
||||
```bash
|
||||
cargo test # Run all tests
|
||||
cargo test --release # Run tests on release build
|
||||
cargo test -- --nocapture # Show test output
|
||||
```
|
||||
|
||||
### Writing Tests
|
||||
|
||||
**Example Test Structure:**
|
||||
```rust
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_parse_car_times() {
|
||||
// Test implementation
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Test Data:**
|
||||
- Create sample JSON files in `tests/fixtures/`
|
||||
- Mock HTTP responses for network tests
|
||||
- Use `tempfile` crate for temporary files
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Installation Issues
|
||||
|
||||
**"rustc not found"**
|
||||
- **Cause**: Rust not in PATH
|
||||
- **Solution**: Add `$HOME/.cargo/bin` to PATH
|
||||
|
||||
**"Permission denied" (Unix)**
|
||||
- **Cause**: Insufficient permissions
|
||||
- **Solution**: Check file permissions or use `sudo` for system-wide install
|
||||
|
||||
**"Linker not found" (Linux)**
|
||||
- **Cause**: Missing build tools
|
||||
- **Solution**: Install `build-essential` (Ubuntu) or `gcc` (CentOS)
|
||||
|
||||
### Build Issues
|
||||
|
||||
**"Package not found"**
|
||||
- **Cause**: Missing dependencies
|
||||
- **Solution**: Run `cargo fetch` or `cargo build` with internet
|
||||
|
||||
**"Conflicting versions"**
|
||||
- **Cause**: Dependency version conflicts
|
||||
- **Solution**: Update Cargo.lock with `cargo update`
|
||||
|
||||
**"Compilation failed"**
|
||||
- **Cause**: Syntax or type errors
|
||||
- **Solution**: Check error messages, fix code issues
|
||||
|
||||
### Runtime Issues
|
||||
|
||||
**"Thread panicked"**
|
||||
- **Cause**: Unhandled error conditions
|
||||
- **Solution**: Add proper error handling, check unwrap() calls
|
||||
|
||||
**"Out of memory"**
|
||||
- **Cause**: Large dataset processing
|
||||
- **Solution**: Process data in batches, increase system memory
|
||||
|
||||
## Development Workflow
|
||||
|
||||
### 1. Code Changes
|
||||
```bash
|
||||
# Make changes to source files
|
||||
code src/records.rs
|
||||
|
||||
# Check compilation
|
||||
cargo check
|
||||
|
||||
# Run tests
|
||||
cargo test
|
||||
|
||||
# Format code
|
||||
cargo fmt
|
||||
|
||||
# Lint code
|
||||
cargo clippy
|
||||
```
|
||||
|
||||
### 2. Testing Changes
|
||||
```bash
|
||||
# Test specific functionality
|
||||
cargo test parse_car_times
|
||||
|
||||
# Benchmark performance
|
||||
cargo bench
|
||||
|
||||
# Check for memory leaks
|
||||
valgrind cargo run
|
||||
```
|
||||
|
||||
### 3. Debugging
|
||||
```bash
|
||||
# Debug build with symbols
|
||||
cargo build
|
||||
|
||||
# Run with debugger
|
||||
gdb target/debug/records
|
||||
|
||||
# Profile performance
|
||||
cargo flamegraph --bin records -- best
|
||||
```
|
||||
|
||||
## Deployment
|
||||
|
||||
### Binary Distribution
|
||||
|
||||
**Release Build:**
|
||||
```bash
|
||||
cargo build --release
|
||||
```
|
||||
|
||||
**Binary Location:**
|
||||
```
|
||||
target/release/records # Main analyzer
|
||||
target/release/club # Club scraper
|
||||
```
|
||||
|
||||
**Cross-Platform Building:**
|
||||
```bash
|
||||
# Build for Windows from Linux/macOS
|
||||
cargo build --target x86_64-pc-windows-gnu --release
|
||||
|
||||
# Build for macOS from Linux/Windows
|
||||
cargo build --target x86_64-apple-darwin --release
|
||||
```
|
||||
|
||||
## Maintenance
|
||||
|
||||
### Regular Tasks
|
||||
|
||||
**Update Dependencies:**
|
||||
```bash
|
||||
cargo update # Update to latest compatible versions
|
||||
cargo outdated # Check for outdated packages
|
||||
```
|
||||
|
||||
**Security Updates:**
|
||||
```bash
|
||||
cargo audit # Check for security vulnerabilities
|
||||
cargo update # Apply security patches
|
||||
```
|
||||
|
||||
**Performance Monitoring:**
|
||||
```bash
|
||||
cargo bloat # Check binary size
|
||||
cargo profdata # Profile-guided optimization
|
||||
```
|
||||
|
||||
## Getting Help
|
||||
|
||||
### Documentation
|
||||
- **Rust Book**: https://doc.rust-lang.org/book/
|
||||
- **Cargo Guide**: https://doc.rust-lang.org/cargo/
|
||||
- **Tokio Tutorial**: https://tokio.rs/tokio/tutorial
|
||||
|
||||
### Community Resources
|
||||
- **Rust Users Forum**: https://users.rust-lang.org/
|
||||
- **Stack Overflow**: Tag `rust`
|
||||
- **Discord**: Rust community servers
|
||||
|
||||
### Project-Specific Help
|
||||
- Check existing documentation in `docs/` directory
|
||||
- Review issue tracker for known problems
|
||||
- Create new issues for bugs or feature requests
|
||||
|
||||
---
|
||||
|
||||
**Development Environment Summary:**
|
||||
- **Rust**: 1.82.0+ (latest stable recommended)
|
||||
- **Build Tool**: Cargo (included with Rust)
|
||||
- **Editor**: VS Code with rust-analyzer (recommended)
|
||||
- **Testing**: Built-in Rust testing framework
|
||||
- **Debugging**: GDB/LLDB integration available
|
||||
334
docs/USER_GUIDE.md
Normal file
334
docs/USER_GUIDE.md
Normal file
|
|
@ -0,0 +1,334 @@
|
|||
# Top Drives Records Analyzer - User Guide
|
||||
|
||||
## Introduction
|
||||
|
||||
This guide provides comprehensive instructions for using the Top Drives Records Analyzer to optimize your car collection and improve your racing performance. The tool analyzes timing data from thousands of community-submitted runs to help you make informed decisions about your car collection.
|
||||
|
||||
## Quick Start Checklist
|
||||
|
||||
- [ ] Install Rust development environment
|
||||
- [ ] Export your car collection from Top Drives game
|
||||
- [ ] Run initial data fetch
|
||||
- [ ] Analyze your collection
|
||||
- [ ] Review recommendations
|
||||
|
||||
## Setup Instructions
|
||||
|
||||
### 1. Game Data Export
|
||||
|
||||
**Export your car collection from Top Drives:**
|
||||
|
||||
1. Open Top Drives on your device
|
||||
2. Go to Settings → Account → Export Data
|
||||
3. Save the file as `my_cars.json` in the project directory
|
||||
|
||||
### 2. Initial Data Download
|
||||
|
||||
Fetch the latest car and track databases:
|
||||
|
||||
```bash
|
||||
cargo run --bin records -- fetch
|
||||
```
|
||||
|
||||
**What this does:**
|
||||
- Downloads `cars_repo.json` (car database)
|
||||
- Downloads `tracks_repo.json` (track definitions)
|
||||
- Downloads timing files for all cars (1000+ files in `times/` directory)
|
||||
|
||||
**Expected output:**
|
||||
```
|
||||
Fetching cars file...
|
||||
Fetching tracks file...
|
||||
Fetching https://api.topdrivesrecords.com/car/1001.json.
|
||||
Fetching https://api.topdrivesrecords.com/car/1002.json.
|
||||
...
|
||||
```
|
||||
|
||||
## Command Reference
|
||||
|
||||
### Basic Analysis Commands
|
||||
|
||||
#### Best Cars Analysis
|
||||
|
||||
**Command:**
|
||||
```bash
|
||||
cargo run --bin records -- best
|
||||
```
|
||||
|
||||
**Purpose:** Shows the best performing cars in your collection, ranked by how often they achieve top times.
|
||||
|
||||
**Sample Output:**
|
||||
```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
|
||||
```
|
||||
|
||||
**Understanding the output:**
|
||||
- **RQ**: Car's rarity level
|
||||
- **Brand**: Car manufacturer
|
||||
- **Model**: Car model name
|
||||
- **Year**: Model year
|
||||
- **Count**: How many times this car was the fastest across different tracks
|
||||
|
||||
**Interpretation:**
|
||||
- Higher counts indicate more versatile cars
|
||||
- Focus on cars with counts ≥ 5 for your core collection
|
||||
- Compare with your owned cars to identify strengths
|
||||
|
||||
#### Missing Cars Analysis
|
||||
|
||||
**Command:**
|
||||
```bash
|
||||
cargo run --bin records -- missing
|
||||
```
|
||||
|
||||
**Purpose:** Identifies high-performing cars you don't own that would improve your collection.
|
||||
|
||||
**Sample Output:**
|
||||
```csv
|
||||
RQ,Brand,Model,Year,Count
|
||||
90,Pagani,Zonda R,2010,18
|
||||
88,Ferrari,F40,1987,15
|
||||
87,McLaren,Senna,2018,12
|
||||
```
|
||||
|
||||
**Action items:**
|
||||
- Prioritize cars with high counts (≥10)
|
||||
- Consider RQ restrictions in your racing bracket
|
||||
- Look for cars that perform well across multiple track types
|
||||
|
||||
#### Collection Optimization (Trash Analysis)
|
||||
|
||||
**Command:**
|
||||
```bash
|
||||
cargo run --bin records -- trash
|
||||
```
|
||||
|
||||
**Purpose:** Identifies cars safe to remove from your collection.
|
||||
|
||||
**Sample Output:**
|
||||
```csv
|
||||
RQ,Brand,Model,Year
|
||||
45,Toyota,Corolla,1995
|
||||
52,Honda,Civic,1992
|
||||
38,Ford,Focus,2001
|
||||
```
|
||||
|
||||
**Safety guidelines:**
|
||||
- Only removes cars below RQ 80
|
||||
- Excludes prize cars (may have collection value)
|
||||
- Only suggests cars that never achieve best times
|
||||
- **Always verify before selling in-game**
|
||||
|
||||
#### Detailed Performance Data
|
||||
|
||||
**Command:**
|
||||
```bash
|
||||
cargo run --bin records -- details
|
||||
```
|
||||
|
||||
**Purpose:** Comprehensive timing data for all tracks and conditions.
|
||||
|
||||
**Sample Output:**
|
||||
```csv
|
||||
RQ,TrackID,Condition,CarRQ,Brand,Model,Year,Config,Time
|
||||
85,laguna,Dry,85,McLaren,F1,1993,233,112.45
|
||||
85,monaco,Dry,85,Pagani,Zonda,2002,111,45.23
|
||||
82,laguna,Dry,82,Ferrari,250 GTO,1962,111,115.67
|
||||
```
|
||||
|
||||
**Use cases:**
|
||||
- External spreadsheet analysis
|
||||
- Track-specific performance research
|
||||
- Historical performance tracking
|
||||
|
||||
#### Track-Specific Analysis
|
||||
|
||||
**Command:**
|
||||
```bash
|
||||
cargo run --bin records -- track "Laguna Seca" "Dry" "85"
|
||||
```
|
||||
|
||||
**Purpose:** Find the best car for a specific track, condition, and RQ level.
|
||||
|
||||
**Sample Output:**
|
||||
```
|
||||
Best car for Track: laguna, Condition: Dry, RQ: 85 -> McLaren F1 (1993) with time 112.45
|
||||
```
|
||||
|
||||
**Track name format:**
|
||||
- Use internal names: `laguna`, `monaco`, `testBowl`
|
||||
- Full mappings in `translate_track_name()` function
|
||||
|
||||
**Condition options:**
|
||||
- `Dry` - Standard asphalt
|
||||
- `Wet` - Rain conditions
|
||||
- `Dirt` - Off-road tracks
|
||||
- `Snow` - Winter conditions
|
||||
|
||||
## Advanced Usage
|
||||
|
||||
### RQ Range Analysis
|
||||
|
||||
The tool analyzes performance across RQ brackets. Understanding RQ ranges:
|
||||
|
||||
- **RQ 10-50**: Entry level cars
|
||||
- **RQ 51-80**: Mid-tier performance
|
||||
- **RQ 81-100**: High performance
|
||||
- **RQ 101-113**: Elite/supercars
|
||||
|
||||
**Strategy:**
|
||||
- Focus on 2-3 RQ brackets where you race most
|
||||
- Identify cars that perform well across multiple brackets
|
||||
- Consider upgrade paths for favorite cars
|
||||
|
||||
### Collection Planning
|
||||
|
||||
#### Step 1: Assess Current Collection
|
||||
```bash
|
||||
cargo run --bin records -- best
|
||||
```
|
||||
- Identify your strongest cars
|
||||
- Note RQ distribution
|
||||
- Find gaps in coverage
|
||||
|
||||
#### Step 2: Identify Priorities
|
||||
```bash
|
||||
cargo run --bin records -- missing
|
||||
```
|
||||
- Focus on high-count cars in your RQ range
|
||||
- Consider track variety needs
|
||||
- Factor in prize car availability
|
||||
|
||||
#### Step 3: Optimize Collection
|
||||
```bash
|
||||
cargo run --bin records -- trash
|
||||
```
|
||||
- Remove redundant low performers
|
||||
- Free up space for better cars
|
||||
- Maintain diversity for different track types
|
||||
|
||||
### Track Type Strategy
|
||||
|
||||
Different cars excel on different track types:
|
||||
|
||||
#### Speed Tracks
|
||||
- **Characteristics**: Long straights, high speeds
|
||||
- **Best cars**: High top speed, good acceleration
|
||||
- **Examples**: Laguna Seca, Monaco Streets
|
||||
|
||||
#### Technical Tracks
|
||||
- **Characteristics**: Tight corners, elevation changes
|
||||
- **Best cars**: Good handling, braking
|
||||
- **Examples**: Deals Gap, Mountain Hairpin
|
||||
|
||||
#### Off-Road Tracks
|
||||
- **Characteristics**: Dirt, gravel, snow surfaces
|
||||
- **Best cars**: Good grip, durability
|
||||
- **Examples**: Desert Rally, Forest Road
|
||||
|
||||
## Output File Analysis
|
||||
|
||||
### CSV File Usage
|
||||
|
||||
The tool generates CSV files for external analysis:
|
||||
|
||||
#### best.csv
|
||||
- Your best performing cars
|
||||
- Import into spreadsheet for sorting/filtering
|
||||
- Track performance across time
|
||||
|
||||
#### missing.csv
|
||||
- Cars to acquire
|
||||
- Prioritize by count and RQ relevance
|
||||
- Plan acquisition strategy
|
||||
|
||||
#### details.csv
|
||||
- Comprehensive timing database
|
||||
- Advanced analysis and research
|
||||
- Historical performance tracking
|
||||
|
||||
### Spreadsheet Analysis Tips
|
||||
|
||||
**Excel/Google Sheets setup:**
|
||||
1. Import CSV files
|
||||
2. Create pivot tables by RQ, Brand, Track
|
||||
3. Add conditional formatting for high performers
|
||||
4. Create dashboards for collection overview
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
#### "Please provide a command"
|
||||
**Problem:** No arguments provided
|
||||
**Solution:** Use `best`, `missing`, `trash`, `details`, or `track`
|
||||
|
||||
#### "Could not read my cars file"
|
||||
**Problem:** `my_cars.json` not found or not readable
|
||||
**Solution:**
|
||||
1. Export fresh data from Top Drives game
|
||||
2. Ensure file is named exactly `my_cars.json`
|
||||
3. Check file permissions
|
||||
|
||||
#### "Failed to fetch the file"
|
||||
**Problem:** Network issues or API unavailable
|
||||
**Solution:**
|
||||
1. Check internet connection
|
||||
2. Try again later (may be temporary)
|
||||
3. Use existing cached data if available
|
||||
|
||||
#### No output or empty results
|
||||
**Problem:** Missing timing data or configuration issues
|
||||
**Solution:**
|
||||
1. Re-run `fetch` command to get fresh data
|
||||
2. Check that `times/` directory has files
|
||||
3. Verify `cars_repo.json` exists
|
||||
|
||||
### Performance Issues
|
||||
|
||||
#### Slow analysis
|
||||
- **Expected**: First run takes 2-3 minutes (downloads data)
|
||||
- **Subsequent runs**: Should be fast (uses cached data)
|
||||
- **Large datasets**: Details command processes all timing data
|
||||
|
||||
#### Memory usage
|
||||
- **Normal**: 100-200MB for full analysis
|
||||
- **High**: Details command loads all timing data
|
||||
- **Solution**: Use specific commands instead of details for large datasets
|
||||
|
||||
## Tips and Best Practices
|
||||
|
||||
### Regular Maintenance
|
||||
1. **Weekly**: Run `fetch` to get latest data
|
||||
2. **Monthly**: Review `missing` cars for new acquisitions
|
||||
3. **Quarterly**: Use `trash` to optimize collection
|
||||
|
||||
### Data-Driven Decisions
|
||||
1. **Trust the numbers**: Count-based recommendations are data-driven
|
||||
2. **Consider context**: Some cars may have strategic value beyond performance
|
||||
3. **Track changes**: Keep performance history for trend analysis
|
||||
|
||||
### Collection Strategy
|
||||
1. **Diversify**: Don't focus only on one RQ bracket
|
||||
2. **Specialize**: Identify your preferred track types
|
||||
3. **Balance**: Mix proven performers with potential upgrades
|
||||
|
||||
## Getting Help
|
||||
|
||||
### Documentation
|
||||
- `README.md`: Project overview and setup
|
||||
- `docs/ARCHITECTURE.md`: Technical details for developers
|
||||
- `docs/USER_GUIDE.md`: This comprehensive guide
|
||||
|
||||
### Community Resources
|
||||
- **Data Source**: Community timing database
|
||||
- **Updates**: Check for new features and improvements
|
||||
- **Issues**: Report bugs or request features
|
||||
|
||||
---
|
||||
|
||||
**Remember**: This tool provides data-driven insights, but ultimately you're the best judge of which cars you enjoy driving. Use the analysis as a guide, not a strict rulebook.
|
||||
Loading…
Add table
Add a link
Reference in a new issue