01020304050607080910111213141516171819202122232425

Advent of Code

2015/21

RPG Simulator 20XX

in C#

by encse

Little Henry Case got a new video game for Christmas. It's an RPG, and he's stuck on a boss. He needs to know what equipment to buy at the shop. He hands you the controller.

In this game, the player (you) and the enemy (the boss) take turns attacking. The player always goes first. Each attack reduces the opponent's hit points by at least 1. The first character at or below 0 hit points loses.

Read the full puzzle.

using System;
using System.Collections.Generic;
using System.Linq;

namespace AdventOfCode.Y2015.Day21;

[ProblemName("RPG Simulator 20XX")]
class Solution : Solver {

    public object PartOne(string input) {
        var boss = Parse(input);
        var minGold = int.MaxValue;
        foreach (var c in Buy()) {
            if (DefeatsBoss((c.damage, c.armor, 100), boss)) {
                minGold = Math.Min(c.gold, minGold);
            }
        }
        return minGold;
    }

    public object PartTwo(string input) {
        var boss = Parse(input);
        var maxGold = 0;
        foreach (var c in Buy()) {
            if (!DefeatsBoss((c.damage, c.armor, 100), boss)) {
                maxGold = Math.Max(c.gold, maxGold);
            }
        }
        return maxGold;
    }

    (int damage, int armor, int hp) Parse(string input) {
        var lines = input.Split("\n");
        var hp = int.Parse(lines[0].Split(": ")[1]);
        var damage = int.Parse(lines[1].Split(": ")[1]);
        var armor = int.Parse(lines[2].Split(": ")[1]);
        return (damage, armor, hp);
    }

    bool DefeatsBoss((int damage, int armor, int hp) player, (int damage, int armor, int hp) boss) {
        while (true) {
            boss.hp -= Math.Max(player.damage - boss.armor, 1);
            if (boss.hp <= 0) {
                return true;
            }

            player.hp -= Math.Max(boss.damage - player.armor, 1);
            if (player.hp <= 0) {
                return false;
            }
        }
    }

    IEnumerable<(int gold, int damage, int armor)> Buy() {
        return
            from weapon in Buy(1, 1, new[] { (8, 4, 0), (10, 5, 0), (25, 6, 0), (40, 7, 0), (74, 8, 0) })
            from armor in Buy(0, 1, new[] { (13, 0, 1), (31, 0, 2), (53, 0, 3), (75, 0, 4), (102, 0, 5) })
            from ring in Buy(1, 2, new[] { (25, 1, 0), (50, 2, 0), (100, 3, 0), (20, 0, 1), (40, 0, 2), (80, 0, 3) })
            select Sum(weapon, armor, ring);
    }

    IEnumerable<(int gold, int damage, int armor)> Buy(int min, int max, (int gold, int damage, int armor)[] items) {
        if (min == 0) {
            yield return (0, 0, 0);
        }

        foreach (var item in items) {
            yield return item;
        }

        if (max == 2) {
            for (int i = 0; i < items.Length; i++) {
                for (int j = i + 1; j < items.Length; j++) {
                    yield return Sum(items[i], items[j]);
                }
            }
        }
    }

    (int gold, int damage, int armor) Sum(params (int gold, int damage, int armor)[] items) {
        return (items.Select(item => item.gold).Sum(), items.Select(item => item.damage).Sum(), items.Select(item => item.armor).Sum());
    }

}

Please ☆ my repo if you like it!

© 2025 Advent of Code is a registered trademark in the US Images provided by Bing image creator