Can you optimize this golang code? Make it memory friendly.
package main
import (
	"bufio"
	"fmt"
	"log"
	"math/rand"
	"os"
	"strconv"
	"time"
)
// substringBetweenCommas extracts substring between nth and mth commas (1-based)
func substringBetweenCommas(line string, nth, mth int) (string, error) {
	if nth >= mth {
		return "", fmt.Errorf("invalid indices: nth >= mth")
	}
	commas := []int{}
	for i, c := range line {
		if c == ',' {
			commas = append(commas, i)
		}
	}
	// Check if having enough commas
	if mth > len(commas) {
		return "", fmt.Errorf("not enough commas")
	}
	start := 0
	if nth > 0 {
		// +1 to skip comma itself
		start = commas[nth-1] + 1
	}
	end := commas[mth-1]
	if start > end || start > len(line) || end > len(line) {
		return "", fmt.Errorf("invalid substring indices")
	}
	return line[start:end], nil
}
// extractParts extracts multiple parts between specified comma indexes
func extractParts(line string, pairs [][2]int) ([]string, error) {
	parts := make([]string, 0, len(pairs))
	for _, pair := range pairs {
		part, err := substringBetweenCommas(line, pair[0], pair[1])
		if err != nil {
			return nil, err
		}
		parts = append(parts, 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
}
// produce 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
}
func main() {
	rand.Seed(time.Now().UnixNano())
	filePath := "CharacterAppearanceData.txt"
	// lines to grab (1-based lines)
	lines, err := readLines(filePath)
	if err != nil {
		log.Fatal(err)
	}
	if len(lines) < 2 {
		log.Fatal("File must have at least two lines")
	}
	// Pick two distinct random lines
	i1 := rand.Intn(len(lines))
	i2 := rand.Intn(len(lines) - 1)
	if i2 >= i1 {
		i2++
	}
	line1 := lines[i1]
	line2 := lines[i2]
	fmt.Printf("Selected lines: %d and %d\n\n", i1+1, i2+1)
	fmt.Printf("Line %d: %s\nLine %d: %s\n\n", i1+1, line1, i2+1, line2)
	// Comma index pairs to extract for each group
	groups := [][2]int{{1, 2}, {2, 3}, {3, 4}, {4, 5}}
	// Example: extract parts between 1th-2th commas and 2th-3th commas
	parts1, err := extractParts(line1, 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 := extractParts(line2, 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)
	// Comma index pairs to extract for each group
	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 = extractParts(line1, groups)
	if err != nil {
		log.Fatalf("Error extracting parts from parent1 line: %v", err)
	}
	parts2, err = extractParts(line2, groups)
	if err != nil {
		log.Fatalf("Error extracting parts from parent2 line: %v", err)
	}
	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)
	}
}
// readLines reads file lines into a slice
func readLines(filepath string) ([]string, error) {
	f, err := os.Open(filepath)
	if err != nil {
		return nil, err
	}
	defer f.Close()
	var lines []string
	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		lines = append(lines, scanner.Text())
	}
	return lines, scanner.Err()
}