{ "cells": [ { "cell_type": "markdown", "metadata": { "toc": "true" }, "source": [ "# Table of Contents\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# NetHack's functions Rne, Rn2 and Rnz in Python 3\n", "\n", "I liked [this blog post](https://eev.ee/blog/2018/01/02/random-with-care/#beware-gauss) by [Eevee](https://eev.ee/blog/).\n", "He wrote about interesting things regarding random distributions, and linked to [this page](https://nethackwiki.com/wiki/Rnz) which describes a weird distribution implemented as `Rnz` in the [NetHack](https://www.nethack.org/) game.\n", "\n", "> Note: I never heard of any of those before today.\n", "\n", "I wanted to implement and experiment with the `Rnz` distribution myself.\n", "Its code ([see here](https://nethackwiki.com/wiki/Source:NetHack_3.6.0/src/rnd.c#rnz)) uses two other distributions, `Rne` and `Rn2`." ] }, { "cell_type": "code", "execution_count": 41, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The watermark extension is already loaded. To reload it, use:\n", " %reload_ext watermark\n", "CPython 3.6.3\n", "IPython 6.2.1\n", "\n", "numpy 1.13.3\n", "matplotlib 2.1.1\n", "\n", "compiler : GCC 7.2.0\n", "system : Linux\n", "release : 4.13.0-21-generic\n", "machine : x86_64\n", "processor : x86_64\n", "CPU cores : 4\n", "interpreter: 64bit\n" ] } ], "source": [ "%load_ext watermark\n", "%watermark -v -m -p numpy,matplotlib" ] }, { "cell_type": "code", "execution_count": 39, "metadata": {}, "outputs": [], "source": [ "import random\n", "import numpy as np\n", "import matplotlib.pyplot as plt" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## `Rn2` distribution\n", "\n", "[The `Rn2` distribution](https://nethackwiki.com/wiki/Rn2) is simply an integer uniform distribution, between $0$ and $x-1$." ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [], "source": [ "def rn2(x):\n", " return random.randint(0, x-1)" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([1, 1, 9, 9, 0, 1, 9, 3, 1, 2, 7, 8, 6, 8, 3, 8, 4, 8, 6, 9, 7, 2, 2,\n", " 0, 9, 0, 7, 5, 0, 9, 4, 1, 1, 9, 2, 7, 3, 7, 4, 7, 3, 2, 0, 2, 8, 2,\n", " 0, 2, 0, 0, 7, 9, 9, 2, 7, 4, 9, 4, 3, 7, 9, 3, 3, 1, 2, 6, 6, 5, 5,\n", " 4, 0, 8, 9, 8, 1, 9, 5, 5, 9, 0, 6, 9, 5, 3, 1, 8, 4, 5, 8, 6, 9, 7,\n", " 9, 4, 1, 2, 0, 0, 9, 0])" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.asarray([rn2(10) for _ in range(100)])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Testing for `rn2(x) == 0` gives a $1/x$ probability :" ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [], "source": [ "from collections import Counter" ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Counter({False: 91, True: 9})" ] }, "execution_count": 35, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Counter([rn2(10) == 0 for _ in range(100)])" ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Counter({False: 894, True: 106})" ] }, "execution_count": 36, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Counter([rn2(10) == 0 for _ in range(1000)])" ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Counter({False: 9000, True: 1000})" ] }, "execution_count": 37, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Counter([rn2(10) == 0 for _ in range(10000)])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## `Rne` distribution\n", "\n", "[The `Rne` distribution]() is a truncated geometric distribution." ] }, { "cell_type": "code", "execution_count": 88, "metadata": {}, "outputs": [], "source": [ "def rne(x, truncation=5):\n", " truncation = max(truncation, 1)\n", " tmp = 1\n", " while tmp < truncation and rn2(x) == 0:\n", " tmp += 1\n", " return tmp" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> In the NetHack game, the player's experience is used as default value of the `truncation` parameter..." ] }, { "cell_type": "code", "execution_count": 89, "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "array([3, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1, 1, 1,\n", " 2, 1, 2, 1, 4, 2, 1, 1, 1, 4, 1, 2, 1, 2, 2, 1, 1, 1, 1, 1, 3, 1, 1,\n", " 2, 1, 1, 2])" ] }, "execution_count": 89, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.asarray([rne(3) for _ in range(50)])" ] }, { "cell_type": "code", "execution_count": 90, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(array([ 6610., 2268., 747., 252., 123.]),\n", " array([ 1. , 1.8, 2.6, 3.4, 4.2, 5. ]),\n", " )" ] }, "execution_count": 90, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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\n", "text/plain": [ "