package persona import "fmt" // ConsistencyIssue represents an internally inconsistent attribute combination. // Unlike plausibility violations (which compare against ethnicity expectations), // consistency issues catch impossible or contradictory combinations regardless // of ethnicity. type ConsistencyIssue struct { Field1 string // First field involved (e.g., "identity.age") Value1 interface{} // Value of first field Field2 string // Second field involved (e.g., "face.hair_color") Value2 interface{} // Value of second field Severity string // "warning" or "error" Description string // Human-readable explanation } // ConsistencyResult contains cross-attribute validation results. type ConsistencyResult struct { Valid bool // True if no errors (warnings are OK) Issues []ConsistencyIssue // All issues found } // ValidateConsistency checks for internally inconsistent attribute combinations // that are implausible regardless of ethnicity. // // Examples of inconsistencies: // - Young age (< 25) with gray hair (without dye indication) // - Mature skin texture with young age // - Height category mismatched with height CM // - Muscle definition that contradicts build type func ValidateConsistency(dna *DNA) *ConsistencyResult { if dna == nil { return &ConsistencyResult{Valid: true} } result := &ConsistencyResult{ Valid: true, Issues: []ConsistencyIssue{}, } // Age vs Hair Color consistency if dna.Identity.Age > 0 && dna.Face.HairColor != "" { if dna.Identity.Age < 30 && dna.Face.HairColor == HairColorGray { result.Issues = append(result.Issues, ConsistencyIssue{ Field1: "identity.age", Value1: dna.Identity.Age, Field2: "face.hair_color", Value2: dna.Face.HairColor, Severity: "warning", Description: fmt.Sprintf("Gray hair at age %d is unusual without premature graying or dye", dna.Identity.Age), }) } } // Age vs Skin Texture consistency if dna.Identity.Age > 0 && dna.Face.SkinTexture != "" { if dna.Identity.Age < 35 && dna.Face.SkinTexture == SkinTextureMature { result.Issues = append(result.Issues, ConsistencyIssue{ Field1: "identity.age", Value1: dna.Identity.Age, Field2: "face.skin_texture", Value2: dna.Face.SkinTexture, Severity: "warning", Description: fmt.Sprintf("Mature skin texture at age %d is inconsistent", dna.Identity.Age), }) } if dna.Identity.Age > 55 && dna.Face.SkinTexture == SkinTextureSmooth { result.Issues = append(result.Issues, ConsistencyIssue{ Field1: "identity.age", Value1: dna.Identity.Age, Field2: "face.skin_texture", Value2: dna.Face.SkinTexture, Severity: "warning", Description: fmt.Sprintf("Smooth skin texture at age %d is unusual", dna.Identity.Age), }) } } // Height Category vs Height CM consistency if dna.Body.HeightCM > 0 && dna.Body.Height != "" { if issue := validateHeightCMConsistency(dna.Body.HeightCM, dna.Body.Height); issue != nil { result.Issues = append(result.Issues, *issue) if issue.Severity == "error" { result.Valid = false } } } // Muscle Definition vs Build consistency if dna.Body.MuscleDefinition != "" && dna.Body.Build != "" { // High muscle definition with slender build is contradictory if dna.Body.MuscleDefinition == MuscleDefinitionDefined || dna.Body.MuscleDefinition == MuscleDefinitionRipped { if dna.Body.Build == BodyBuildSlender || dna.Body.Build == BodyBuildPetite { result.Issues = append(result.Issues, ConsistencyIssue{ Field1: "body.muscle_definition", Value1: dna.Body.MuscleDefinition, Field2: "body.build", Value2: dna.Body.Build, Severity: "warning", Description: fmt.Sprintf("High muscle definition (%s) is unusual with %s build", dna.Body.MuscleDefinition, dna.Body.Build), }) } } } // Body Fat Percent vs Build consistency if dna.Body.BodyFatPercent > 0 && dna.Body.Build != "" { // Very low body fat (< 15%) with curvy/plus_curvy build is contradictory if dna.Body.BodyFatPercent < 15 { if dna.Body.Build == BodyBuildCurvy || dna.Body.Build == BodyBuildPlusCurvy { result.Issues = append(result.Issues, ConsistencyIssue{ Field1: "body.body_fat_percent", Value1: dna.Body.BodyFatPercent, Field2: "body.build", Value2: dna.Body.Build, Severity: "error", Description: fmt.Sprintf("Very low body fat (%d%%) is incompatible with %s build", dna.Body.BodyFatPercent, dna.Body.Build), }) result.Valid = false } } // High body fat (> 30%) with athletic/muscular build is contradictory if dna.Body.BodyFatPercent > 30 { if dna.Body.Build == BodyBuildAthletic || dna.Body.Build == BodyBuildMuscular { result.Issues = append(result.Issues, ConsistencyIssue{ Field1: "body.body_fat_percent", Value1: dna.Body.BodyFatPercent, Field2: "body.build", Value2: dna.Body.Build, Severity: "error", Description: fmt.Sprintf("High body fat (%d%%) is incompatible with %s build", dna.Body.BodyFatPercent, dna.Body.Build), }) result.Valid = false } } } return result } // validateHeightCMConsistency checks if height CM matches height category. func validateHeightCMConsistency(heightCM int, heightCat HeightCategory) *ConsistencyIssue { expectedCat := HeightCategoryFromCM(heightCM) if heightCat != expectedCat { return &ConsistencyIssue{ Field1: "body.height_cm", Value1: heightCM, Field2: "body.height", Value2: heightCat, Severity: "error", Description: fmt.Sprintf("Height %dcm should be category %s, not %s", heightCM, expectedCat, heightCat), } } return nil } // HasErrors returns true if there are any error-level issues. func (r *ConsistencyResult) HasErrors() bool { for _, issue := range r.Issues { if issue.Severity == "error" { return true } } return false } // HasWarnings returns true if there are any warning-level issues. func (r *ConsistencyResult) HasWarnings() bool { for _, issue := range r.Issues { if issue.Severity == "warning" { return true } } return false }