package main
import (
	"bufio"
	"fmt"
	"log"
	"math/rand"
	"os"
	"strconv"
	"time"
)
// getCommaPositions returns the slice of indices where commas occur in the line
func getCommaPositions(line string) []int {
	var commas []int
		for i := 0; i < len(line); i++ {
			if line[i] == ',' {
				commas = append(commas, i)
			}
		}
		return commas
	}
// substringBetweenCommasFast extracts substring between nth and mth commas (1-based), using cached comma positions
// Returns empty string if indices invalid.
func substringBetweenCommasFast(line string, commas []int, nth, mth int) (string, error) {
	if nth >= mth {
		return "", fmt.Errorf("invalid indices: nth >= mth")
	}
	if mth > len(commas) {
		return "", fmt.Errorf("not enough commas: needed %d got %d", mth, len(commas))
	}
	start := 0
	if nth > 0 {
		start = commas[nth-1] + 1
	}
	end := commas[mth-1]
	if start > end || start > len(line) || end > len(line) || start < 0 || end < 0 {
		return "", fmt.Errorf("invalid substring indices")
	}
	return line[start:end], nil
}
// extractPartsFast extracts multiple parts given pairs of (nth, mth) comma indices, using cached comma positions
func extractPartsFast(line string, commas []int, pairs [][2]int) ([]string, error) {
	parts := make([]string, len(pairs))
	for i, pair := range pairs {
		part, err := substringBetweenCommasFast(line, commas, pair[0], pair[1])
		if err != nil {
			return nil, err
		}
		parts[i] = part
	}
	return parts, nil
}
// pickOffspringParts1 picks offspring parts from parents with ensured swapping
func pickOffspringParts1(p1G, p1g, p2G, p2g string) (string, string) {
	if rand.Intn(2) == 0 {
		if rand.Intn(2) == 0 {
			return p1G, p2G
		}
		return p1G, p2g
	}
	if rand.Intn(2) == 0 {
		return p2G, p1G
	}
	return p2G, p1g
}
// pickOffspringParts2 picks offspring parts by selecting each part from either parent1 or parent2
func pickOffspringParts2(parent1Parts, parent2Parts []string) ([]string, error) {
	if len(parent1Parts) != len(parent2Parts) {
		return nil, fmt.Errorf("parent parts slices must have the same length")
	}
	offspringParts := make([]string, len(parent1Parts))
	for i := range parent1Parts {
		if rand.Intn(2) == 0 {
			offspringParts[i] = parent1Parts[i]
		} else {
			offspringParts[i] = parent2Parts[i]
		}
	}
	return offspringParts, nil
}
// offspringWithRandomExclusion produces offspring parts, 2 parts replaced by random int strings between 1-10 excluding parents' values
func offspringWithRandomExclusion(parent1Parts, parent2Parts []string) ([]string, error) {
	if len(parent1Parts) != len(parent2Parts) {
		return nil, fmt.Errorf("parent parts slices must have the same length")
	}
	n := len(parent1Parts)
	if n < 2 {
		return nil, fmt.Errorf("need at least two parts for replacement")
	}
	offspring := make([]string, n)
	chosenRandomIdx := make(map[int]bool)
	// randomly pick 2 distinct indexes to replace with random ints
	perm := rand.Perm(n)
	chosenRandomIdx[perm[0]] = true
	chosenRandomIdx[perm[1]] = true
	for i := 0; i < n; i++ {
		if chosenRandomIdx[i] {
			// parse parent parts to integers
			p1v, err1 := strconv.Atoi(parent1Parts[i])
			p2v, err2 := strconv.Atoi(parent2Parts[i])
			// collect forbidden values (only if both parses succeeded)
			forbidden := map[int]bool{}
			if err1 == nil {
				forbidden[p1v] = true
			}
			if err2 == nil {
				forbidden[p2v] = true
			}
			var val int
			tries := 0
			// randomly select int 1-10 excluding forbidden
			for {
				val = rand.Intn(10) + 1
				if !forbidden[val] {
					break
				}
				tries++
				if tries > 100 {
					return nil, fmt.Errorf("cannot find suitable random integer excluding parents at index %d", i)
				}
			}
			offspring[i] = strconv.Itoa(val)
			} else {
			// pick randomly parent1 or parent2 part for this index
			if rand.Intn(2) == 0 {
				offspring[i] = parent1Parts[i]
			} else {
				offspring[i] = parent2Parts[i]
			}
		}
	}
	return offspring, nil
}
// countLines quickly counts lines in file; only scanning once
func countLines(filepath string) (int, error) {
	f, err := os.Open(filepath)
	if err != nil {
		return 0, err
	}
	defer f.Close()
	scanner := bufio.NewScanner(f)
	count := 0
	for scanner.Scan() {
		count++
	}
	return count, scanner.Err()
}
// getLineAt reads only the specified line (0-based) from the file
func getLineAt(filepath string, lineNum int) (string, error) {
	f, err := os.Open(filepath)
	if err != nil {
		return "", err
	}
	defer f.Close()
	scanner := bufio.NewScanner(f)
	current := 0
	for scanner.Scan() {
		if current == lineNum {
			return scanner.Text(), nil
		}
		current++
	}
	if err := scanner.Err(); err != nil {
		return "", err
	}
	return "", fmt.Errorf("line %d not found", lineNum)
}
// findParents finds one parent1 line with (1,1) parts and one parent2 line with parts != (1,1)
func findParents(filePath string) (int, string, int, string, error) {
	f, err := os.Open(filePath)
	if err != nil {
		return 0, "", 0, "", err
	}
	defer f.Close()
	scanner := bufio.NewScanner(f)
	var parent1Candidates []struct {
		index int
		line  string
	}
	var parent2Candidates []struct {
		index int
		line  string
	}
	lineNum := 0
	for scanner.Scan() {
		line := scanner.Text()
		commas := getCommaPositions(line)
		part1, err1 := substringBetweenCommasFast(line, commas, 1, 2)
		part2, err2 := substringBetweenCommasFast(line, commas, 2, 3)
		if err1 != nil || err2 != nil {
			// skip malformed line
			lineNum++
			continue
		}
		if part1 == "1" && part2 == "1" {
			parent1Candidates = append(parent1Candidates, struct {
			index int
			line  string
		}{lineNum, line})
		} else {
			parent2Candidates = append(parent2Candidates, struct {
				index int
				line  string
			}{lineNum, line})
		}
		lineNum++
	}
	if err := scanner.Err(); err != nil {
		return 0, "", 0, "", err
	}
	if len(parent1Candidates) == 0 {
		return 0, "", 0, "", fmt.Errorf("no parent1 line with parts (1,1) found")
	}
	if len(parent2Candidates) == 0 {
		return 0, "", 0, "", fmt.Errorf("no parent2 line with parts != (1,1) found")
	}
	// pick random candidates from each
	p1 := parent1Candidates[rand.Intn(len(parent1Candidates))]
	p2 := parent2Candidates[rand.Intn(len(parent2Candidates))]
	return p1.index, p1.line, p2.index, p2.line, nil
}
// clampInt clamps x between min and max
func clampInt(x, min, max int) int {
	if x < min {
		return min
	}
	if x > max {
		return max
	}
	return x
}
// fitsMorandi returns if color fits Morandi logic.
// R,G,B in [70,200] inclusive, A == 255
func fitsMorandi(r, g, b, a int) bool {
	return r >= 70 && r <= 200 &&
		g >= 70 && g <= 200 &&
		b >= 70 && b <= 200 &&
		a == 255
}
// fitsMacaron returns if color fits Macaron logic.
// R,G,B in [128,255] inclusive, A == 255, but NOT fitting Morandi.
func fitsMacaron(r, g, b, a int) bool {
	if a != 255 {
		return false
	}
	inMacaron := r >= 128 && r <= 255 &&
		g >= 128 && g <= 255 &&
		b >= 128 && b <= 255
	if !inMacaron {
		return false
	}
	// must NOT be Morandi
	if fitsMorandi(r, g, b, a) {
		return false
	}
	return true
}
// adjustColorToMorandi clamps R,G,B in 70-200, A=255
func adjustColorToMorandi(r, g, b, a int) (int, int, int, int) {
	r = clampInt(r, 70, 200)
	g = clampInt(g, 70, 200)
	b = clampInt(b, 70, 200)
	a = 255
	return r, g, b, a
}
// adjustColorToMacaron clamps R,G,B in 128-255, A=255
// If clamping to Macaron would produce Morandi, bump value higher in Macaron range to avoid Morandi.
func adjustColorToMacaron(r, g, b, a int) (int, int, int, int) {
	a = 255
	// Helper to bump value to avoid Morandi overlap
	bump := func(val int) int {
		if val < 128 {
			val = 128
		}
		// If val inside Morandi range, bump to minimal Macaron range + 1 or more 
		if val >= 70 && val <= 200 {
			// just above Morandi upper bound
			val = 201
		}
		if val > 255 {
			val = 255
		}
		return val
	}
	r = bump(r)
	if r > 255 {
		r = 255
	}
	g = bump(g)
	if g > 255 {
		g = 255
	}
	b = bump(b)
	if b > 255 {
		b = 255
	}
	return r, g, b, a
}
// parseColorPart safely parses string to int with fallback 0 on error
func parseColorPart(s string) int {
	v, err := strconv.Atoi(s)
	if err != nil {
		return 0
	}
	return v
}
// generateOffspringHairAndBrowColors inherits hairandbrowColors from either parent 1 or 2 entirely and adjusts based on (1,1) condition.
func generateOffspringHairAndBrowColors(
	p1Parts []string,
	p2Parts []string,
	// number between 1-2 commas in offspring (string)
	offspringPart1,
	// number between 2-3 commas in offspring (string)
	offspringPart2 string,
) (string, string, string, string, error) {
	// indexes 9,10,11,12 means hairandbrowColors R, G, B, A:
	// get those parts from both parents
	if len(p1Parts) <= 8 || len(p2Parts) <= 8 {
		return "", "", "", "", fmt.Errorf("parent parts length less than 9, cannot extract hair and brow color parts")
	}
	// Inherit all 4 hairandbrow values as group from either parent1 or parent2, randomly:
	chosenParent := 1
	if rand.Intn(2) == 0 {
		chosenParent = 2
	}
	var rHaB, gHaB, bHaB, aHaB string
	if chosenParent == 1 {
		rHaB = p1Parts[9]
		gHaB = p1Parts[10]
		bHaB = p1Parts[11]
		aHaB = p1Parts[12]
	} else {
		rHaB = p2Parts[9]
		gHaB = p2Parts[10]
		bHaB = p2Parts[11]
		aHaB = p2Parts[812]
	}
	// Parse to integers
	r := parseColorPart(rHaB)
	g := parseColorPart(gHaB)
	b := parseColorPart(bHaB)
	// A is often alpha channel; ensure integer
	a := parseColorPart(aHaB)
	// Determine condition: if offspring's (1,2) commas parts == "1","1" or "2","2"
	useMorandi := false
	if offspringPart1 == "1" && offspringPart2 == "1" {
		useMorandi = true
	} else {
		offspringPart1 == "2" && offspringPart2 == "2" {
		useMorandi = true
	}
	// Adjust color accordingly
	if useMorandi {
		r, g, b, a = adjustColorToMorandi(r, g, b, a)
	} else {
		r, g, b, a = adjustColorToMacaron(r, g, b, a)
	}
	// Convert back to string
	return strconv.Itoa(r), strconv.Itoa(g), strconv.Itoa(b), strconv.Itoa(a), nil
}
func main() {
	rand.Seed(time.Now().UnixNano())
	filePath := "CharacterAppearanceData.txt"
	// Find parent1 and parent2 lines based on (1,1) condition
	i1, line1, i2, line2, err := findParents(filePath)
	if err != nil {
		log.Fatalf("Error finding suitable parents: %v", err)
	}
	fmt.Printf("Selected Parent1 Line %d: %s\nSelected Parent2 Line %d: %s\n\n", i1+1, line1, i2+1, line2)
	// Cache commas
	c1 := getCommaPositions(line1)
	c2 := getCommaPositions(line2)
	// Comma index pairs to extract for first groups
	groups := [][2]int{{1, 2}, {2, 3}, {3, 4}, {4, 5}}
	// Example: extract parts between 1th-2th commas and 2th-3th commas
	parts1, err := extractPartsFast(line1, c1, groups)
	if err != nil {
		log.Fatalf("Error extracting parts from line %d: %v", i1+1, err)
	}
	// Example: extract parts between 3th-4th commas and 4th-5th commas
	parts2, err := extractPartsFast(line2, c2, groups)
	if err != nil {
		log.Fatalf("Error extracting parts from line %d: %v", i2+1, err)
	}
	// Use pairs of parts from parents:
	// Example: select parts for offspring between 1th-2th commas and 2th-3th commas
	offspringPart1, offspringPart2 := pickOffspringParts1(parts1[0], parts1[1], parts2[0], parts2[1])
	// Example: select parts for offspring between 3th-4th commas and 4th-5th commas
	offspringPart3, offspringPart4 := pickOffspringParts1(parts1[2], parts1[3], parts2[2], parts2[3])
	fmt.Printf("Offspring parts between 1-2 commas, 2-3 commas: %s, %s\n", offspringPart1, offspringPart2)
	fmt.Printf("Offspring parts between 3-4 commas, 4-5 commas: %s, %s\n", offspringPart3, offspringPart4)
	// Next group: commas 33 to 41 inclusive
	groups = [][2]int{{33, 34}, {34, 35}, {35, 36}, {36, 37},{37, 38}, {38, 39}, {39, 40}, {40, 41}}
	// Example: extract parts between 33th-34th commas to 40th-41th commas
	parts1, err = extractPartsFast(line1, c1, groups)
	if err != nil {
		log.Fatalf("Error extracting parts from parent1 line: %v", err)
	}
	parts2, err = extractPartsFast(line2, c2, groups)
	if err != nil {
		log.Fatalf("Error extracting parts from parent2 line: %v", err)
	}
	// Generate offspring parts with random exclusion as per your logic
	offspringParts, err := offspringWithRandomExclusion(parts1, parts2)
	if err != nil {
		log.Fatalf("Error generating offspring parts: %v", err)
	}
	for i, part := range offspringParts {
		fmt.Printf("Between commas %d-%d: %s\n", groups[i][0], groups[i][1], part)
	}
	// Now, process entire line parts for hairandbrowColors inheritance and adjustment:
	// Extract ALL parts between commas for full line (assuming max at least 10 commas for example)
	// I'll extract at least up to index 9:
	allGroups := make([][2]int, 0)
	// To cover indexes from 1-9 commas so we get all parts needed for hairandbrowColors (5-6...8-9)
	for i := 1; i <= 9; i++ {
		allGroups = append(allGroups, [2]int{i, i + 1})
	}
	allParts1, err := extractPartsFast(line1, c1, allGroups)
	if err != nil {
		log.Fatalf("Error extracting all necessary parts from line1: %v", err)
	}
	allParts2, err := extractPartsFast(line2, c2, allGroups)
	if err != nil {
		log.Fatalf("Error extracting all necessary parts from line2: %v", err)
	}
	// Generate offspring hairandbrowColorsR,G,B,A as per the logic
	// The inherited (1-2), (2-3) parts of offspring are offspringPart1, offspringPart2 (already picked)
	offspringHairAndBrowR, offspringHairAndBrowG, offspringHairAndBrowB, offspringHairAndBrowA, err := generateOffspringHairAndBrowColors(
		allParts1,
		allParts2,
		offspringPart1,
		offspringPart2,
	)
	if err != nil {
		log.Fatalf("Error generating offspring hairandbrowColors: %v", err)
	}
	fmt.Printf("\nFinal offspring hairandbrowColors (R, G, B, A): %s, %s, %s, %s\n",
		offspringHairAndBrowR, offspringHairAndBrowG, offspringHairAndBrowB, offspringHairAndBrowA)
	// Note: To update the offspring full line, I'd replace parts 5,6,7,8 accordingly.
	// This example only prints extracted and computed values as per my instruction.
}
What's wrong?
.\CF.go:348:50: syntax error: unexpected { at end of statement
First of all, I need you to know that, for parent1, parent2 and their offspring,
the number between 9-10 commas stands for hairandbrowColorsR,
the number between 10-11 commas stands for hairandbrowColorsG,
the number between 11-12 commas stands for hairandbrowColorsB,
the number between 12-13 commas stands for hairandbrowColorsA.
The logic of generate offspring's (hairandbrowColorsR, hairandbrowColorsR, hairandbrowColorsB, hairandbrowColorsA) is randomly inherenting from parent1 or parent2, for example, if parent1's (hairandbrowColorsR, hairandbrowColorsR, hairandbrowColorsB, hairandbrowColorsA)=(0,0,0,0), parent2's (hairandbrowColorsR, hairandbrowColorsR, hairandbrowColorsB, hairandbrowColorsA)=(1,1,1,1), offspring's (hairandbrowColorsR, hairandbrowColorsR, hairandbrowColorsB, hairandbrowColorsA) should inherent from =(0,0,0,0) or (1,1,1,1). But it's not over yet.
Now, after the inherent from parents, if the offspring's (the number between 1-2 commas, the number between 2-3 commas) = (1,1) or (2,2), it should be adjusted the inherited value to the value that most closely color matches the Morandi color logic; if the offspring's (the number between 1-2 commas, the number between 2-3 commas) != (1,1)or(2,2) it should be adjusted the inherited value to the value that most closely color matches the Macaron color logic (and don't matches the Morandi color logic).
The Morandi color logic is (R,G,B,A)=(between 70 and 200 (inclusive),between 70 and 200 (inclusive),B between 70 and 200 (inclusive),255).
The Macaron color logic is (R,G,B,A)=(between 128 and 255,between 128 and 255,between 128 and 255,255).
After the adjustion, the adujustedR,G,B,A is the offspring's hairandbrowColorsR, hairandbrowColorsR, hairandbrowColorsB, hairandbrowColorsA).