{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Sprator\n", "\n", "In this example, we'll try cloning [Sprator](https://github.com/yurkth/sprator) using Seagull. The idea for Sprator is fun and simple:\n", "\n", "1. Generate a 4x8 random noise\n", "2. Change the state according to the Conway Rule\n", "3. Repeat steps twice\n", "4. Flip the 4x8 image to create an 8x8 one" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "# Some settings to show a JS animation\n", "import matplotlib.pyplot as plt\n", "plt.rcParams[\"animation.html\"] = \"jshtml\"\n", "%matplotlib inline" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "import seagull as sg\n", "import seagull.lifeforms as lf" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Creating the lifeform\n", "\n", "First, we'll setup the board, of the size 4x8. Then, we'll create a [Custom lifeform](https://pyseagull.readthedocs.io/en/master/notebooks/basic-usage.html#Custom-lifeform) using some random noise. Lastly, we'll add the lifeform onto the board." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "board = sg.Board(size=(8,4))" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "np.random.seed(42)\n", "noise = np.random.choice([0,1], size=(8,4))\n", "custom_lf = lf.Custom(noise)" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "board.add(custom_lf, loc=(0,0))" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(
,\n", " )" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAMIAAAF2CAYAAAA8zUfmAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4xLjAsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+17YcXAAAEYElEQVR4nO3YQWoDMRQFwVbw0rr/QeW9coIxGEJk46rt3zwGGsGMvXfw7X5OD4B3IARICFAJASohQCUEqOr27Ph4PD7u3+qc8/SEl621Tk/4Cvf7fVzdvAiQEKASAlRCgEoIUAkBKiFAJQSohACVEKASAlRCgEoIUAkBKiFAJQSohACVEKASAlRCgEoIUAkBKiFAJQSohACVEKASAlRCgEoIUAkBKiFAJQSohACVEKASAlRCgEoIUAkBKiFAJQSohACVEKASAlRCgEoIUAkBKiFAJQSohACVEKASAlRCgEoIUAkBKiFAJQSohACVEKASAlRCgEoIUAkBKiFAJQSohACVEKASAlRCgEoIUAkBKiFAJQSohACVEKASAlRCgEoIUAkBKiFAJQSohACVEKASAlRCgEoIUAkBKiFAJQSohACVEKASAlRCgEoIUAkBKiFAJQSohACVEKASAlRCgEoIUAkBKiFAJQSohACVEKASAlRCgEoIUAkBKiFAJQSohACVEKASAlRCgEoIUAkBKiFAJQSohACVEKASAlRCgEoIUAkBKiFAJQSohACVEKASAlRCgEoIUAkBqrqdHvDX1lqnJ7xsznl6wss+8Ts/40WAhACVEKASAlRCgEoIUAkBKiFAJQSohACVEKASAlRCgEoIUAkBKiFAJQSohACVEKASAlRCgEoIUAkBKiFAJQSohACVEKASAlRCgEoIUAkBKiFAJQSohACVEKASAlRCgEoIUAkBKiFAJQSohACVEKASAlR1e3acc/7XDjjKiwAJASohQCUEqIQAlRCgEgJUQoBKCFAJASohQCUEqIQAlRCgEgJUQoBKCFAJASohQCUEqIQAlRCgEgJUQoBKCFAJASohQCUEqIQAlRCgEgJUQoBKCFAJASohQCUEqIQAlRCgEgJUQoBKCFAJASohQCUEqGrsva+PY1wf39Ra6/SEl805T0/4CnvvcXXzIkBCgEoIUAkBKiFAJQSohACVEKASAlRCgEoIUAkBKiFAJQSohACVEKASAlRCgEoIUAkBKiFAJQSohACVEKASAlRCgEoIUAkBKiFAJQSohACVEKASAlRCgEoIUAkBKiFAJQSohACVEKASAlRCgEoIUAkBqhp77+vjGNdH+DB773F18yJAQoBKCFAJASohQCUEqIQAlRCgEgJUQoBKCFAJASohQCUEqIQAlRCgEgJUQoBKCFAJASohQCUEqIQAlRCgEgJUQoBKCFAJASohQCUEqIQAlRCgEgJUQoBKCFAJASohQCUEqIQAlRCgEgJUQoBKCFAJAaq6nR5ArbVOT/h6XgRICFAJASohQCUEqIQAlRCgEgJUQoBKCFAJASohQCUEqIQAlRCgEgJUQoBKCFAJASohQCUEqIQAlRCgEgJUQoBKCFAJASohQCUEqIQAlRCgEgJUQoBKCFAJASohQCUEqIQAlRCgEgJUQoBKCFAJASohQFVj7316AxznRYCEAJUQoBICVEKASghQ1S//TSm+fjc9mwAAAABJRU5ErkJggg==\n", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "board.view()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Creating a custom rule\n", "\n", "The rule of Sprator is a bit different from Conway's Game of Life, so we'll create a custom function instead. What's cool about the Sprator rule is that all dead cells stay dead and all other live cells die in the next generation. Interesting concept!" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "import scipy.signal\n", "\n", "\n", "def count_neighbors(X) -> np.ndarray:\n", " \"\"\"Count neighbors for each element in an array\"\"\"\n", " return scipy.signal.convolve2d(X, np.ones((3, 3)), mode=\"same\", boundary=\"fill\") - X\n", "\n", "\n", "def custom_rule(X) -> np.ndarray:\n", " \"\"\"Custom sprator rule\"\"\"\n", " # Count the neighbors for each cell\n", " n = count_neighbors(X)\n", " dead_with_less_one_neighbor = (X == 0) & (n <= 1)\n", " alive_with_two_three_neighbors = (X == 1) & ((n == 2) | (n == 3))\n", " return dead_with_less_one_neighbor | alive_with_two_three_neighbors" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Running the simulation\n", "\n", "Now that we have a board, what's left is to just run the simulation!" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "2020-03-22 20:54:54.626 | INFO | seagull.simulator:compute_statistics:128 - Computing simulation statistics...\n" ] } ], "source": [ "sim = sg.Simulator(board)\n", "stats = sim.run(custom_rule, iters=1) # 1 iteration seems to give better results" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Create the Sprite!\n", "\n", "In order to create the Sprite, we should get the final step of the simulator (from the history), flip the array, and concatenate them into an 8x8 sprite!\n" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "final = sim.get_history()[-1]" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "sprator = np.hstack([final, np.fliplr(final)])" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "sprator = np.pad(sprator, mode=\"constant\", pad_width=1, constant_values=1)" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import matplotlib.pyplot as plt\n", "fig = plt.figure(figsize=(5,5))\n", "ax = fig.add_axes([0, 0, 1 , 1], xticks=[], yticks=[], frameon=False)\n", "im = ax.imshow(sprator, cmap=plt.cm.binary, interpolation=\"nearest\")\n", "im" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Let's add some colors!\n", "\n", "Let's put some colors in our sprites by assigning a `fill_color` and an `line_color`" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [], "source": [ "def hex_to_rgb(h):\n", " \"\"\"Convert a hex code to an RGB tuple\"\"\"\n", " h_ = h.lstrip(\"#\")\n", " return tuple(int(h_[i:i+2], 16) for i in (0, 2, 4))" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [], "source": [ "def apply_color(sprite, fill_color, line_color):\n", " \"\"\"Apply color to the sprite\"\"\"\n", " sprite_color = [\n", " (sprite * line) + (np.invert(sprite) * fill)\n", " for line, fill in zip(hex_to_rgb(line_color), hex_to_rgb(fill_color))\n", " ]\n", " return np.rollaxis(np.asarray(sprite_color), 0, 3)" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [], "source": [ "sprite_color = apply_color(sprator, \"#0d4d4d\", \"#ffaaaa\")" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fig = plt.figure(figsize=(5,5))\n", "ax = fig.add_axes([0, 0, 1 , 1], xticks=[], yticks=[], frameon=False)\n", "im = ax.imshow(sprite_color)\n", "im" ] } ], "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.6.7" } }, "nbformat": 4, "nbformat_minor": 2 }