Skip to content

Instantly share code, notes, and snippets.

@Quentin-M
Created November 16, 2021 23:18
Show Gist options
  • Save Quentin-M/36a8a4a038c1cb0a934ce9e4cab2f6e0 to your computer and use it in GitHub Desktop.
Save Quentin-M/36a8a4a038c1cb0a934ce9e4cab2f6e0 to your computer and use it in GitHub Desktop.
OgameX - Resource Trader Calculator.ipynb
Display the source blob
Display the rendered blob
Raw
{
"nbformat": 4,
"nbformat_minor": 0,
"metadata": {
"colab": {
"name": "OgameX - Resource Trader Calculator.ipynb",
"provenance": [],
"collapsed_sections": [],
"authorship_tag": "ABX9TyPKTy7WuhvD0yBWWD86Wtqo",
"include_colab_link": true
},
"kernelspec": {
"name": "python3",
"display_name": "Python 3"
},
"language_info": {
"name": "python"
},
"accelerator": "GPU"
},
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "view-in-github",
"colab_type": "text"
},
"source": [
"<a href=\"https://colab.research.google.com/gist/Quentin-M/36a8a4a038c1cb0a934ce9e4cab2f6e0/ogamex-resource-trader-calculator.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "2TW7DwR7-tMj"
},
"source": [
"# OGameX - Resource Trader Calculator\n",
"\n",
"After entering your available resources and the cost of the building / ship you'd like to build - this calculator will automatically optimize and output the resource trades you should make in order to enable you to make the building / unit as many times as possible. In other words, it will instruct you to sell some resources for others, and how much specifically, through the resource trader, to get the perfect resource ratio to build as many of your desired building/ship as possible.\n",
"\n",
"You may select how many trades are you willing to make (one or two - each costing 2500 DM). Note that the calculator will make use of all resources you specified, if you want to keep some of your resources around, simply omit it from the AVAILABLE field. For instance, if you have 3B Deuterium but only want to use 2B Deuterium to build, put 2B in the AVAILABLE_DEUT."
]
},
{
"cell_type": "code",
"metadata": {
"id": "RbW22AdkMeLN",
"cellView": "form"
},
"source": [
"#@markdown # Initilization - Run me once\n",
"\n",
"import os, time, inspect, importlib\n",
"\n",
"need_init = True\n",
"if importlib.util.find_spec(\"cvxpy\") is not None:\n",
" import cvxpy\n",
" need_init = len(inspect.getmembers(cvxpy, inspect.ismodule)) <= 0 or cvxpy.__version__ != \"1.1.17\"\n",
"\n",
"if need_init:\n",
" print(\"Installing dependencies (~2min required the first time)..\")\n",
"\n",
" !sudo apt install coinor-cbc coinor-libcbc-dev &>/dev/null\n",
" !pip install cylp &>/dev/null\n",
"\n",
" ![ -d cvxpy ] || git clone https://github.com/cvxpy/cvxpy.git &> /dev/null\n",
" !cd cvxpy; git checkout v1.1.17 &> /dev/null; pip install -e . &> /dev/null\n",
"\n",
" print(\"Restarting Google Colab...\")\n",
" print(\"You can ignore the error message [Your session crashed for an unknown reason.], and skip ahead to the next cell.\")\n",
" time.sleep(0.5)\n",
" os._exit(0)\n",
"\n",
"print(\"Initialization is done, you do not need to run this cell ever again.\")"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "OgCMQFK1PNig",
"colab": {
"base_uri": "https://localhost:8080/"
},
"cellView": "form",
"outputId": "796977ce-29d0-420b-c4ad-f273af7d60e1"
},
"source": [
"#@markdown # Configuration & Optimization\n",
"\n",
"#@markdown ### Available resources\n",
"\n",
"AVAILABLE_METAL = \"20,000,000,000\" #@param {type:\"string\"}\n",
"AVAILABLE_CRYSTAL = \"80,000,000,000\" #@param {type:\"string\"}\n",
"AVAILABLE_DEUT = \"30,000,000,000\" #@param {type:\"string\"}\n",
"\n",
"#@markdown ---\n",
"#@markdown ### Buildings/Ships Cost\n",
"\n",
"BUILD_METAL = \"10,000,000,000\" #@param {type:\"string\"}\n",
"BUILD_CRYSTAL = \"5,000,000,000\" #@param {type:\"string\"}\n",
"BUILD_DEUT = \"1,000,000,000\" #@param {type:\"string\"}\n",
"\n",
"#@markdown ---\n",
"\n",
"MAX_TRADES = 2 #@param {type:\"slider\", min:1, max:2, step:1}\n",
"DEBUG = False #@param {type:\"boolean\"}\n",
"\n",
"#############################################\n",
"#############################################\n",
"#############################################\n",
"\n",
"import locale\n",
"import cvxpy as cp\n",
"import numpy as np\n",
"from tabulate import tabulate\n",
"\n",
"locale.setlocale(locale.LC_ALL, \"en_US.UTF-8\")\n",
"np.set_printoptions(suppress=True, formatter={'float': lambda x: \"{:,.2f}\".format(x)})\n",
"\n",
"\n",
"def optimize(RESOURCES, TARGET_COST, MAX_TRADES, DEBUG):\n",
" # Define constants.\n",
" TABLE_IDX = [\"metal\", \"crystal\", \"deuterium\"]\n",
" RATES = np.array([\n",
" #M, C, D (Sell)\n",
" [3/3, 3/2, 3/1], # M,C,D (Buy)\n",
" [2/3, 1/1, 2/1], # M,C,D (Buy)\n",
" [1/3, 1/2, 1/1] # M,C,D (Buy)\n",
" ])\n",
"\n",
" # Define variables.\n",
" trades = cp.Variable((3,3), nonneg=True)\n",
" trades_count = cp.Variable(3, boolean=True)\n",
" target_count = cp.Variable(integer=True)\n",
"\n",
" # Define formulas.\n",
" exchange_got = cp.multiply(trades, RATES) @ RESOURCES\n",
" exchange_spent = cp.multiply(cp.cumsum(trades, axis=0)[2], RESOURCES)\n",
" new_resources = RESOURCES - exchange_spent + exchange_got\n",
"\n",
" # Define problem and solve.\n",
" objective = cp.Maximize(\n",
" 10 * target_count # As many times the target as possible\n",
" - 3 * cp.sum(trades_count) # Trade as few times as possible\n",
" - 1 * cp.sum(trades) # Trade as few resources as possible\n",
" )\n",
" constraints = [ # Aware this model resolves in float while ogame resources are integer, but who cares - resources are priting so fast.\n",
" TARGET_COST * target_count <= new_resources, # Build up to our resources\n",
" cp.cumsum(trades, axis=0)[2] <= [1,1,1], # When selling a resource, we may only sell up to 100% of the resource\n",
" cp.diag(trades) == [0,0,0], # Don't sell resource into itself\n",
" cp.sum(trades, axis=0) <= trades_count, cp.sum(trades_count) <= MAX_TRADES, # Don't trade more than what we've specified\n",
" ]\n",
" problem = cp.Problem(objective, constraints)\n",
" problem.solve(verbose=DEBUG, solver='CBC')\n",
"\n",
" # Print result\n",
" if(problem.status == \"infeasible\" or target_count.value < 1):\n",
" print(\"Not enough resources.\")\n",
" return\n",
"\n",
" print()\n",
" print(f\"You may make your ship/building {int(target_count.value):n} time{'s'[:int(target_count.value)^1]} by trading resources {int(cp.sum(trades_count).value):n} time{'s'[:int(cp.sum(trades_count).value)^1]}:\")\n",
" for si, sv in enumerate(trades.value.T):\n",
" if np.sum(sv) <= 0:\n",
" continue\n",
" \n",
" print(f\"* Sell {RESOURCES[si]*cp.sum(sv).value:,.0f} {TABLE_IDX[si]} for\", end=\"\")\n",
" for bi, bv in enumerate(sv):\n",
" if bv > 0:\n",
" print(f\" {RESOURCES[si]*bv*RATES[bi,si]:,.0f} {TABLE_IDX[bi]},\", end=\"\")\n",
" print()\n",
"\n",
" table_data = np.abs(np.array([\n",
" RESOURCES,\n",
" new_resources.value,\n",
" (TARGET_COST * target_count).value\n",
" ], dtype='float64'))\n",
" table_data = np.concatenate((np.array([[\"Before\", \"After\", \"Required\"]], dtype='<U8').T, table_data), axis=1)\n",
" print(tabulate(table_data, [\"\", \"Metal\", \"Crystal\", \"Deuterium\"], tablefmt=\"fancy_grid\", floatfmt=\",.0f\", colalign=(\"left\", \"right\", \"right\", \"right\")))\n",
"\n",
" if(DEBUG):\n",
" print()\n",
" print(\"Score: %.f\" % problem.value)\n",
" print(\"Trade Table:\")\n",
" print(trades.value)\n",
"\n",
"RESOURCES = np.array([locale.atoi(AVAILABLE_METAL),locale.atoi(AVAILABLE_CRYSTAL),locale.atoi(AVAILABLE_DEUT)])\n",
"TARGET_COST = np.array([locale.atoi(BUILD_METAL), locale.atoi(BUILD_CRYSTAL), locale.atoi(BUILD_DEUT)])\n",
"optimize(RESOURCES, TARGET_COST, MAX_TRADES, DEBUG)"
],
"execution_count": null,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"\n",
"You may make your ship/building 11 times by trading resources 2 times:\n",
"* Sell 25,000,000,000 crystal for 37,500,000,000 metal,\n",
"* Sell 17,500,000,000 deuterium for 52,500,000,000 metal,\n",
"╒══════════╤═════════════════╤════════════════╤════════════════╕\n",
"│ │ Metal │ Crystal │ Deuterium │\n",
"╞══════════╪═════════════════╪════════════════╪════════════════╡\n",
"│ Before │ 20,000,000,000 │ 80,000,000,000 │ 30,000,000,000 │\n",
"├──────────┼─────────────────┼────────────────┼────────────────┤\n",
"│ After │ 110,000,000,000 │ 55,000,000,000 │ 12,500,000,000 │\n",
"├──────────┼─────────────────┼────────────────┼────────────────┤\n",
"│ Required │ 110,000,000,000 │ 55,000,000,000 │ 11,000,000,000 │\n",
"╘══════════╧═════════════════╧════════════════╧════════════════╛\n"
]
}
]
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment