package main import ( "bufio" "fmt" "os" "strconv" "strings" ) func main() { var input []string scanner := bufio.NewScanner(os.Stdin) for scanner.Scan() { input = append(input, scanner.Text()) } transforms := make(map[string][]string) var stMolecule string for i := range input { if strings.Contains(input[i], "=>") { parts := strings.Split(input[i], " => ") transforms[parts[0]] = append(transforms[parts[0]], parts[1]) } else if len(input[i]) > 0 { // Must be the calibration molecule stMolecule = input[i] } } fmt.Println("*** CALIBRATION ***") res := calibrate(stMolecule, transforms) fmt.Println("Found " + strconv.Itoa(len(res)) + " combinations") fmt.Println("\n") fmt.Println("*** MANUFACTURING ***") backTrans := reverseTransforms(transforms) manufacture(stMolecule, backTrans, 0) } // stepBack takes an ending string and a reversed list of the transformations func manufacture(molecule string, transforms map[string][]string, steps int) { fmt.Println(molecule) if molecule == "e" { fmt.Println("Found in " + strconv.Itoa(steps) + " steps") os.Exit(0) } for k, v := range transforms { if strings.Contains(molecule, k) { for i := range v { tstVal := strings.Replace(molecule, k, v[i], 1) manufacture(tstVal, transforms, steps+1) } } } } func reverseTransforms(t map[string][]string) map[string][]string { ret := make(map[string][]string) for v := range t { for j := range t[v] { ret[t[v][j]] = append(ret[t[v][j]], v) } } return ret } func calibrate(molecule string, transforms map[string][]string) []string { var ret []string atoms := getAtomsFromMolecule(molecule) for i := range atoms { // Build a string of atoms before i start := strings.Join(atoms[:i], "") // And all after i too end := strings.Join(atoms[i+1:], "") // Find all transforms for atoms[i] if v, ok := transforms[atoms[i]]; ok { for j := range v { ret = uniqueInsert(ret, start+v[j]+end) //ret = append(ret, start+v[j]+end) } } } return ret } func getAtomsFromMolecule(molecule string) []string { var atoms []string var bldAtom string for i := range molecule { if molecule[i] >= 'A' && molecule[i] <= 'Z' { if len(bldAtom) > 0 { atoms = append(atoms, bldAtom) } bldAtom = string(molecule[i]) continue } bldAtom = bldAtom + string(molecule[i]) } return append(atoms, bldAtom) } func uniqueInsert(curr []string, ins string) []string { for i := range curr { if curr[i] == ins { return curr } } return append(curr, ins) } func mustAtoi(s string) int { var i int var err error if i, err = strconv.Atoi(s); err != nil { fmt.Println("Tried to atoi " + s) os.Exit(1) } return i }