+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": 133,
+ "metadata": {
+ "collapsed": true
+ },
+ "outputs": [],
+ "source": [
+ "import collections\n",
+ "import itertools"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 134,
+ "metadata": {
+ "collapsed": true
+ },
+ "outputs": [],
+ "source": [
+ "def player_wins_fight(player, boss, verbose=False):\n",
+ " boss_hp = boss['hp']\n",
+ " pc_hp = player['hp']\n",
+ " while pc_hp > 0 and boss_hp > 0:\n",
+ " boss_hp -= max(player['damage'] - boss['armour'], 1)\n",
+ " pc_hp -= max(boss['damage'] - player['armour'], 1)\n",
+ " if verbose:\n",
+ " print('PC:', player['hp'], ' . Boss:', boss_hp)\n",
+ " if boss_hp <= 0:\n",
+ " return True\n",
+ " else:\n",
+ " return False"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 135,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "True"
+ ]
+ },
+ "execution_count": 135,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "pc = {'hp': 8, 'damage': 5, 'armour': 5}\n",
+ "boss = {'hp': 12, 'damage': 7, 'armour': 2}\n",
+ "player_wins_fight(pc, boss)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 136,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "({'armour': 5, 'damage': 5, 'hp': 8}, {'armour': 2, 'damage': 7, 'hp': 12})"
+ ]
+ },
+ "execution_count": 136,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "pc, boss"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 137,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "{'armour': 2, 'damage': 8, 'hp': 100}"
+ ]
+ },
+ "execution_count": 137,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "boss = {}\n",
+ "for l in open('advent21.txt').readlines():\n",
+ " t, v = l.strip().split(': ')\n",
+ " if t == 'Hit Points': \n",
+ " boss['hp'] = int(v)\n",
+ " elif t == 'Armor': \n",
+ " boss['armour'] = int(v)\n",
+ " else:\n",
+ " boss[t.lower()] = int(v)\n",
+ "boss"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 138,
+ "metadata": {
+ "collapsed": true
+ },
+ "outputs": [],
+ "source": [
+ "weapons_s = \"\"\"Dagger 8 4 0\n",
+ "Shortsword 10 5 0\n",
+ "Warhammer 25 6 0\n",
+ "Longsword 40 7 0\n",
+ "Greataxe 74 8 0\"\"\"\n",
+ "\n",
+ "armour_s = \"\"\"Leather 13 0 1\n",
+ "Chainmail 31 0 2\n",
+ "Splintmail 53 0 3\n",
+ "Bandedmail 75 0 4\n",
+ "Platemail 102 0 5\"\"\"\n",
+ "\n",
+ "rings_s = \"\"\"Damage+1 25 1 0\n",
+ "Damage+2 50 2 0\n",
+ "Damage+3 100 3 0\n",
+ "Defense+1 20 0 1\n",
+ "Defense+2 40 0 2\n",
+ "Defense+3 80 0 3\"\"\""
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 140,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "def parse_table(table):\n",
+ " d = []\n",
+ " for l in table.split('\\n'):\n",
+ " ls = l.split()\n",
+ " d += [{'name': ls[0].strip(),\n",
+ " 'cost': int(ls[1].strip()), \n",
+ " 'damage': int(ls[2].strip()), \n",
+ " 'armour': int(ls[3].strip()) }]\n",
+ " return d"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 141,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "([{'armour': 0, 'cost': 8, 'damage': 4, 'name': 'Dagger'},\n",
+ " {'armour': 0, 'cost': 10, 'damage': 5, 'name': 'Shortsword'},\n",
+ " {'armour': 0, 'cost': 25, 'damage': 6, 'name': 'Warhammer'},\n",
+ " {'armour': 0, 'cost': 40, 'damage': 7, 'name': 'Longsword'},\n",
+ " {'armour': 0, 'cost': 74, 'damage': 8, 'name': 'Greataxe'}],\n",
+ " [{'armour': 1, 'cost': 13, 'damage': 0, 'name': 'Leather'},\n",
+ " {'armour': 2, 'cost': 31, 'damage': 0, 'name': 'Chainmail'},\n",
+ " {'armour': 3, 'cost': 53, 'damage': 0, 'name': 'Splintmail'},\n",
+ " {'armour': 4, 'cost': 75, 'damage': 0, 'name': 'Bandedmail'},\n",
+ " {'armour': 5, 'cost': 102, 'damage': 0, 'name': 'Platemail'},\n",
+ " {}],\n",
+ " [{'armour': 0, 'cost': 25, 'damage': 1, 'name': 'Damage+1'},\n",
+ " {'armour': 0, 'cost': 50, 'damage': 2, 'name': 'Damage+2'},\n",
+ " {'armour': 0, 'cost': 100, 'damage': 3, 'name': 'Damage+3'},\n",
+ " {'armour': 1, 'cost': 20, 'damage': 0, 'name': 'Defense+1'},\n",
+ " {'armour': 2, 'cost': 40, 'damage': 0, 'name': 'Defense+2'},\n",
+ " {'armour': 3, 'cost': 80, 'damage': 0, 'name': 'Defense+3'},\n",
+ " {},\n",
+ " {}])"
+ ]
+ },
+ "execution_count": 141,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "weapons = parse_table(weapons_s)\n",
+ "armour = parse_table(armour_s)\n",
+ "armour += [{}]\n",
+ "rings = parse_table(rings_s)\n",
+ "rings += [{}, {}]\n",
+ "weapons, armour, rings"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 142,
+ "metadata": {
+ "collapsed": true
+ },
+ "outputs": [],
+ "source": [
+ "def player(equipment):\n",
+ " player = {'hp': 100, 'damage': 0, 'armour': 0, 'cost': 0, 'name': ''}\n",
+ " for e in equipment:\n",
+ " for s in e:\n",
+ " player[s] += e[s]\n",
+ " return player"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 143,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "{'armour': 4, 'cost': 217, 'damage': 10, 'hp': 100, 'name': ''}"
+ ]
+ },
+ "execution_count": 143,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "player([{'armour': 0, 'cost': 74, 'damage': 8}, {'armour': 1, 'cost': 13, 'damage': 0},\n",
+ " {'armour': 0, 'cost': 50, 'damage': 2}, {'armour': 3, 'cost': 80, 'damage': 0}])"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 144,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "840"
+ ]
+ },
+ "execution_count": 144,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "equipment = itertools.chain(\n",
+ " itertools.product(\n",
+ " itertools.combinations(weapons, 1),\n",
+ " itertools.combinations(armour, 1),\n",
+ " itertools.combinations(rings, 2)))\n",
+ "\n",
+ "players = [player(j for i in e for j in i) for e in equipment]\n",
+ "len(players)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 146,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "91"
+ ]
+ },
+ "execution_count": 146,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "winner = [p for p in players if not player_wins_fight(p, boss)]\n",
+ "# sorted(winners, key=lambda w: w['cost'])\n",
+ "min(winners, key=lambda w: w['cost'])['cost']"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#Part 2"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 149,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "840"
+ ]
+ },
+ "execution_count": 149,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "equipment = itertools.chain(\n",
+ " itertools.product(\n",
+ " itertools.combinations(weapons, 1),\n",
+ " itertools.combinations(armour, 1),\n",
+ " itertools.combinations(rings, 2)))\n",
+ "\n",
+ "players = [player(j for i in e for j in i) for e in equipment]\n",
+ "len(players)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 151,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "158"
+ ]
+ },
+ "execution_count": 151,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "losers = [p for p in players if not player_wins_fight(p, boss)]\n",
+ "# sorted(losers, key=lambda w: w['cost'], reverse=True)\n",
+ "max(losers, key=lambda w: w['cost'])['cost']"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": true
+ },
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.4.3"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}