-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add puzzle1 * Add algorithm. * Add some types * Define some variables. * Implement equipCharacter * Implement generateWeapon, generateArmor, generateRings * Implement modifyCharWithEquip * Implement charAAttacksCharB * Implement simulateFight * Find solution for puzzle1 * Find solution for puzzle2 * Add puzzle1 for day 22 * Update algorithm * Partially implement castSpell * Implement isSpellAvailable * Add first test for applyEffects * Use spellCost * Implement apply shielded effect * Implement apply recharging effect * Implement apply poisoned effect * Implement isThereAWinner * Implement getAvailableSpells * Solve demo example * Solve demo example II * Cleanup solve puzzle1 * Solve puzzle2. * Cleanup
- Loading branch information
1 parent
9402d7d
commit 6fa5e76
Showing
7 changed files
with
1,548 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
Hit Points: 109 | ||
Damage: 8 | ||
Armor: 2 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
/* | ||
--- Day 21: RPG Simulator 20XX --- | ||
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. | ||
Damage dealt by an attacker each turn is equal to the attacker's damage score minus the defender's armor score. | ||
An attacker always does at least 1 damage. | ||
So, if the attacker has a damage score of 8, and the defender has an armor score of 3, the defender loses 5 hit points. | ||
If the defender had an armor score of 300, the defender would still lose 1 hit point. | ||
Your damage score and armor score both start at zero. | ||
They can be increased by buying items in exchange for gold. | ||
You start with no items and have as much gold as you need. | ||
Your total damage or armor is equal to the sum of those stats from all of your items. | ||
You have 100 hit points. | ||
Here is what the item shop is selling: | ||
Weapons: Cost Damage Armor | ||
Dagger 8 4 0 | ||
Shortsword 10 5 0 | ||
Warhammer 25 6 0 | ||
Longsword 40 7 0 | ||
Greataxe 74 8 0 | ||
Armor: Cost Damage Armor | ||
Leather 13 0 1 | ||
Chainmail 31 0 2 | ||
Splintmail 53 0 3 | ||
Bandedmail 75 0 4 | ||
Platemail 102 0 5 | ||
Rings: Cost Damage Armor | ||
Damage +1 25 1 0 | ||
Damage +2 50 2 0 | ||
Damage +3 100 3 0 | ||
Defense +1 20 0 1 | ||
Defense +2 40 0 2 | ||
Defense +3 80 0 3 | ||
You must buy exactly one weapon; no dual-wielding. | ||
Armor is optional, but you can't use more than one. | ||
You can buy 0-2 rings (at most one for each hand). | ||
You must use any items you buy. The shop only has one of each item, so you can't buy, for example, two rings of Damage +3. | ||
For example, suppose you have 8 hit points, 5 damage, and 5 armor, and that the boss has 12 hit points, 7 damage, and 2 armor: | ||
The player deals 5-2 = 3 damage; the boss goes down to 9 hit points. | ||
The boss deals 7-5 = 2 damage; the player goes down to 6 hit points. | ||
The player deals 5-2 = 3 damage; the boss goes down to 6 hit points. | ||
The boss deals 7-5 = 2 damage; the player goes down to 4 hit points. | ||
The player deals 5-2 = 3 damage; the boss goes down to 3 hit points. | ||
The boss deals 7-5 = 2 damage; the player goes down to 2 hit points. | ||
The player deals 5-2 = 3 damage; the boss goes down to 0 hit points. | ||
In this scenario, the player wins! (Barely.) | ||
You have 100 hit points. The boss's actual stats are in your puzzle input. What is the least amount of gold you can spend and still win the fight? | ||
*/ | ||
|
||
// Algorithm | ||
|
||
// create your basic character | ||
import { Character, modifyCharWithEquip, simulateFight } from "./utils.ts"; | ||
|
||
const basicPlayer: Character = { | ||
hitPoints: 100, | ||
damage: 0, | ||
defense: 0, | ||
}; | ||
|
||
// create boss character (read file) | ||
const boss: Character = { | ||
hitPoints: 109, | ||
damage: 8, | ||
defense: 2, | ||
}; | ||
|
||
// create list of items in shop | ||
|
||
// create board with prices | ||
const board: { character: Character; goldSpent: number }[] = []; | ||
|
||
// loop | ||
// create variations of armor (make sure to safe it's complete price) | ||
// modify your basic character | ||
for (const playerWithEquip of modifyCharWithEquip(basicPlayer)) { | ||
// simulate fight | ||
const playerWins = simulateFight( | ||
playerWithEquip.character, | ||
Object.assign({}, boss), | ||
); | ||
// if you win, store price into an board | ||
if (playerWins) { | ||
board.push(playerWithEquip); | ||
} | ||
} | ||
console.log("min price ", Math.min(...board.map(({ goldSpent }) => goldSpent))); | ||
console.log("winner ", board.find(({ goldSpent }) => goldSpent === 111)); | ||
// find min price | ||
|
||
// find max price and loose | ||
|
||
const looseBoard: { character: Character; goldSpent: number }[] = []; | ||
|
||
for (const playerWithEquip of modifyCharWithEquip(basicPlayer)) { | ||
// simulate fight | ||
const playerWins = simulateFight( | ||
playerWithEquip.character, | ||
Object.assign({}, boss), | ||
); | ||
// if you win, store price into an looseBoard | ||
if (!playerWins) { | ||
looseBoard.push(playerWithEquip); | ||
} | ||
} | ||
console.log( | ||
"Max price ", | ||
Math.max(...looseBoard.map(({ goldSpent }) => goldSpent)), | ||
); | ||
console.log( | ||
"looser with most expensive armor ", | ||
looseBoard.find(({ goldSpent }) => goldSpent === 188), | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
import { assert, assertEquals, assertThrows } from "@std/assert"; | ||
import { beforeEach, describe, it } from "@std/testing/bdd"; | ||
import { | ||
allArmors, | ||
allRings, | ||
allWeapons, | ||
charAAttacksCharB, | ||
Character, | ||
equipCharacter, | ||
generateRings, | ||
simulateFight, | ||
} from "./utils.ts"; | ||
|
||
describe("equipCharacter", function () { | ||
it("should equip character with some items", function () { | ||
const character: Character = { | ||
hitPoints: 100, | ||
damage: 0, | ||
defense: 0, | ||
}; | ||
const armor = allArmors[0]; | ||
const weapon = allWeapons[0]; | ||
const ring = allRings[0]; | ||
assertEquals( | ||
equipCharacter(character, { | ||
armor, | ||
weapon, | ||
ringsLeft: ring, | ||
}), | ||
{ | ||
character: { | ||
...character, | ||
defense: armor.defense + weapon.defense + ring.defense, | ||
damage: armor.damage + weapon.damage + ring.damage, | ||
}, | ||
goldSpent: armor.cost + weapon.cost + ring.cost, | ||
}, | ||
); | ||
}); | ||
}); | ||
|
||
describe("generateRings", function () { | ||
it("should generate all variations for rings", function () { | ||
assertEquals([...generateRings()], [ | ||
[undefined, undefined], | ||
[allRings[0], undefined], | ||
[allRings[1], undefined], | ||
[allRings[2], undefined], | ||
[allRings[3], undefined], | ||
[allRings[4], undefined], | ||
[allRings[5], undefined], | ||
[allRings[0], allRings[1]], | ||
[allRings[0], allRings[2]], | ||
[allRings[0], allRings[3]], | ||
[allRings[0], allRings[4]], | ||
[allRings[0], allRings[5]], | ||
[allRings[1], allRings[0]], | ||
[allRings[1], allRings[2]], | ||
[allRings[1], allRings[3]], | ||
[allRings[1], allRings[4]], | ||
[allRings[1], allRings[5]], | ||
[allRings[2], allRings[0]], | ||
[allRings[2], allRings[1]], | ||
[allRings[2], allRings[3]], | ||
[allRings[2], allRings[4]], | ||
[allRings[2], allRings[5]], | ||
[allRings[3], allRings[0]], | ||
[allRings[3], allRings[1]], | ||
[allRings[3], allRings[2]], | ||
[allRings[3], allRings[4]], | ||
[allRings[3], allRings[5]], | ||
[allRings[4], allRings[0]], | ||
[allRings[4], allRings[1]], | ||
[allRings[4], allRings[2]], | ||
[allRings[4], allRings[3]], | ||
[allRings[4], allRings[5]], | ||
[allRings[5], allRings[0]], | ||
[allRings[5], allRings[1]], | ||
[allRings[5], allRings[2]], | ||
[allRings[5], allRings[3]], | ||
[allRings[5], allRings[4]], | ||
]); | ||
}); | ||
}); | ||
|
||
describe("charAAttacksCharB", function () { | ||
const charA: Character = { hitPoints: 100, damage: 10, defense: 20 }; | ||
const charB: Character = { hitPoints: 200, damage: 10, defense: 3 }; | ||
|
||
it("should execute charA attack on charB", function () { | ||
const charAClone = Object.assign({}, charA); | ||
const charBClone = Object.assign({}, charB); | ||
charAAttacksCharB(charAClone, charBClone); | ||
assertEquals(charAClone, charA); | ||
assertEquals(charBClone, { | ||
...charB, | ||
hitPoints: charB.hitPoints - (charA.damage - charB.defense), | ||
}); | ||
}); | ||
|
||
it("should execute charB attack on charA", function () { | ||
const charAClone = Object.assign({}, charA); | ||
const charBClone = Object.assign({}, charB); | ||
charAAttacksCharB(charBClone, charAClone); | ||
assertEquals(charBClone, charB); | ||
assertEquals(charAClone, { | ||
...charA, | ||
hitPoints: charA.hitPoints - 1, | ||
}); | ||
}); | ||
}); | ||
|
||
describe("simulateFight", function () { | ||
it("return true, if player wins", function () { | ||
// For example, suppose you have 8 hit points, 5 damage, and 5 armor, | ||
// and that the boss has 12 hit points, 7 damage, and 2 armor: | ||
assertEquals( | ||
simulateFight({ | ||
hitPoints: 8, | ||
damage: 5, | ||
defense: 5, | ||
}, { | ||
hitPoints: 12, | ||
damage: 7, | ||
defense: 2, | ||
}), | ||
true, | ||
); | ||
}); | ||
}); |
Oops, something went wrong.