Search code examples
pythonmatplotlibcolormaphexagonal-tiles

Hexgrid in matplotlib showing unexpected colors


I have been trying to create a Game of Life clone on a hexagonal grid in Python. I have managed to use hexbin to display the results and update them regularly to create an animation.

Each cell can only have values 0 or 1 and I am using the Greys colormap to display them as black and white tiles. However, in the output the center tiles appear grey instead of black, even though the value is set to 1 (I used print statements to check the value of those tiles).

enter image description here

How can I remove that? The problem becomes more apparent with a different color map, such as Spectral: in this case even some horizontal lines appear). I have used nearly the same code before for the same project with rectangular grid, where this problem didn't show up.

# -*- coding: utf-8 -*-

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import matplotlib.mlab as mlab


n = 20       # number of rows
alive = 1       # value of a cell that is alive
dead = 0        # value of a dead cell
pause = False

x = y = np.arange(0, n, 1.0)
X, Y = np.meshgrid(x, y)
Z = np.zeros((n, n))
Z[10][10] = Z[9][11] = Z[10][11] = Z[9][9] = 1

x = X.ravel()
y = Y.ravel()
z = Z.ravel()

def update(*args):
    if not pause:
        advance()

# this is the main function that advances the animation by one frame (time step)
def advance(*args):
    global Z
    newZ = Z.copy()
    for i in range(n):
        for j in range(n):
            total = (Z[i][(j-1)%n] + Z[i][(j+1)%n] + Z[(i-1)%n][j] + Z[(i+1)%n][j] + Z[(i-1)%n][(j-1)%n] + Z[(i+1)%n][(j-1)%n])

            if Z[i][j] == alive:
                if (total < 2) or (total > 4):
                    newZ[i][j] = dead
            else:
                if total == 2:
                    newZ[i][j] = alive
    Z = newZ
    z = Z.ravel()
    grid = plt.hexbin(x, y, z, gridsize=n, cmap="Spectral")
    return [grid]

fig = plt.figure(figsize=(5, 5))
ax = plt.subplot()
ani = animation.FuncAnimation(fig, update, interval=50)
plt.show()

Solution

  • You are using a square grid to perform the game of life algorithm. You then map the result onto a hexagonal grid. This means that some square grid values lie in the same cell of the hexagonal grid (adding up to its value), while some hexagonal grid cells are empty because they do not hit any sqaure grid cell.

    enter image description here

    As an example, you can see that e.g. the hexagonal grid's cell centered at (0.5,2.5) does not include any position of the original square grid. Hence this cell stays empty.

    Since the game of life is strongly depending on the grid itself, it anyway makes no sense to use a different grid. So just stay with the square grid - or let the game of life run on a different grid, changing the complete algorithm to account for the grid in use.