package day21 import ( "math" "strconv" "strings" "tools" ) var armor = map[string]map[string]int{ "none": { "cost": 0, "armor": 0, }, "Leather": { "cost": 13, "armor": 1, }, "Chainmail": { "cost": 31, "armor": 2, }, "Splintmail": { "cost": 53, "armor": 3, }, "Bandedmail": { "cost": 75, "armor": 4, }, "Platemail": { "cost": 102, "armor": 5, }, } var weapons = map[string]map[string]int{ "Dagger": { "cost": 8, "damage": 4, }, "Shortsword": { "cost": 10, "damage": 5, }, "Warhammer": { "cost": 25, "damage": 6, }, "Longsword": { "cost": 40, "damage": 7, }, "Greataxe": { "cost": 74, "damage": 8, }, } var rings = map[string]map[string]int{ "none1": { "cost": 0, "damage": 0, "armor": 0, }, "none2": { "cost": 0, "damage": 0, "armor": 0, }, "Damage +1": { "cost": 25, "damage": 1, "armor": 0, }, "Damage +2": { "cost": 50, "damage": 2, "armor": 0, }, "Damage +3": { "cost": 100, "damage": 3, "armor": 0, }, "Armor +1": { "cost": 20, "damage": 0, "armor": 1, }, "Armor +2": { "cost": 40, "damage": 0, "armor": 2, }, "Armor +3": { "cost": 80, "damage": 0, "armor": 3, }, } func fight(playerHp, playerPower, playerArmor, bossHp, bossPower, bossArmor int) bool { playerStrike := float64(tools.Max(1, playerPower-bossArmor)) bossStrike := float64(tools.Max(1, bossPower-playerArmor)) return math.Ceil(float64(bossHp)/playerStrike) <= math.Ceil(float64(playerHp)/bossStrike) } func Part1(puzzle tools.AoCPuzzle) interface{} { input := puzzle.GetInputArray() hpLine := strings.Split(input[0], ": ") powerLine := strings.Split(input[1], ": ") armorLine := strings.Split(input[2], ": ") bossHp, _ := strconv.Atoi(hpLine[1]) bossPower, _ := strconv.Atoi(powerLine[1]) bossArmor, _ := strconv.Atoi(armorLine[1]) playerHp := 100 playerPower := 0 playerArmor := 0 goldSpent := 0 minGold := math.MaxInt32 // completely ignoring rings for p1 as they are way too expensive in any case for _, weaponStats := range weapons { for _, armorStats := range armor { goldSpent = weaponStats["cost"] + armorStats["cost"] playerPower = weaponStats["damage"] playerArmor = armorStats["armor"] if fight(playerHp, playerPower, playerArmor, bossHp, bossPower, bossArmor) && goldSpent < minGold { minGold = goldSpent } } } // REVISIT: The "correct" answer according to AoC website was 78, but that doesn't work! // The only way to spent 78 Gold is by buying a Warhammer and the Splintmail // That put the player damage at 6(weapon)-1(bossArmor) == 5 // And the boss damage becomes 8(input)-3(armor) == 5 // But the boss has 104 HP and the player only 100. // Leaving the player dead after 20 Rounds with the boss still having 4 HP return minGold } func Part2(puzzle tools.AoCPuzzle) interface{} { input := puzzle.GetInputArray() hpLine := strings.Split(input[0], ": ") powerLine := strings.Split(input[1], ": ") armorLine := strings.Split(input[2], ": ") bossHp, _ := strconv.Atoi(hpLine[1]) bossPower, _ := strconv.Atoi(powerLine[1]) bossArmor, _ := strconv.Atoi(armorLine[1]) playerHp := 100 playerPower := 0 playerArmor := 0 goldSpent := 0 maxGold := 0 for _, weaponStats := range weapons { for _, armorStats := range armor { for ring1Name, ring1Stats := range rings { for ring2Name, ring2Stats := range rings { if ring1Name == ring2Name { continue } goldSpent = weaponStats["cost"] + armorStats["cost"] + ring1Stats["cost"] + ring2Stats["cost"] playerPower = weaponStats["damage"] + ring1Stats["damage"] + ring2Stats["damage"] playerArmor = armorStats["armor"] + ring1Stats["armor"] + ring2Stats["armor"] if !fight(playerHp, playerPower, playerArmor, bossHp, bossPower, bossArmor) && goldSpent > maxGold { maxGold = goldSpent } } } } } return maxGold }