Day 21
[advent-of-code-15.git] / advent21.ipynb
diff --git a/advent21.ipynb b/advent21.ipynb
new file mode 100644 (file)
index 0000000..6fbd087
--- /dev/null
@@ -0,0 +1,389 @@
+{
+ "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
+}