# Wa-Tor World on a torus

The population dynamics simulation known as Wa-Tor was described in a previous post. When carried out on a grid with periodic boundary conditions, the Wa-Tor "universe" is topologically equivalent to a torus, as depicted below using the code provided here.

The code below also requires the torus.py import from an earlier blog post defining a class for depicting 3D objects in SVG.

from wator import World, EMPTY, FISH, SHARK
from torus import Torus
import random
import numpy as np

# Save every SAVE_EVERYth chronon iteration.
SAVE_EVERY = 5
# Run the simulation for MAX_CHRONONS chronons (time intervals).
MAX_CHRONONS = 2000

# PRNG seed.
SEED = 15
random.seed(SEED)

# The major and minor radius of the torus.
c, a = 2.5, 1.5
# Image dimensions and scaling factors from torus units to image units.
width, height = 800, 600
scalex, scaley = 100, 100
# Tait-Bryan angles for intrinsic rotation of the figure.
alpha, beta, gamma = 90, 35, 235
# Camera position, C, and projection plane position, E (relative to C).
cx, cy, cz = 0, 6, 0
ex, ey, ez = 0, 3, 0
C, E = np.array((cx,cy,cz)), np.array((ex,ey,ez))

def preamble(fo):
"""The SVG preamble and styles."""

print('<?xml version="1.0" encoding="utf-8"?>\n'

'<svg xmlns="http://www.w3.org/2000/svg"\n' + ' '*5 +
.format(width, height), file=fo)

print("""
<defs>
<style type="text/css"><![CDATA[""", file=fo)

print('path {stroke-width: 1px; stroke: #000;}', file=fo)

print("""]]></style>
</defs>""", file=fo)

world_width = nphi = 100
world_height = ntheta = int(world_width * c/a)

def get_colour(i, u):
"""Get a colour for quad i from the colourmap."""
cm = {EMPTY: '#00008b', FISH: '#ff69b4', SHARK: '#ffd700'}
return cm[u[i]]

def draw_torus(itr, u):
"""Draw a torus on iteration itr of the diffusion with array u."""
torus = Torus(c, a, ntheta, nphi)
torus.setup_torus()
torus.rotate_xyz(alpha, beta, gamma)

torus.get_perspective_view(C, E)

# Draw the torus as a SVG image and save.
with open('frames/wator-{:04d}.svg'.format(itr), 'w') as fo:
preamble(fo)
for i in torus.idy:
colour = get_colour(i, u.ravel())
print('<path d="M{},{} L{},{} L{},{} L{},{} Z" fill="{}"/>'.format(
print('</svg>', file=fo)

world = World(world_width, world_height)
world.populate_world()
for chronon in range(MAX_CHRONONS):
if not chronon % SAVE_EVERY:
print('{}/{}: {}'.format(chronon, MAX_CHRONONS, len(world.creatures)))
draw_torus(chronon, np.array(world.get_world_image_array()))
alpha += 1
beta += 1
world.evolve_world()


wator.py, as provided in the earlier blog post. has been customized with the following settings to make this simulation:

initial_energies = {FISH: 15, SHARK: 5}
fertility_thresholds = {FISH: 3, SHARK: 6}

Currently unrated