package main
import (
    "bufio"
    "fmt"
    "math/rand"
    "os"
    "strconv"
    "strings"
    "time"
)
// The file where Character data will be stored
const filename = "CharacterAppearanceData.txt"
// Function to generate random bloodline DNA
func GenerateRandomBloodline() ([]int, []float64) {
	const totalPercentage = 100.0
	// Slice to hold percentages for each bloodline
	percentages := make([]float64, 100)
	// Seed rand with the current time
	rand.Seed(time.Now().UnixNano())
	// Randomly generate percentages for each bloodline
	for i := 0; i < 100; i++ {
		percentages[i] = rand.Float64() * totalPercentage
	}
	// Normalize the percentages to sum to 100%
	sum := 0.0
	for _, value := range percentages {
		sum += value
	}
	for i := range percentages {
		percentages[i] = (percentages[i] / sum) * totalPercentage
		// Count to forth decimal places and round to three decimal places
		percentages[i] = roundToThreeDecimal(percentages[i])
	}
	bloodlineNumbers := make([]int, 100)
	for i := 0; i < 100; i++ {
		// Assign bloodline IDs 1 through 100
		bloodlineNumbers[i] = i + 1
	}
	return bloodlineNumbers, percentages
}
// Round a float64 to three decimal places
func roundToThreeDecimal(value float64) float64 {
	return float64(int(value*1000+0.5)) / 1000.0
}
// Function to find the ID of the bloodline with the highest percentage
func FindMaxID(ids []int, percentages []float64) int {
	maxID := ids[0]
	maxValue := percentages[0]
	for i := 1; i < len(percentages); i++ {
		if percentages[i] > maxValue {
			maxValue = percentages[i]
			maxID = ids[i]
		}
	}
	return maxID
}
// Generate Morandi color
func generateMorandiColor() []int {
	return []int{
		// R between 70 and 200 (inclusive)
		rand.Intn(131) + 70,
		// G between 70 and 200 (inclusive)
		rand.Intn(131) + 70,
		// B between 70 and 200 (inclusive)
		rand.Intn(131) + 70,
		// A = 255 (fully opaque)
		255,
	}
}
// Generate Macaron color
func generateMacaronColor() []int {
	return []int{
		// R between 128 and 255 (inclusive)
		rand.Intn(128) + 128,
		// G between 128 and 255 (inclusive)
		rand.Intn(128) + 128,
		// B between 128 and 255 (inclusive)
		rand.Intn(128) + 128,
		// A = 255 (fully opaque)
		255,
	}
}
// Generate No color
func generateNoColor() []int {
	return []int{
		// R = 255
		255,
		// G = 255
		255,
		// B = 255
		255,
		// A = 0 (fully transparent)
		0,
	}
}
// Declare color palettes at the top
var morandiColorPalette = generateMorandiColor()
var macaronColorPalette = generateMacaronColor()
var noColorPalette = generateNoColor()
// Function to generate a random NPC
func generateNPC() []interface{} {
	// Get the next sequential NPC ID
    playerID := getNextID()
	// Randomly generate first sex genes between 1 and 2 (inclusive)
	firstsexgene1 := rand.Intn(2) + 1
    firstsexgene2 := rand.Intn(2) + 1
	// Randomly generate second sex genes between 1 and 3 (inclusive)
    secondsexgene1 := rand.Intn(3) + 1
    secondsexgene2 := rand.Intn(3) + 1
	// Random skin colors based on realistic skin color
	skinColors := []int{
		// R between 200 and 255 (inclusive)
		rand.Intn(56) + 200,
		// G between 100 and 150 (inclusive)
		rand.Intn(51) + 100,
		// B between 80 and 130 (inclusive)
		rand.Intn(51) + 80,
		// A = 255 (fully opaque)
		255,
    }
	// Determine eye color, tail color, wing color and antenna color based on first sex genes
	hairandbrowColors := []int{}
	eyeColors := []int{}
	tailColors := []int{}
	wingColors := []int{}
	antennaColors := []int{}
	// Both firstsexgene1 and firstsexgene2 are 1
	if firstsexgene1 == 1 && firstsexgene2 == 1 {
		hairandbrowColors = morandiColorPalette
		eyeColors = morandiColorPalette
		tailColors = morandiColorPalette
		wingColors = noColorPalette
		antennaColors = noColorPalette
	// Both firstsexgene1 and firstsexgene2 are 2
	} else if firstsexgene1 == 2 && firstsexgene2 == 2 {
		hairandbrowColors = morandiColorPalette
		eyeColors = morandiColorPalette
		tailColors = noColorPalette
		wingColors = noColorPalette
		antennaColors = macaronColorPalette
	// Other combinations
	} else {
		hairandbrowColors = macaronColorPalette
		eyeColors = macaronColorPalette
		tailColors = noColorPalette
		wingColors = macaronColorPalette
		antennaColors = macaronColorPalette
	}
	// Other attributes
	// Randomly generate face shape variations between 1 and 10 (inclusive)
	faceshape := rand.Intn(10) + 1
	// Randomly generate brow shape variations between 1 and 10 (inclusive)
	browshape := rand.Intn(10) + 1
	// Randomly generate eye shape variations between 1 and 10 (inclusive)
    eyeshape := rand.Intn(10) + 1
	// Randomly generate ear shape variations between 1 and 10 (inclusive)
    earshape := rand.Intn(10) + 1
	// Randomly generate nose shape variations between 1 and 10 (inclusive)
    noseshape := rand.Intn(10) + 1
	// Randomly generate mouth shape variations between 1 and 10 (inclusive)
    mouthshape := rand.Intn(10) + 1
	// Randomly generate front hair shape variations between 1 and 10 (inclusive)
    fronthairshape := rand.Intn(10) + 1
	// Randomly generate back hair shape variations between 1 and 10 (inclusive)
    backhairshape := rand.Intn(10) + 1
	// Determine body shape based on first sex genes
	var bodyshape int
	bodyshape = determineBodyShape(firstsexgene1, firstsexgene2)
	// Add bloodline data and percentages
	bloodlineIDs, bloodlinePercentages := GenerateRandomBloodline()
	// Get the max ID based on percentages
	maxID := FindMaxID(bloodlineIDs, bloodlinePercentages)
	// Determine tail shape, tooth shape, wing shape and antenna shape based on first sex genes
	tailshape, toothshape, wingshape, antennashape := determineShapes(firstsexgene1, firstsexgene2, maxID)
	// Append ID and sex data to output
	output := []interface{}{playerID, firstsexgene1, firstsexgene2, secondsexgene1, secondsexgene2}
	// Append skin color data (convert to []interface{} before append) to output
	output = append(output, intSliceToInterfaceSlice(skinColors)...)
	// Append hair and brow color data (convert to []interface{} before append) to output
	output = append(output, intSliceToInterfaceSlice(hairandbrowColors)...)
	// Append eye color data (convert to []interface{} before append) to output
	output = append(output, intSliceToInterfaceSlice(eyeColors)...)
	// Append tail color data (convert to []interface{} before append) to output
	output = append(output, intSliceToInterfaceSlice(tailColors)...)
	// Append wing color data (convert to []interface{} before append) to output
	output = append(output, intSliceToInterfaceSlice(wingColors)...)
	// Append antenna color data (convert to []interface{} before append) to output
	output = append(output, intSliceToInterfaceSlice(antennaColors)...)
	// Append face shape, brow shape, eye shape, ear shape, nose shape, mouth shape, front hair shape, back hair shape, body shape, tail shape, tooth shape, wing shape, antenna shape data to output
	output = append(output, faceshape, browshape, eyeshape, earshape, noseshape, mouthshape, fronthairshape, backhairshape, bodyshape, tailshape, toothshape, wingshape, antennashape)
	for i := 0; i < len(bloodlineIDs); i++ {
		// Add each bloodline ID and its percentage
		output = append(output, bloodlineIDs[i], bloodlinePercentages[i])
	}
	// Add the bloodline maxID to the output
	output = append(output, maxID)
	// Return the fully formed NPC with bloodline data
	return output
}
// Function to determine body shape
func determineBodyShape(firstsexgene1, firstsexgene2 int) int {
	// Both firstsexgene1 and firstsexgene2 are 1
	if firstsexgene1 == 1 && firstsexgene2 == 1 {
		// Randomly generate body shape variations between 1 and 3 (inclusive)
		return rand.Intn(3) + 1
	// Both firstsexgene1 and firstsexgene2 are 2
	} else if firstsexgene1 == 2 && firstsexgene2 == 2 {
		// Randomly generate body shape variations between 7 and 9 (inclusive)
		return rand.Intn(3) + 7
	// Other combinations
	} else {
		// Randomly generate body shape variations between 4 and 6 (inclusive)
		return rand.Intn(3) + 4
	}
}
// Function to determine tail shape, tooth shape, wing shape and antenna shape
func determineShapes(firstsexgene1, firstsexgene2, maxID int) (int, int, int, int) {
var tailshape, toothshape, wingshape, antennashape int
	// Both firstsexgene1 and firstsexgene2 are 1
	if firstsexgene1 == 1 && firstsexgene2 == 1 {
		// Randomly generate tail shape variations between 1 and 100 (inclusive)
		tailshape = maxID
		// Randomly generate tooth shape variations between 1 and 100 (inclusive)
		toothshape = maxID
		// Randomly generate wing shape variation 101 (no wing)
		wingshape = 101
		// Randomly generate antenna shape variation 101 (no antenna)
		antennashape = 101
	// Both firstsexgene1 and firstsexgene2 are 2
	if firstsexgene1 == 2 && firstsexgene2 == 2 {
		// Randomly generate tail shape variation 101 (no tail)
		tailshape = 101
		// Randomly generate tooth shape variation 101 (no tooth)
		toothshape = 101
		// Randomly generate wing shape variation 101 (no wing)
		wingshape = 101
		// Randomly generate antenna shape variations between 1 and 100 (inclusive)
		antennashape = maxID
	// Other combinations
	} else {
		// Randomly generate tail shape variation 101 (no tail)
		tailshape = 101
		// Randomly generate tooth shape variation 101 (no tooth)
		toothshape = 101
		// Randomly generate wing shape variations between 1 and 100 (inclusive)
		wingshape = maxID
		// Randomly generate antenna shape variations between 1 and 100 (inclusive)
		antennashape = maxID
	}
	return tailshape, toothshape, wingshape, antennashape
}
// Function to convert []int to []interface{}
func intSliceToInterfaceSlice(ints []int) []interface{} {
	ifaces := make([]interface{}, len(ints))
	for i, v := range ints {
	    ifaces[i] = v
    }
    return ifaces
}
// Function to read existing data from the file and find the next ID
func getNextID() int {
	// Check if the file exists
	if _, err := os.Stat(filename); os.IsNotExist(err) {
		// If the file doesn't exist, return ID 1
		return 1
	}
	file, err := os.Open(filename)
	if err != nil {
		// If unable to open the file, return ID 1
		return 1
	}
	defer file.Close()
	maxID := 0
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		// Trim the line for any whitespace
		line := strings.TrimSpace(scanner.Text())
		// Skip empty lines
		if line == "" {
			continue
		}
		// Split by commas
		parts := strings.Split(line, ",")
		if len(parts) > 0 {
			// Assuming IDs start with "[c/" prefix
			idStr := strings.Trim(parts[0], "[c/")
			// Trim the closing bracket
			idStr = strings.Trim(idStr, "]")
			// Parse the ID
			id, err := strconv.Atoi(idStr)
			if err == nil {
				if id > maxID {
					// Keep track of the maximum valid ID
					maxID = id
				}
			}
		}
	}
	// Debugging information
	if err := scanner.Err(); err != nil {
		fmt.Println("Error reading from file:", err)
	}
	// Next ID is maxID + 1, starting from 1 if there are no previous IDs
	if maxID == 0 {
		// If no valid IDs were found, return 1
		return 1
	}
	// Next ID is maxID + 1
	return maxID + 1
}
// Function to format output as expected
func formatOutput(output []interface{}) string {
	stringOutput := make([]string, len(output))
	for i, v := range output {
		// Convert each element to a string
		stringOutput[i] = fmt.Sprintf("%v", v)
	}
	return fmt.Sprintf("[c/%s]", strings.Join(stringOutput, ","))
}
// Function to append the generated NPC output to the text file
func appendToFile(data string) {
	file, err := os.OpenFile(filename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		fmt.Println("Error opening file:", err)
		return
	}
	defer file.Close()
	if _, err := file.WriteString(data + "\n"); err != nil {
		fmt.Println("Error writing to file:", err)
	}
}
// Start NPC generation
func main() {
	rand.Seed(time.Now().UnixNano())
	var input string
	fmt.Println("Press Enter to generate a random NPC...")
	// Infinite loop allowing repeated NPC generation
	for {
		// Wait for user to press Enter
		fmt.Scanln(&input)
		// Generate and output the NPC
		npc := generateNPC()
		npcOutput := formatOutput(npc)
		fmt.Println(npcOutput)
		// Append the NPC output to the text file
		appendToFile(npcOutput)
	}
}
# command-line-arguments
./main.go:253:6: syntax error: unexpected name intSliceToInterfaceSlice, expected (
./main.go:261:6: syntax error: unexpected name getNextID, expected (
./main.go:263:2: syntax error: unexpected if at end of statement