Fix multi-copy car handling: track all configs per guid and add copy count
- Change owned_configs from HashMap<String, String> to HashMap<String, Vec<String>> to capture all configs across copies (previously last-write-wins lost data) - Add copy_count field to MyCarsData and CarCounts for wishlist copy-coverage logic - Fix improve.csv to exclude already-owned final configs as targets (e.g., Fiat Campagnola 1974 now targets 332 instead of already-owned 323) - Fix wishlist to check all owned configs for exact-match and final-build detection - Fix trash to check all configs for final-build-not-best detection - Update all tests for new Vec type and add multi-copy improve test
This commit is contained in:
parent
b24b274d25
commit
62688926c3
7 changed files with 494 additions and 83 deletions
|
|
@ -21,10 +21,15 @@ pub struct MyCarsData {
|
|||
pub owned: HashSet<String>,
|
||||
/// GUIDs of wishlisted cars (from `wishlists[0].cards`)
|
||||
pub wishlist: HashSet<String>,
|
||||
/// Actual tune config for each owned car (guid → "{engineMajor}{weightMajor}{chassisMajor}").
|
||||
/// Only populated for locked cards. Used by the improve command to determine
|
||||
/// which cars are not yet at a final build.
|
||||
pub owned_configs: HashMap<String, String>,
|
||||
/// Actual tune configs for each owned car (guid → list of configs from all copies).
|
||||
/// Multiple copies of the same car model share the same guid, so this Vec
|
||||
/// captures all their individual configs. Previously a single String lost
|
||||
/// data from all but the last-processed copy.
|
||||
pub owned_configs: HashMap<String, Vec<String>>,
|
||||
/// Number of copies owned for each car GUID.
|
||||
/// Multiple copies of the same car model share the same guid.
|
||||
/// `owned` HashSet deduplicates, so this counts duplicates.
|
||||
pub copy_count: HashMap<String, usize>,
|
||||
}
|
||||
|
||||
/// Loads and parses `my_cars.json` once, extracting both owned and wishlist data.
|
||||
|
|
@ -55,13 +60,15 @@ pub fn load_my_cars_data(path: &str) -> Result<MyCarsData, AppError> {
|
|||
|
||||
// Extract owned car GUIDs and their actual configs from playerDeck
|
||||
let mut owned: HashSet<String> = HashSet::new();
|
||||
let mut owned_configs: HashMap<String, String> = HashMap::new();
|
||||
let mut owned_configs: HashMap<String, Vec<String>> = HashMap::new();
|
||||
let mut copy_count: HashMap<String, usize> = HashMap::new();
|
||||
|
||||
if let Some(deck) = json.get("playerDeck").and_then(|d| d.as_array()) {
|
||||
for card in deck {
|
||||
if card.get("locked").and_then(|v| v.as_bool()).unwrap_or(false) {
|
||||
if let Some(guid) = card.get("cardId").and_then(|v| v.as_str()) {
|
||||
owned.insert(guid.to_string());
|
||||
*copy_count.entry(guid.to_string()).or_insert(0) += 1;
|
||||
// Extract actual tune config: engineMajor + weightMajor + chassisMajor.
|
||||
// Skip cards with incomplete tune data to avoid false "000" entries
|
||||
// that would incorrectly appear as needing upgrade.
|
||||
|
|
@ -70,7 +77,7 @@ pub fn load_my_cars_data(path: &str) -> Result<MyCarsData, AppError> {
|
|||
let chassis = card.get("chassisMajor").and_then(|v| v.as_u64());
|
||||
if let (Some(e), Some(w), Some(c)) = (engine, weight, chassis) {
|
||||
let config = format!("{e}{w}{c}");
|
||||
owned_configs.insert(guid.to_string(), config);
|
||||
owned_configs.entry(guid.to_string()).or_default().push(config);
|
||||
} else {
|
||||
eprintln!(
|
||||
"Warning: locked card {guid} has incomplete tune data — skipped"
|
||||
|
|
@ -90,7 +97,7 @@ pub fn load_my_cars_data(path: &str) -> Result<MyCarsData, AppError> {
|
|||
.map(|cards| cards.iter().filter_map(|c| c.as_str().map(|s| s.to_string())).collect())
|
||||
.unwrap_or_default();
|
||||
|
||||
Ok(MyCarsData { owned, wishlist, owned_configs })
|
||||
Ok(MyCarsData { owned, wishlist, owned_configs, copy_count })
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue