Skip to content

Instantly share code, notes, and snippets.

@residentsummer
Created March 29, 2020 20:53
Show Gist options
  • Save residentsummer/be91cf453662b2db41ab2118404ae22c to your computer and use it in GitHub Desktop.
Save residentsummer/be91cf453662b2db41ab2118404ae22c to your computer and use it in GitHub Desktop.
Destiny Reduntant Gear
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Destiny Redundant Gear"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"scrolled": false
},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "96402c20a9224c2ca501e78dbf7b2378",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"HBox(children=(FileUpload(value={}, description='Upload'), Label(value='Filename: '), Label(value='-')))"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "d03a8757315b4ce2be5cfa4f063e0e62",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"HBox(children=(IntSlider(value=2, description='Threshold:', max=10), IntSlider(value=1, description='Max misse…"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "b8a39773f28f45dab6c5bd05f0a72185",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"Output()"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"import io\n",
"import csv\n",
"import codecs\n",
"from collections import defaultdict\n",
"\n",
"import ipywidgets as widgets\n",
"\n",
"\n",
"STATS = ['Mobility', 'Resilience', 'Recovery', 'Discipline', 'Intellect', 'Strength']\n",
"BASE_STATS = [\"%s (Base)\" % x for x in STATS]\n",
"ALL_STATS_FMT = '|'.join(\"{%s:2}\" % x for x in BASE_STATS)\n",
"\n",
"\n",
"def format_armor(p):\n",
" return ('{Total (Base):2}|%s {Name} ({Power:3})' % ALL_STATS_FMT).format(**p)\n",
"\n",
"\n",
"_ARMOR = {}\n",
"def load_armor(filelike):\n",
" cats = defaultdict(list)\n",
"\n",
" for piece in csv.DictReader(filelike):\n",
" for k, v in piece.items():\n",
" try:\n",
" piece[k] = int(v)\n",
" except ValueError:\n",
" pass\n",
"\n",
" cats[piece['Type']].append(piece)\n",
"\n",
" _ARMOR.clear()\n",
" _ARMOR.update(cats)\n",
"\n",
"\n",
"def is_better(a, b, threshold, max_misses):\n",
" if a['Equippable'] != b['Equippable']:\n",
" return False\n",
"\n",
" miss = 0\n",
" for stat in BASE_STATS:\n",
" if a[stat] >= b[stat]:\n",
" continue\n",
"\n",
" if b[stat] - a[stat] > threshold:\n",
" return False\n",
"\n",
" miss += 1\n",
"\n",
" return miss <= max_misses\n",
"\n",
"\n",
"def find_dupes(cat, threshold, max_misses):\n",
" '''Finds duplicate items within a category'''\n",
"\n",
" scat = sorted(cat, key=lambda p: p['Total (Base)'], reverse=True)\n",
" l = len(scat)\n",
" for i in range(l):\n",
" dupes = []\n",
" for j in range(i+1, l):\n",
" if is_better(scat[i], scat[j], threshold, max_misses):\n",
" dupes.append(scat[j])\n",
" if dupes:\n",
" yield (scat[i], dupes)\n",
"\n",
"\n",
"def armor_filter(piece):\n",
" return (\n",
" piece['Tier'] == 'Legendary' and\n",
" # Need only armor 2.0\n",
" 'Energy Capacity' in piece['Masterwork Type'] and\n",
" # Filter out class items\n",
" piece['Total (Base)'] > 10\n",
" )\n",
"\n",
"\n",
"def print_dupes(armor, threshold, max_misses):\n",
" has_dupes = False\n",
"\n",
" for t, pieces in armor.items():\n",
" eligible = filter(armor_filter, pieces)\n",
" dupes = list(find_dupes(eligible, threshold, max_misses))\n",
" if not dupes:\n",
" continue\n",
"\n",
" has_dupes = True\n",
" print(t)\n",
" for more, lesses in dupes:\n",
" print(' ', format_armor(more))\n",
" for less in lesses:\n",
" print(' ', format_armor(less))\n",
" print()\n",
" \n",
" if not has_dupes:\n",
" print(\"=== No duplicates found, try to adjust sliders ===\")\n",
"\n",
"\n",
"### Display widgets and handle input ###\n",
"\n",
"uploader = widgets.FileUpload(multi=False)\n",
"threshold = widgets.IntSlider(value=2, min=0, max=10, step=1, description='Threshold:')\n",
"max_misses = widgets.IntSlider(value=1, min=0, max=6, step=1, description='Max misses:')\n",
"label = widgets.Label(value=\"-\")\n",
"output = widgets.Output()\n",
"display(widgets.HBox([uploader, widgets.Label(value=\"Filename: \"), label]),\n",
" widgets.HBox([threshold, max_misses]),\n",
" output)\n",
"\n",
"\n",
"def handle_upload(change, *args):\n",
" files = change['new']\n",
" if isinstance(files, dict):\n",
" files = list(files.values())\n",
"\n",
" if not files:\n",
" file_label.value = \"-\"\n",
" return\n",
" \n",
" f = files[-1]\n",
" label.value = f['metadata']['name']\n",
"\n",
" # Loads armor into a global var\n",
" load_armor(codecs.iterdecode(io.BytesIO(f['content']), 'utf8'))\n",
"\n",
" analyze()\n",
"\n",
"\n",
"uploader.observe(handle_upload, names='value')\n",
" \n",
"\n",
"def analyze(*args):\n",
" output.clear_output(wait=True)\n",
" \n",
" if not _ARMOR:\n",
" with output:\n",
" print(\"=== No data ===\")\n",
" return\n",
" \n",
" with output:\n",
" print_dupes(_ARMOR, threshold.value, max_misses.value)\n",
"\n",
"threshold.observe(analyze, names='value')\n",
"max_misses.observe(analyze, names='value')\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Instruction\n",
"\n",
"* Extract an armor spreadsheet from DIM (at the bottom of it's settings page)\n",
"* Upload it into this notebook via \"Upload\" button\n",
"* Use sliders to adjust tolerations (see below)\n",
"\n",
"#### One piece of armor is considered superior to the other if:\n",
"* sum of its base stats is greater or equal\n",
"* most* of base stats are greater or equal\n",
"\n",
"\\* **Max misses** - number of base stats that are allowed to be worse. **Threshold** - maximum deficiency of each stat that \"misses\".\n",
"\n",
"_Mods and full MW bonus (+2 to all stats) are not considered._"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"### Hide code blocks ###\n",
"\n",
"from IPython.display import display, Javascript\n",
"import IPython.core.display as di\n",
"\n",
"# This line will hide code by default when the notebook is loaded\n",
"display(Javascript('''jQuery(\".input\").hide();'''))\n",
"\n",
"# This line will add a button to toggle visibility of code blocks, for use with the HTML export version\n",
"di.display_html('''<button onclick=\"jQuery('.input').toggle();\">Toggle code</button>''', raw=True)"
]
}
],
"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.7.5"
}
},
"nbformat": 4,
"nbformat_minor": 4
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment