{"cells":[{"metadata":{},"cell_type":"markdown","source":"<center><h1>Conway's Game of Life in Python</h1></center>","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"In 1970 the British Mathematician John Conway created his \"Game of Life\" -- a set of rules that mimics the chaotic yet patterned growth of a colony of biological organisms. The \"game\" takes place on a two-dimensional grid consisting of \"living\" and \"dead\" cells, and the **rules to step from generation to generation are simple:**\n\n* **Overpopulation**: if a living cell is surrounded by more than three living cells, it dies.\n* **Stasis**: if a living cell is surrounded by two or three living cells, it survives.\n* **Underpopulation**: if a living cell is surrounded by fewer than two living cells, it dies.\n* **Reproduction**: if a dead cell is surrounded by exactly three cells, it becomes a live cell","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"By enforcing these rules in sequential steps, beautiful and unexpected patterns can appear.\n\nHere I'll use Python and NumPy to compute generational steps for the game of life.\n\nBecause the Game of Life is so simple, the time step can be computed rather tersely in Python.  Two implementation possibilities: one using **generator expressions**, and one using the **convolve2d** function from scipy.","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"**<span style=\"color:Red\">If you find this kernel useful, Please Upvote it , it motivates me to write more Quality content**","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"## Helpful functions\n\n* credits http://jakevdp.github.io/pages/about.html","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"!pip install -q JSAnimation","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"%matplotlib inline","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# JSAnimation import available at https://github.com/jakevdp/JSAnimation\nfrom JSAnimation.IPython_display import display_animation, anim_to_html\nimport numpy as np\nimport matplotlib.pyplot as plt\nfrom matplotlib import animation, rc\nfrom IPython.display import HTML\nimport matplotlib\nimport pandas as pd\nimport seaborn as sns\nimport matplotlib.image as image\nfrom IPython.display import HTML","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"* Helper matplotlib animation function","execution_count":null},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"def animate_fig(X,cmap='binary',frames=10, interval=300): \n    \n    X_blank = np.zeros_like(X)\n    # First set up the figure, the axis, and the plot element we want to animate\n    fig = plt.figure(figsize=(38,38), dpi=10)\n    ax = fig.add_axes([0, 0, 1, 1], xticks=[], yticks=[], frameon=False)\n    im = ax.imshow(X,cmap=cmap)\n    im.set_clim(-0.05, 1) \n\n    def init():\n        im.set_data(X_blank)\n        return (im,)\n\n    # animation function.  This is called sequentially\n    def animate(i):\n        im.set_data(animate.X)\n        animate.X = life_step(animate.X)\n        return (im,)\n\n    animate.X = X\n\n    ### call the animator. blit=True means only re-draw the parts that have changed.\n\n    anim = animation.FuncAnimation(fig, animate, init_func=init,\n                               frames=frames, interval=interval, blit=True)\n\n    # equivalent to rcParams['animation.html'] = 'html5'\n    rc('animation', html='jshtml')\n    rc\n    return HTML(anim.to_jshtml())","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"shape = (25,25)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Helper functions for life step according to above 4 game rules","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"def life_step_1(X):\n    \"\"\"Game of life step using generator expressions\"\"\"\n    nbrs_count = sum(np.roll(np.roll(X, i, 0), j, 1)\n                     for i in (-1, 0, 1) for j in (-1, 0, 1)\n                     if (i != 0 or j != 0))\n    return (nbrs_count == 3) | (X & (nbrs_count == 2))\n\ndef life_step_2(X):\n    \"\"\"Game of life step using scipy tools\"\"\"\n    from scipy.signal import convolve2d\n    nbrs_count = convolve2d(X, np.ones((3, 3)), mode='same', boundary='wrap') - X\n    return (nbrs_count == 3) | (X & (nbrs_count == 2))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"life_step = life_step_1","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df = pd.read_csv('../input/conways-reverse-game-of-life-2020/train.csv')\ntest = pd.read_csv('../input/conways-reverse-game-of-life-2020/test.csv')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def plot_fig(n): # n = row of train data\n    X = df.iloc[n][2:625+2].values\n    Y = df.iloc[n][625+2:].values\n    shape = (25,25)\n    plt.figure(1,figsize=(16,6))\n    plt.subplot(121)\n    plt.imshow(X.reshape(shape),cmap='binary');plt.title('start stage')\n    plt.subplot(122)\n    plt.imshow(Y.reshape(shape),cmap='binary');plt.title('stop stage')\n    plt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Helper functions to Animate","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"def plot_animate_fig(idx,cmap='binary'):  # Enter row number\n    \n    X = df.iloc[idx][2:625+2].values\n    Y = df.iloc[idx][625+2:].values\n    shape = (25,25)\n    plt.figure(1,figsize=(14,6))\n    plt.subplot(121)\n    plt.imshow(X.reshape(shape),cmap=cmap);plt.title('start stage')\n    plt.subplot(122)\n    plt.imshow(Y.reshape(shape),cmap=cmap);plt.title('stop stage')\n    plt.show()\n    \n    print('Approximated Animation till stop stage : ')\n    \n    X = df.iloc[idx][2:625+2].values\n    X = X.reshape(shape)\n    fr = df.iloc[idx][1]\n    X_blank = np.zeros_like(X)\n    # First set up the figure, the axis, and the plot element we want to animate\n    fig = plt.figure(figsize=(38,38), dpi=10)\n    ax = fig.add_axes([0, 0, 1, 1], xticks=[], yticks=[], frameon=False)\n    im = ax.imshow(X,cmap=cmap)\n    im.set_clim(-0.05, 1) \n\n    def init():\n        im.set_data(X_blank)\n        return (im,)\n\n    # animation function.  This is called sequentially\n    def animate(i):\n        im.set_data(animate.X)\n        animate.X = life_step(animate.X)\n        return (im,)\n\n    animate.X = X\n\n    ### call the animator. blit=True means only re-draw the parts that have changed.\n\n    anim = animation.FuncAnimation(fig, animate, init_func=init,\n                               frames=fr, interval=300, blit=True)\n\n    # equivalent to rcParams['animation.html'] = 'html5'\n    rc('animation', html='jshtml')\n    rc\n    return HTML(anim.to_jshtml())","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Lets grab a single row and see start and stop, and animate it.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"plot_animate_fig(53)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Work in progress stay tuned","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"# Some simple oscillators (The \"Blinker\" and the \"Toad\")\n\nAn oscillator is a pattern that returns to its initial configuration after some number of steps. The static patterns shown above could be thought of as oscillators with a period of one. Here are two commonly-seen period-two oscillators:","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"blinker  =  [ 1 ,  1 ,  1 ] \ntoad  =  [[ 1 ,  1 ,  1 ,  0 ], \n        [ 0 ,  1 ,  1 ,  1 ]]\n\nX = np.zeros((6, 11))\nX[2, 1:4] = blinker\nX[2:4, 6:10] = toad\nanimate_fig(X.astype(int),frames=4)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Another Oscillator: The \"Pulsar\"\n\nMore complicated oscillators exist. Here's a period-three oscillator known as \"The Pulsar\", \n\n\nwhich displays some appealing symmetry.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"X = np.zeros((17, 17))\nX[2, 4:7] = 1\nX[4:7, 7] = 1\nX += X.T\nX += X[:, ::-1]\nX += X[::-1, :]\nanimate_fig(X.astype(int),frames=6)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# The \"Glider\"\n\n* There are other classes of object which oscillate, but also move while oscillating. One of the earliest seen is the \"Glider\", \n* Which after 4 steps returns to its initial configuration, but shifted by one cell in both the x and y direction. \n* This is a configuration that often emerges from random starting points.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"glider = [[1, 0, 0],\n          [0, 1, 1],\n          [1, 1, 0]]\nX = np.zeros((8, 8))\nX[:3, :3] = glider\nanimate_fig(X.astype(int),frames=32)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Unbounded Growth\n\n\nAn early question posed about the Game of Life was whether any configurations exist which result in asymptotically unbounded growth. It was quickly found that the answer was yes. Though it wasn't the first discovered, the following is one of the most compact configurations which display unbounded growth. Note that this claim is precisely true only on an infinite game board: using a torroidal (i.e. wrapping) geometry like we do here will lead to different results, but the first several hundred generations are unaffected:","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"unbounded = [[1, 1, 1, 0, 1],\n             [1, 0, 0, 0, 0],\n             [0, 0, 0, 1, 1],\n             [0, 1, 1, 0, 1],\n             [1, 0, 1, 0, 1]]\nX = np.zeros((30, 40))\nX[15:20, 18:23] = unbounded\nanimate_fig(X.astype(int),frames=100)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# The \"Gosper Glider Gun\" --> my favourite\n\n\nThe earliest known instance of unbounded growth is one of my favorite configurations: the \"Glider Gun\" discovered by Bill Gosper. It is an oscillating pattern that creates an infinite series of gliders. It still amazes me that something like this can even emerge from Conway's simple rules, but here it is. We'll stop after a couple hundred frames, but given an infinite game board this action would go on forever:","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"glider_gun =\\\n[[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0],\n [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0],\n [0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1],\n [0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1],\n [1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0],\n [1,1,0,0,0,0,0,0,0,0,1,0,0,0,1,0,1,1,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0],\n [0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0],\n [0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],\n [0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]]\n\nX = np.zeros((50, 70))\nX[1:10,1:37] = glider_gun\n\nanimate_fig(X.astype(int), frames=180,interval=50)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Here are another few patterns you might try embedding in a game board, to see what will happen","execution_count":null},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"diehard = [[0, 0, 0, 0, 0, 0, 1, 0],\n           [1, 1, 0, 0, 0, 0, 0, 0],\n           [0, 1, 0, 0, 0, 1, 1, 1]]\n\nboat = [[1, 1, 0],\n        [1, 0, 1],\n        [0, 1, 0]]\n\nr_pentomino = [[0, 1, 1],\n               [1, 1, 0],\n               [0, 1, 0]]\n\nbeacon = [[0, 0, 1, 1],\n          [0, 0, 1, 1],\n          [1, 1, 0, 0],\n          [1, 1, 0, 0]]\n\nacorn = [[0, 1, 0, 0, 0, 0, 0],\n         [0, 0, 0, 1, 0, 0, 0],\n         [1, 1, 0, 0, 1, 1, 1]]\n\nspaceship = [[0, 0, 1, 1, 0],\n             [1, 1, 0, 1, 1],\n             [1, 1, 1, 1, 0],\n             [0, 1, 1, 0, 0]]\n\nblock_switch_engine = [[0, 0, 0, 0, 0, 0, 1, 0],\n                       [0, 0, 0, 0, 1, 0, 1, 1],\n                       [0, 0, 0, 0, 1, 0, 1, 0],\n                       [0, 0, 0, 0, 1, 0, 0, 0],\n                       [0, 0, 1, 0, 0, 0, 0, 0],\n                       [1, 0, 1, 0, 0, 0, 0, 0]]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"animate_fig(block_switch_engine,frames=100,interval=50)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Work in progress, stay tuned, I will updated as I learn on the way. \n## Please Upvote it , it motivates me to write more Quality content\n\n**<span style=\"color:Red\">If you find this kernel useful, Please Upvote it , it motivates me to write more Quality content**","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat":4,"nbformat_minor":4}