whydoitweet

I'm a Lecturer in Computer Science at Brunel University London. I teach games and research simulations. Views are my own :).

I have shown several ways to make your own simulation, but why are simulations useful in general? I figures explaining the things you can do with simulations is worth its own blog post, so today I will highlight six things you can do with simulations in general:

1. Create a realistic behaviour

These days, many players just “assume” various things in games are realistic, but almost always the developers resort to simulations to create these realistic behaviors. Examples include players kicking a ball, rain falling out of the sky, non-player characters reacting to your movement in the vicinity, or even people falling over after being shot. Few people notice it if the behavior is realistic, but fails tend to quickly spread across the web. See e.g. /r/gamephysics or the video below:

https://www.youtube.com/watch?v=3FHw-MsYVIo&t=48s

2. Create a Digital Twin

A second you can do is create a so-called “Digital Twin” of a physical thing. For instance, you could try to make a simulated replica of a car, a factory, or an human limb, and then use sensors on the real thing to either test the quality of the Digital Twin or (if the Twin is realistic) learn more about the real thing. A big advantage of Digital Twins is that you can freely examine those replicas in all places, without having to add more and more sensors.

3. Forecasting/prediction/early warning

Simulations can be used to forecast or predict things. This is not as easy as many people think, because to do a good job at forecasting one thing (e.g., a hurricane), you usually need to forecast several other things as well (e.g., temperature, rainfall, ocean currents). Forecasting is great, because it allows you to prepare for important events (such as disasters, accidents or economic events) in advance.

Nevertheless, forecasting is often the main reason people think about when they think about the use of simulation.

Example forecast of a Hurricane, based on data from weather stations and simulations. Source: https://www.nhc.noaa.gov/

4. Raising awareness about hidden/forgotten aspects

Simulations can also help expose things that are not easily visible. For instance, simulations can help us to reveal large black holes, which have a huge effect on the way that stars move but are extremely difficult to observe. Likewise, we can use simulations to understand things that are so small, we can hardly see them even with microscopes (e.g., quantum-mechanical simulations).

5. Learn how something works

Simulation-building is a great tool to learn how a system really works. For instance, you might build a star cluster model using Newton's Law of gravity, and it appears to work well. However, somehow you realize something might be missing, because your results mismatch reality every time that two stars come particularly close to each other. Perhaps you are not incorporating relativistic effects, or the stars are actually exchanging gaseous matter between them in the close encounter? Without building and testing your model, you might never have learned about that...

Likewise, if you build a model of a labour market, you can study what would happen to wages when immigrants arrive and start working as well. The ability to test all sorts of different cases, and make any change you like to your model really helps you to learn how something works. And doing the same thing in reality can be impossible (think of having to rearrange a few stars, or recruiting a few thousand people from a broad for a few years...).

6. Assist teaching

Lastly, simulations are by definition a simplified version of reality that nevertheless capture the aspects that you care most about. Because they are simpler, they are very useful for teaching. I don't have to go far for examples on this one, because my Small Sims tutorials (e.g., the one about the random walk) are exactly about that :). A simulation of Newton's Cradle (see below) is another example:

Courtesy of DemonDeLuxe (Dominique Toussaint), CC BY-SA 3.0.

Wrap-up

So there you go: six things you can do with simulation.

Honestly, this blog post only scratches the surface, and there are quite a few scientific papers that discuss the added value of simulations (and/or modelling) in much greater depth. The 2008 article “Why Model?” by Joshua M. Epstein (http://jasss.soc.surrey.ac.uk/11/4/12.html.bak) is one I particularly like in that aspect.

For subscribers I've got a small teaser about the next entry in the Small Sims series, and an idea for a new type of post on this blog.

Credits

Header image by Image by Yassay from Pixabay.

Read more...

This time I just wanted to share a short post with a few practical, and for me important, notes. I also realized that I should have a place where I mention the license with which I share everything. Consider this post as being that place.

Reminder of my original post

It seems like a while ago, but in my original post I offered to share my expertise, more or less as follows:

“Some of you might want to know my thoughts on a particular topic in my range of expertise. If so, please drop me a line, and I'll respond publicly through a Coil blog post, and make any prototype data or software associated with the response publicly available.”

Now that you have seen a number of posts from me, you might have a clearer idea as to how I go about these things. So yes: the offer still stands (just e-mail ideas to me at djgroennl@gmail.com).

Sharing and license

All content in this blog is free to reuse, under the rather permissive CC-BY 4.0 license. Please refer here for the exact conditions.

Contest update

So far I haven't received any entries for the contest in my last post yet. But rest assured, I'll leave it open for entries until the end of this month (September 2019), so there is plenty of time to spare.

Credits

Header image courtesy of maxpixel.net.

Read more...

Welcome to the first entry in the Game World series. In this series I will guide you step by step in making your own gaming world from the top down, using Python + a bunch of creativity.

Now I will cover a lot of creative world-building aspects as part of this series, but before I get into that I wanted to start with a very simple technical mission:

  • Show how to make a spinning globe, using a world map.

As in my last Small Sims post, I will use repl.it and Pillow for now to get the graphics working. I also base my solution off an image projection from ActiveState, which shows how to create a static globe projection with working rotation in two directions (the yz rotation in the example is actually broken).

In this post I will explain you a way how to make this, and you can follow it in two ways:

1. If you want to follow it as an actual tutorial, then create a new repl.it (or use a different Python environment), and add code to your main.py file whenever you see blocks like:

#This is an example comment.

2. Or, you can simply read/skim/browse the post, and download a working repl.it example at the end, which you can then test and modify as you see fit :).

Libraries needed

Quite simply, we need a basic maths library, and the Image function from Pillow:

import math

from PIL import Image

To actually use PIL, you also need to set up Pillow, which you can do as follows:

1. Click on the Package Icon on the left side of your screen when you have you repl.it open:

2. And then select the Pillow 7.0.0 package, and press + (once you've pressed it, it will change to a -

You may have to wait a little while before the package is set up, but you only have to do this once :).

Rendering a single world frame

To render a single world frame, we define a new function called render_world(). As arguments it will take:

- The rotation in the xy-plane (0.0-1.0). This will turn the world, like the drum in a washing machine.

- The rotation in the xz-plane (0.0-1.0). This will turn the world, like a ball rolling away or towards you.

- The rotation in the yz-plane (0.0-1.0). This one we will use to 'spin' the world in an animation, like the world normally turns.

- The width of the animation in pixels.

- The height of the animation in pixels.

Then, as a start we define the function and load a default image:

def render_world(xyr, xzr, yzr, imgxOutput, imgyOutput):

# Load image and make output buffer

imageInput = Image.open("Equirectangular_projection_SW.gif")

(imgxInput, imgyInput) = imageInput.size

pixelsInput = imageInput.load()

imageOutput = Image.new("RGB", (imgxOutput, imgyOutput))

pixelsOutput = imageOutput.load()

Note that we use an image from Wikipedia called Equirectangular_projection_SW.gif. This image looks as follows:

Source: WikipediaUserStrebe, CC BY-SA 3.0,

Next up, we define some essential values for each of the three rotations,

pi2 = math.pi * 2

# 3D Sphere Rotation Angles (arbitrary)

xy = -pi2 * xyr

xz = -pi2 * xzr

yz = -pi2 * yzr

sxy = math.sin(xy); cxy = math.cos(xy)

sxz = math.sin(xz); cxz = math.cos(xz)

a range of center points and the radius,

# define a sphere behind the screen

xc = (imgxOutput - 1.0) / 2

yc = (imgyOutput - 1.0) / 2

zc = min((imgxOutput - 1.0), (imgyOutput - 1.0)) / 2

r = min((imgxOutput - 1.0), (imgyOutput - 1.0)) / 2

the camera view point:

# define camera view point

xo = (imgxOutput - 1.0) / 2

yo = (imgyOutput - 1.0) / 2

zo = -min((imgxOutput - 1.0), (imgyOutput - 1.0))

and the distances between the camera viewpoint and the center point:

xoc = xo - xc

yoc = yo - yc

zoc = zo - zc

doc2 = xoc * xoc + yoc * yoc + zoc * zoc

The next part of render_world() is the trickiest part of the function. Here we use the Equirectangular projection to map the flat image above to a sphere. Note that we are just creating one image of the sphere at a particular rotation, as we'll do the animation later on.

To start off, we make two loops, over x and y to generate each pixel in our output image:

for yi in range(imgyOutput):

for xi in range(imgxOutput):

First in the loop, we calculate distances between the camera viewpoint, and the area that corresponds to that specific pixel:

xio = xi - xo

yio = yi - yo

zio = 0.0 - zo

dio = math.sqrt(xio * xio + yio * yio + zio * zio)

, as well as ratios of the distance of each coordinate in relation to the total distance.

xl = xio / dio

yl = yio / dio

zl = zio / dio

Next, we will need a few more complicated functions to ensure we map the half-sphere facing us correctly to the image (and not trace pixels that are on the opposite side, for instance):

dot = xl * xoc + yl * yoc + zl * zoc

val = dot * dot - doc2 + r * r

if val >= 0: # if there is line-sphere intersection

if val == 0: # 1 intersection point

d = -dot

else: # 2 intersection points => choose the closest

d = min(-dot + math.sqrt(val), -dot - math.sqrt(val))

Once we have identified the area closest to us, we will render it using the equations for the equirectangular projection...

xd = xo + xl * d

yd = yo + yl * d

zd = zo + zl * d

x = (xd - xc) / r

y = (yd - yc) / r

z = (zd - zc) / r

...but as part of that, we need to incorporate the rotations in the xy plane and in the xz plane, as we indicated with xyr and xzr parameters at the start...

x0=x*cxy-y*sxy;y=x*sxy+y*cxy;x=x0 # xy-plane rotation

x0=x*cxz-z*sxz;z=x*sxz+z*cxz;x=x0 # xz-plane rotation

Now, there is a third rotation, namely in the yz plan (yzr), and that rotation was actually not working properly in the original script from ActiveState. To fix this, I replaced the old equation with a simpler approach, where I simply add the yz rotation as an offset to the longitude of the area that we are visualizing:

lng = (math.atan2(y, x) + pi2 + yz) % pi2

To do the final rendering, we also, need the lattitude of course,

lat = math.acos(z)

and we actually write the pixels of our destination image, and return our result at the very end of the function:

ix = int((imgxInput - 1) * lng / pi2 + 0.5)

iy = int((imgyInput - 1) * lat / math.pi + 0.5)

try:

pixelsOutput[xi, yi] = pixelsInput[ix, iy]

except:

pass

return imageOutput

If you put all this together, and you'd test it out, e.g. using imageOutput = render_world(0.25,0.25,0.0,384,384)

imageOutput.save(“World.png”, “PNG”))

you would get an image like this:

From single image to spinning animation

To make the animation of the world spinning, we generate all the images in a loop, and append them to an array. This is similar to what we did before in the last Small Sims post. We start off by defining the width and height in pixels (384 for now), and the number of frames (10). I didn't choose these values because they look cool, but rather because repl.it is quite slow, and can time out for much higher values. If you use your own Python installation, you can easily crank up the resolution:

if __name__ == "__main__":

xsize = 384

ysize = 384

steps = 10

Next up, we generate our first frame (which appears twice in the animation to indicate a clear starting point),

b = [render_world(0.25,0.25,0.0,xsize,ysize)]

, and then we generate all the other frames, increasing the yz rotation at every step:

for i in range(0, steps):

b.append(render_world(0.25, 0.25, (0.0+float(i)/float(steps))%1.0,xsize,ysize))

With all the frames generated, all that we need to do is to write the whole thing to an animated gif.

b[0].save("test.gif", save_all=True, append_images=b[1:], duration=100, loop=0)

And after you've coded all that, you can run the code and generate this:

Wrap-up

And that's it! Now as a first step of creating your own game world, you can now make your own map, and use that image to generate a spinning world. It will still work, even if the dimensions aren't quite right.

Now there are still a few things I want to share before I end this post:

  • If you have a cool world map and are able to make a rotating sphere like the one above, please send it to me (djgroennl@gmail.com). I'll happily highlight the best ones in a future blog post (if you permit me to do so), and if I get entries from 8 or more different people I'll award 5 XRP to the best entry as a tiny gesture of appreciation.
  • As usual, I'll provide the source code and a repl.it.
  • And for subscribers, I've got a slightly edited version of the Wikipedia world image without the white border, and a version of the script that uses numpy, and should be a tiny tiny bit faster.

Credits

The header image is a projected version of the aforementioned image by WikipediaUserStrebe, CC BY-SA 3.0.

Appendix: full source code and Replit

As always, you can find the full source code here, as well as the repl.it.

# Animated World Projection

# Image from: https://en.wikipedia.org/wiki/Equirectangular_projection

#(By Strebe - Own work, CC BY-SA 3.0, https://commons.wikimedia.org/w/index.php?curid=16115228)

# This is a modified version of the recipe found at:

# http://code.activestate.com/recipes/580695-image-projection-onto-sphere/

import math

from PIL import Image

def render_world(xyr, xzr, yzr, imgxOutput, imgyOutput):

pi2 = math.pi * 2

# 3D Sphere Rotation Angles (arbitrary)

xy = -pi2 * xyr

xz = -pi2 * xzr

yz = -pi2 * yzr

sxy = math.sin(xy); cxy = math.cos(xy)

sxz = math.sin(xz); cxz = math.cos(xz)

#syz = math.sin(yz); cyz = math.cos(yz)

imageInput = Image.open("Equirectangular_projection_SW.png")

(imgxInput, imgyInput) = imageInput.size

pixelsInput = imageInput.load()

imageOutput = Image.new("RGB", (imgxOutput, imgyOutput))

pixelsOutput = imageOutput.load()

# define a sphere behind the screen

xc = (imgxOutput - 1.0) / 2

yc = (imgyOutput - 1.0) / 2

zc = min((imgxOutput - 1.0), (imgyOutput - 1.0)) / 2

r = min((imgxOutput - 1.0), (imgyOutput - 1.0)) / 2

# define camera view point

xo = (imgxOutput - 1.0) / 2

yo = (imgyOutput - 1.0) / 2

zo = -min((imgxOutput - 1.0), (imgyOutput - 1.0))

xoc = xo - xc

yoc = yo - yc

zoc = zo - zc

doc2 = xoc * xoc + yoc * yoc + zoc * zoc

for yi in range(imgyOutput):

for xi in range(imgxOutput):

xio = xi - xo

yio = yi - yo

zio = 0.0 - zo

dio = math.sqrt(xio * xio + yio * yio + zio * zio)

xl = xio / dio

yl = yio / dio

zl = zio / dio

dot = xl * xoc + yl * yoc + zl * zoc

val = dot * dot - doc2 + r * r

if val >= 0: # if there is line-sphere intersection

if val == 0: # 1 intersection point

d = -dot

else: # 2 intersection points => choose the closest

d = min(-dot + math.sqrt(val), -dot - math.sqrt(val))

xd = xo + xl * d

yd = yo + yl * d

zd = zo + zl * d

x = (xd - xc) / r

y = (yd - yc) / r

z = (zd - zc) / r

x0=x*cxy-y*sxy;y=x*sxy+y*cxy;x=x0 # xy-plane rotation

x0=x*cxz-z*sxz;z=x*sxz+z*cxz;x=x0 # xz-plane rotation

lng = (math.atan2(y, x) + pi2 + yz) % pi2

lat = math.acos(z)

ix = int((imgxInput - 1) * lng / pi2 + 0.5)

iy = int((imgyInput - 1) * lat / math.pi + 0.5)

try:

pixelsOutput[xi, yi] = pixelsInput[ix, iy]

except:

pass

return imageOutput

if __name__ == "__main__":

imageOutput = render_world(0.25,0.25,0.0,384,384)

imageOutput.save("World.png", "PNG")

xsize = 384

ysize = 384

steps = 10

b = [render_world(0.25,0.25,0.0,xsize,ysize)]

for i in range(0, steps):

b.append(render_world(0.25, 0.25, (0.0+float(i)/float(steps))%1.0,xsize,ysize))

b[0].save("test.gif", save_all=True, append_images=b[1:], duration=100, loop=0)

You can find a Repl here.

Read more...

Ever had that feeling that you want to develop something, but you don't know what language to use? I've had it many times, for instance when developing a computer game, a message passing library, or a simulation code. In this brief article I'll share what 7 considerations mainly guide my choice.

1. Existing expertise

This is the most important one in most cases. Experts don't need to learn the language to do the job, know where the development pitfalls are and know where to turn to for further support. If you know a language well, then that's a legitimate reason to prefer that language for future projects.

Or, perhaps you're new to coding and are not sure where to begin? For sure, scripting languages like Python and R tend to be simpler than lower-level languages such as C++. And sometimes you can even choose to start off without programming. For instance, for game development you can get pretty far with Unity without doing any coding.

2. Your personal and career development

How do you want to develop yourself when you develop this software? Do you want to become a crack in one language, become a flexibly deployable jack-of-all-trades, or maintain a working knowledge of a couple of languages? If you want to learn a lot of languages, then make sure you write programs in many different languages as well. But in relation to this, also think about the following aspects:

  • Can you re-apply your knowledge of this language for a wide range of other software projects?
  • Would learning/mastering this language help you in any way to get a better job, or become better in your current job? Or is it a very specific language that has a single use?
  • Is there a large demand for people with skills in the language you're considering?
  • If it's a new language: is it similar to any other important languages you know, so that you can more easily learn those later on?

3. Development performance

What is development performance? Simply put, it means the speed with which you can write new code, understand and modify existing code and test any code you've written. If you use a language with good development performance then coding is as quick and painless, and making mistakes is not much of an issue. On the other hand, if you use a language with poor development performance then coding is slow, and mistakes may take a long time to resolve. Development performance matters a lot when you're still unsure what your code is meant to be like, but it's not as important if you have a well-defined idea (e.g., when you're porting code from another language, or the underlying theory is already well established).

So what aspects may testify for a good development performance?

  • Short and human-readable syntax: e.g., function names that are descriptive, support for defaults in function arguments, or values of different types, limited use of special characters in the syntax, convenient ways to handle complicated data, minimal boilerplate code.
  • Rapid testing: e.g., ability to run code without compiling first, quick compilation, good compilation/execution error diagnostics pointing to file names and line numbers, presence of good testing and continuous integration tools.
  • Simple and intuitive program structures: e.g., simple ways to include code, transparent object hierarchies that avoid complex templating (e.g., CRTP), and simple ways organize your code in packages.

4. Platform

Many languages are more appropriate for some platforms than for others. For instance, C# works particularly well on Windows platforms, C++ for supercomputer platforms, Swift for iPhone development, and JavaScript for web development. Make sure you check which languages are well-supported for your target platform.

And sometimes, be prepared to cobble together something yourself. For instance, I still haven't found a good online environment for prototyping game engines (which I need for part of my blog), so I may well end up with something quite customized there :).

5. Runtime performance

Another aspect to keep in mind is the runtime performance. Some languages like R openly admit to being slow, so you're warned in advance. In general, lower-level languages like C++ and Fortran tend to run faster than high-level languages like Python and R. However, lower-level languages also often have worse development performance (see 3), so this can be very annoying. If you want to have both development and runtime performance, then you could try using a high performance scripting language like Julia, or combine a scripting language like Python with libraries written for performance in other languages (e.g., through NumPy).

6. Community support

Simply put, some languages have small communities with few well-maintained libraries (e.g., Algol), and some have large communities with numerous well-maintained libraries (e.g., Python). The difference matters, particularly if you try to combine many types of functionalities in your software, or if you're not completely familiar with your language of choice yet.

7. Ease of use

Lastly, whatever you develop will end up being used somewhere. Now some languages can result in programs that are quite tricky to install and use (anyone remember the tediousness around the Java Virtual Machine?). Have a look at what programs written in your choice of languages are going to look like to users. Are they easy to install, and can they operate them in a seamless way, or do you need your users to do an assault course and five tutorials before they can click a button or move a sprite in your software? Needless to say, the difference matters.

Wrap-up

These 7 aspects matter the most when choosing a programming language for a new project. Although there are many other things that matter as well, I think this write-up should help you to avoid the biggest pitfalls when choosing a programming language.

For Coil subscribers, I've added a bit on using multiple languages, and provide a bit of signposting to other relevant blogs about this topic. If you are a subscriber, please see below the credits for details :).

Credits

Cover image courtesy of the AAU Blog.

Ninja warrior image courtesy of Piotr Siedlicki.

Hardware image by Clovis Cheminot from Pixabay.

Powerglove image is courtesy of Nintendo.

Read more...

Previously in Small Sims we looked at simulating people walking in random directions, and boring topics such as simulating crypto prices. Today we are going to do something quite simple and fundamental: simulating living populations

Like last time, we'll use repl.it to make this simulation, because it works just about anywhere.

So our mission is:

  • to make a simple simulation that mimics the growth, migration and decay of populations.

As before you can choose to either:

1. Follow it as an actual tutorial, then create a new repl, and add code to your main.py file whenever you see blocks like:

#This is an example comment.

2. or simply read/skim/browse the post, and download a working repl.it example at the end, which you can test and modify as you like.

What libraries do we need today?

We are going to need a few more libraries than usual today. These include the Python Image Library (or its derivative pillow), as well as some routines from the common numpy and scipy libraries. As a whole, our imports look like this:

from PIL import Image, ImageDraw

import numpy as np

from scipy.signal import convolve2d

(update Jan 2020:) To actually use PIL, you also need to set up Pillow, which you can do as follows:

1. Click on the Package Icon on the left side of your screen when you have you repl.it open:

2. And then select the Pillow 7.0.0 package, and press + (once you've pressed it, it will change to a -

You may have to wait a little while before the package is set up, but you only have to do this once :).

The Game of Life algorithm

This particular topic happens to map very nicely to a classic cellular automaton model called “the Game of Life”. The Game of Life was devised by John Horton Conway (1937-2020, may he rest in piece), and is arguably more of a simulation than a game, since there are no actual players in it. It consists of a grid of “living” and “dead” cells (which we name life_grid). Here a cell is set to 0 if it is dead, or 1 if it is alive. Whether a cell changes from living to dead (or vice versa), depends on how many living neighbors it has:

  • A living cell with two or three neighbors stays alive; it dies due to starvation if it has <2 neighbors, or due to overpopulation if it has >3 neighbors.
  • A dead cell with exactly three neighbors becomes alive due to reproduction, and stays dead in all other cases.

When we put these rules in code, it looks roughly like this:

def step(life_grid):

# Count the number of neighbours for each cell.

kernel = np.array([[1,1,1],[1,0,1],[1,1,1]])

neigh_counts = convolve2d(life_grid,kernel,'same')

for x in range(0, life_grid.shape[0]):

for y in range(0, life_grid.shape[1]):

# Cell is alive

if life_grid[x][y] == 1:

if neigh_counts[x][y] == 2:

pass

elif neigh_counts[x][y] == 3:

pass

else:

life_grid[x][y] = 0

# Cell is dead

else:

if neigh_counts[x][y] == 3:

life_grid[x][y] = 1

By the way, the Game of Life is well-known, and you can find a pretty good overview of it on Wikipedia, among many other places.

Drawing the life grid

Now this life_grid may be interesting, but it'd be cool if we can draw it. To do so, we define the following function to draw a grid on an image d:

def draw_grid(d, life_grid):

""" Draws a game of life grid. """

for x in range(0, life_grid.shape[0]):

for y in range(0, life_grid.shape[1]):

if life_grid[x][y] == 1:

d.ellipse([x*50,y*50,(x+1)*50,(y+1)*50],fill="#fff")

else:

d.ellipse([x*50,y*50,(x+1)*50,(y+1)*50],fill="#888")

Here, we depict every live cell with a white circle, and a dead one with a gray circle.

Building your simulation

Now that we have two big building blocks in place (our algorithm for advancing time in the simulation, and our algorithm for drawing the grid), we want to bring in the rest.

First, we want to define the main function in our script, and introduce parameters that we can use to adjust the size of the life grid (in x and y dimensions), as well as the number of time steps we want to simulate:

if __name__ == "__main__":

xsize = 10

ysize = 10

steps = 5

Since we have many cells that change state at different times, the best way to visualize our output is by making an animation. To do this, we need to make an list containing multiple images, each of which is large enough to fit all the cells. We do this as follows:

# make a blank image for the text, initialized to transparent text color

base = Image.new('RGBA', (xsize*50+10,ysize*50+10), (0,0,0,0))

b = [base]

d = [ImageDraw.Draw(base)]

Not unimportant, we also need to construct the life_grid, and ensure a few cells are alive (no interesting behavior occurs if all the cells are dead...).

life_grid = np.zeros((xsize, ysize), dtype=int)

#put an example block

life_grid[5][5] = 1

life_grid[5][6] = 1

life_grid[5][7] = 1

With all that in place, we can finally set up the main loop in our simulation. In this loop, we do the following at every time step:

  • Draw the grid with cells.
  • Add a new empty base image to our animation.
  • Advance our life_grid system by one time step, using the game of life algorithm.

Now arguably, you can play around with the order of things in this loop, but this setup is very simple, and gives an okayish result. In code it looks like this:

for i in range(0, steps):

draw_grid(d[-1], life_grid)

b.append(Image.new('RGBA', (1024,1024), (0,0,0,0)))

d.append(ImageDraw.Draw(b[-1]))

step(life_grid)

Now at this point the simulation has run completely, and all the frames of the animation have been stored. The last thing we then have to do, is save the whole thing to an animated gif:

b[0].save("test.gif", save_all=True, append_images=b[1:], duration=1000, loop=0)

Please note that repl.it still has this image writing bug. So: please delete test.gif in the main directory before you (re-)run your script.

Once you have that in place, you get something like this:

Closing thoughts

In this bread-and-butter blog post I showed how you can program your own Game of Life. If you set the right squares in your life grid to alive (1), you can make all sorts of patterns, including:

  • Populations that oscillate between different states (like the small example I made).
  • Populations that remain stables (e.g., a 2x2 block).
  • Populations that disappear (e.g., a 1x1 block).
  • Populations that multiply endlessly (e.g., Gosper's Glider Gun).

I appreciate that these simulations are very common, and you can find them easily online. However, I think it is particularly nice for all of you to be able to actually code such things yourself. In addition, it's also great to see just how simple this stuff is: the code, including the animation-building, is only 68 lines (!) in total.

Also, I managed to introduce you to Pillow and simple animation-building through this tutorial. This is handy, because I'm pretty sure I'll be re-using some of that in my future tutorials :).

I won't leave expert questions this time (there's little I can ask to which Google doesn't know the answer), but I will give you an overview of the full code (and a link to the repl I made). For the Coil subscribers, I will give an update on that other track I have been planning (on the step-by-step building of a computer game).

(Credit: header image courtesy of Wikipedia user Kieff)

Next: Bouncing Ball (with Pygame).

Previous: Crypto Price Chart.

Appendix 1: Code summary

The full code will look like this:

from PIL import Image, ImageDraw, ImageFont

import numpy as np

from scipy.signal import convolve2d

def step(life_grid):

# Count the number of neighbours for each cell.

kernel = np.array([[1,1,1],[1,0,1],[1,1,1]])

neigh_counts = convolve2d(life_grid,kernel,'same')

for x in range(0, life_grid.shape[0]):

for y in range(0, life_grid.shape[1]):

# Cell is alive

if life_grid[x][y] == 1:

if neigh_counts[x][y] == 2:

pass

elif neigh_counts[x][y] == 3:

pass

else:

life_grid[x][y] = 0

# Cell is dead

else:

if neigh_counts[x][y] == 3:

life_grid[x][y] = 1

def draw_grid(d, life_grid):

"""

Draws a game of life grid.

"""

for x in range(0, life_grid.shape[0]):

for y in range(0, life_grid.shape[1]):

if life_grid[x][y] == 1:

d.ellipse([x*50,y*50,(x+1)*50,(y+1)*50],fill="#fff")

else:

d.ellipse([x*50,y*50,(x+1)*50,(y+1)*50],fill="#888")

if __name__ == "__main__":

xsize = 10

ysize = 10

steps = 5

# make a blank image for the text, initialized to transparent text color

base = Image.new('RGBA', (xsize*50+10,ysize*50+10), (0,0,0,0))

b = [base]

d = [ImageDraw.Draw(base)]

life_grid = np.zeros((xsize, ysize), dtype=int)

#put an example block

life_grid[5][5] = 1

life_grid[5][6] = 1

life_grid[5][7] = 1

for i in range(0, steps):

draw_grid(d[-1], life_grid)

b.append(Image.new('RGBA', (1024,1024), (0,0,0,0)))

d.append(ImageDraw.Draw(b[-1]))

step(life_grid)

b[0].save("test.gif", save_all=True, append_images=b[1:], duration=100, loop=0)

And a working repl.it of it can be found here (please do make sure you delete test.gif before re-running the script).

Read more...

Last time in Small Sims we looked at simulating people walking in random directions. Today we are going to take one step further, and build on the things we put together in that last post to make a very basic simulation of the price movement of a cryptocurrency. Think of generating an chart a bit like this:

(image courtesy of Binance.com)

Like last time, we'll use repl.it to make this simulation.

So our mission is:

  • to make a simple simulation that can make a jittery curve like this, reproducing a typical crypto price curve.

In this post I will explain you a way how to make this, and you can follow it in two ways:

1. If you want to follow it as an actual tutorial, then create a new repl, and add code to your main.py file whenever you see blocks like:

#This is an example comment.

2. Or, you can simply read/skim/browse the post, and download a working repl.it example at the end, which you can then test and modify as you see fit :).

What libraries do we need today?

To make our lives much easier, we will need to paste in all the code from the previous Random Walk post into our main.py file.

We do need to comment out the last line though, to prevent the original Random Walk simulation from being run.

From a Random Walk to a Random Price Change algorithm

Remember, the Random Walk worked like this:

  • As a starting point you have an object (e.g., an NPC) on a given location.

    Then, at every time step the object:

    - picks a random direction (North, South, East or West)

    - and moves one step in that direction.

Now to move the price, we need to reinterpret the x and y coordinates. In the graph above, the time is on the x axis, while the price is on the y axis. To start of, we make a point at x=0, with a starting price of say, y=$10.

Every step of the simulation, we want to increment the time by one (so x +=1), and we want to change the price by, say, up to 2% (so somewhere between y *= 0.98 and y *= 1.02). To do this, we need to change the “directions” in our Random Walk algorithm, such that instead of going North, East, South or West, we go +2%,+1%,unchanged,-1% or -2%. In code, such a changed step function looks like this:

def random_price_change(xs, ys):

direction = random.randint(-2, 2)

newx = xs[-1] + 1

# time x is incremented by 1

newy = (ys[-1] * (100.0 + direction)) / 100.0

# y is multiplied by a value in the range of 0.98 to 1.02.

xs.append(newx)

ys.append(newy)

Making a random price progression, using the algorithm

Just like last time, making the chart is just a matter of calling the same function many times. You can do this as follows:

def random_price_chart_simple(x_start, y_start, steps=10):

xsprice = [x_start]

ysprice = [y_start]

for i in range(0,steps):

random_price_change(xsprice, ysprice)

return xsprice,ysprice

Of course, making the function alone will not give you any output when you run the script. To actually use the function and generate a price graph with 100 time points, you can use:

xsprice,ysprice = random_price_chart_simple(0,10, steps=100)

And to print out the values in text, just use:

print(xsprice,ysprice)

Plotting the output of your simulation

The main reason why it's useful to reuse the code from my previous post, is because the plotting is very similar. You can actually re-use the plot_random_walk() function without any further modifications for this. Just add:

plot_random_walk(xsprice,ysprice,name='random_price_chart.png')

(I changed the filename of the output to something more appropriate for this post)

Now remember: due to a glitch in repl.it, you should delete your output random_price_chart.png file every time you rerun your code. If not, you may risk generating a corrupted image.

If you run the code, you will obviously get a random graph. Mine looks like this:

Which by coincidence is not too far off from the chart above :).

By the way, if the numbers near the point annoy you, you could remove them by commenting out these lines in plot_random_walk().

for i in range(len(xswalk)):

x = xswalk[i]

y = yswalk[i]

plt.plot(x, y, 'bo')

plt.text(x + 0.1, y + 0.1 , i, fontsize=12)

Closing thoughts

The main point of this post is to show how you can make a very basic change to an existing algorithm to use it for something rather difference. In addition, I chose this topic because I am curious whether people are interested in simulating price curves. I suppose I can gauge the interest by the upvotes this post gets in comparison to others, and then take that into account when choosing topics for future Small Sim editions :).

Just like last time, I will leave you with:

1. two questions for the experts.

2. an overview of the full code (and a link to the repl I made).

3. (only for Coil subscribers): some new suggestions on improving Coil and Coil.com.

Previous: Random Walk.

Next: Living Populations.

Appendix 1: Expert question

Here's two questions to challenge the experts:

  • Can you make the random graph generator such that it generates exactly the same graph for everyone who is using your program?
  • How would you incorporate “whales” in your simulation, who could periodically pump the price up by a large amount, and then dump at an arbitrary later moment?

Appendix 2: Code summary

So in conclusion, the full code will look like this (note that you could choose to leave the random_step and the random_walk functions out from the code if you prefer):

### BEGIN OF PREVIOUS RANDOM WALK EXAMPLE

# we need to generate random numbers

import random

# and we need to plot stuff.

import matplotlib.pyplot as plt

import matplotlib as mpl

mpl.use('Agg')

# define a function that takes one step in a random direction

# we assume 4 possible directions: N,E,S,W.

def random_step(xs, ys):

direction = random.randint(0,3)

newx = xs[-1]

newy = ys[-1]

if direction == 0:

newx -= 1

if direction == 1:

newx += 1

if direction == 2:

newy -= 1

if direction == 3:

newy += 1

xs.append(newx)

ys.append(newy)

def random_walk(x_start, y_start, steps=10):

xswalk = [x_start]

yswalk = [y_start]

for i in range(0,steps):

random_step(xswalk, yswalk)

return xswalk,yswalk

# Let's try it out!

xswalk,yswalk = random_walk(100,100)

print(xswalk,yswalk)

def plot_random_walk(xswalk, yswalk, name='random_walk.png'):

plt.clf()

#plot the lines

plt.plot(xswalk, yswalk)

#plot the points

plt.plot(xswalk, yswalk, 'ro')

# add a step number near each point.

for i in range(len(xswalk)):

x = xswalk[i]

y = yswalk[i]

plt.plot(x, y, 'bo')

plt.text(x + 0.1, y + 0.1 , i, fontsize=12)

plt.show()

plt.savefig(name)

# Let's try to plot our random walk

# plot_random_walk(xswalk,yswalk) ## commented out because we don't want to run the random walk example here.

### END OF PREVIOUS RANDOM WALK EXAMPLE

# Now we make a variation for a price chart.

def random_price_change(xs, ys):

direction = random.randint(-2, 2)

newx = xs[-1]+1

newy = (ys[-1] * (100.0 + direction)) / 100.0

xs.append(newx)

ys.append(newy)

def random_price_chart_simple(x_start, y_start, steps=10):

xsprice = [x_start]

ysprice = [y_start]

for i in range(0,steps):

random_price_change(xsprice, ysprice)

return xsprice,ysprice

# Let's try it out!

xsprice,ysprice = random_price_chart_simple(0,10, steps=100)

print(xsprice,ysprice)

plot_random_walk(xsprice,ysprice,name='random_price_chart.png')

And a working repl.it of it can be found here.

Read more...

In the first edition of this Small Sims series, I will keep it simple and show you how you can code a random walk. Random Walks are useful in simulation because you can use them to mimic the movements of people wandering around town, for example in games such as these:

Source: *http://kidscancode.org/blog/2018/01/godot3_inheritance/*, a site with some great game tutorials by the way.

Another thing you can do with random walks is tocreate cool looking fractal-like images such as these:

Image and header image courtesy of László Németh.

Our platform of choice is repl.it, because it allows you all to make and run the code without installing any Python. The platform does have a few glitches though, but I'll carefully highlight them as part of this post.

So our mission is:

  • to make a simple simulation that can make a jittery curve like this, reproducing a typical crypto price curve.

In this post I will explain you a way how to make this, and you can follow it in two ways:

1. If you want to follow it as an actual tutorial, then create a new repl.it, and add code to your main.py file whenever you see blocks like:

#This is an example comment.

2. Or, you can simply read/skim/browse the post, and download a working repl.it example at the end, which you can then test and modify as you see fit :).

Creating a repl.it

For full details on how to use repl.it, see the website. However, in a nutshell if you want to create a new “repl”, you can go to the website and press the “new repl” button. You then get a screen like this:

...where you can select Python as your programming language. Once you've done that you're all set up, and you'll have a main.py file opened, where you can put in the code from our tutorial. By the way, to run the file, just press the “run >” button at the top.

Update 05-01-2020: install Pillow as well

Never versions of repl.it don't come with Pillow set up, but you can easily install this inside your repl.it. Just click on the Package Icon on the left side of your screen when you have you repl.it open:

And then select the Pillow 7.0.0 package, and press + (once you've pressed it, it will change to a -

What libraries do we need today?

We need to generate random numbers:

import random

and we need matplotlib to plot basic data:

import matplotlib.pyplot as plt

import matplotlib as mpl

mpl.use('Agg')

So be sure to put those 4 lines at the top of your main.py. Note: the last two lines are specific to repl.it: they're needed to make sure you can write images without any errors.

The Random Walk algorithm

So what does the algorithm look like in a very basic form?

As a starting point you have an object (e.g., an NPC) on a given location.

Then, at every time step the object:

- picks a random direction (North, South, East or West)

- and moves one step in that direction.

In code, a function that picks a direction and takes a step will look roughly like this:

def random_step(xs, ys):

"""

generate a random step

xs: x coordinates of previous locations of the object.

ys: y coordinates of previous locations of the object.

"""

direction = random.randint(0,3)

newx = xs[-1]

newy = ys[-1]

if direction == 0: # Step west

newx -= 1

if direction == 1: # Step east

newx += 1

if direction == 2: # Step south

newy -= 1

if direction == 3: # Step north

newy += 1

xs.append(newx)

ys.append(newy)

This function actually does most of the work, as you will see.

Doing an actual Random Walk, using the algorithm

With a function in place to do a single random step, doing a walk is a matter of simply repeating the function many times. For instance, you can do it using a function like this:

def random_walk(x_start, y_start, steps=10):

xswalk = [x_start]

yswalk = [y_start]

for i in range(0,steps):

random_step(xswalk, yswalk)

return xswalk,yswalk

If you'd like to try out if your random walk code works, you can simply run the random_walk function and print the coordinates so you can check them:

xswalk,yswalk = random_walk(100,100)

print(xswalk,yswalk)

And that's all there is to it! Although... you may want to actually see what your random walk looks like, right?

Plotting the output of your simulation

To make a graph of your random walk, we will use the commonly used matplotlib library. We'd like to draw lines of the steps taken, points where the object was at each time step, and also numbers next to the points indicating where the object was at each of these steps. Here's a function that covers all three of them:

def plot_random_walk(xswalk, yswalk, name='random_walk.png'):

plt.clf()

#plot the lines

plt.plot(xswalk, yswalk)

#plot the points

plt.plot(xswalk, yswalk, 'ro')

# add a step number near each point.

for i in range(len(xswalk)):

x = xswalk[i]

y = yswalk[i]

plt.plot(x, y, 'bo')

plt.text(x + 0.1, y + 0.1 , i, fontsize=12)

plt.show()

plt.savefig(name)

...and you can test the plotting by simply calling it again:

plot_random_walk(xswalk,yswalk)

NOTE: around this part there are a few glitches in repl.it that are worth noting:

  • First, plt.show() doesn't quite work properly in repl.it, but will show your plot on the screen if you run it with a regular Python installation. However, it does save a figure under the name random_walk.png, which you can open on the left side.
  • Second, there is a peculiar glitch where the png file is not generated if it already exists. So if you choose to rerun the code, please make sure you delete the random_walk.png file first!

Anyway, once you've done that you'll get a random walk! Mine happens to look like this:

As you can see, my agent started at [100,100], and then went North, South, North, South, West, South, South, South, East and East :).

Closing thoughts

Anyway, all basic stuff but all beginning is simple. Moreover, we'll be using some of this code in future editions in this series, as the Random Walk algorithm is an excellent foundation for more exciting simulations!

So what still remains? Well, I'll leave you with:

  1. a question for the experts.
  2. an overview of the full code (and a link to the repl I made).
  3. (only for Coil subscribers): my feedback to Coil, based on my experience of making this blog post.

So, here we go!

Next: Crypto Price Chart.

Appendix 1: Expert question

Here's one question to challenge the experts:

  • What is the probability that any 10-step random walk does not contain the same location twice?

Appendix 2: Code summary

Just the full code will look roughly like this:

import random

import matplotlib.pyplot as plt

import matplotlib as mpl

mpl.use('Agg')

# define a function that takes one step in a random direction

# we assume 4 possible directions: N,E,S,W.

def random_step(xs, ys):

direction = random.randint(0,3)

newx = xs[-1]

newy = ys[-1]

if direction == 0:

newx -= 1

if direction == 1:

newx += 1

if direction == 2:

newy -= 1

if direction == 3:

newy += 1

xs.append(newx)

ys.append(newy)

def random_walk(x_start, y_start, steps=10):

xswalk = [x_start]

yswalk = [y_start]

for i in range(0,steps):

random_step(xswalk, yswalk)

return xswalk,yswalk

# Let's try it out!

xswalk,yswalk = random_walk(100,100)

print(xswalk,yswalk)

def plot_random_walk(xswalk, yswalk, name='random_walk.png'):

plt.clf()

#plot the lines

plt.plot(xswalk, yswalk)

#plot the points

plt.plot(xswalk, yswalk, 'ro')

# add a step number near each point.

for i in range(len(xswalk)):

x = xswalk[i]

y = yswalk[i]

plt.plot(x, y, 'bo')

plt.text(x + 0.1, y + 0.1 , i, fontsize=12)

plt.show()

plt.savefig(name)

# Let's try to plot our random walk

plot_random_walk(xswalk,yswalk)

And a working repl.it of it can be found here.

If you have any specific questions or comments, feel free to contact me by e-mail (djgroennl@gmail.com) or via Twitter (@whydoitweet).

Read more...

It has been 5 days since my initial post, and so far no questions have rolled in as of yet. To be fair, that is roughly what I expected because (1) this blog only had one post to begin with, (2) I only just started thinking about how to structure things and (3) it's quite hard for people to think up questions, when there is little material to start questioning.

One and two are easily solved, but the third point got me thinking, and I realized it makes sense to set up so-called parallel tracks. These parallel tracks will be:

  • Responses to questions (see my last post for details).
  • Small articles where I will cover very simple individual things that one can easily simulate. I'll describe the content here, but I will also make a Jupyter Notebook available for these cases. These articles will be stand-alone, and quite light-weight so that also non-programmers should get a decent idea of how to do this.
  • Larger articles towards building something much larger step-by-step. The best way to do this is to take a particularly entertaining and rich form of simulations, namely a computer game. Now for this it will not make much sense to use Jupyter notebooks, so I am considering a different online platform, for instance repl.it in combination with the Atlas toolkit. These articles will build a bit more on top of each other, and will be a bit more advanced than the small articles (e.g., I expect the reader to know roughly enough Python to make a simple graph).

So, three tracks to cater for three purposes. Now it's time to start putting some stuff out... (to be continued)

(header picture courtesy of Jaymanti: https://www.pexels.com/photo/industry-rails-train-5348/)

Hello everyone. My name is Derek Groen, and I am a Lecturer in Computer Science at Brunel University London. As part of my work I teach Digital Media & Games, and for research I build simulations of all sorts (as part of two EU projects, HiDALGO and VECMA). Currently, I have a strong interest in modelling the movement of people, but in the past I have also modeled blood flow in arteries, composite materials using molecular dynamics, and even star clusters and dark matter as part of my PhD work.

I have been a Coil subscriber for a while, but I have been quite reluctant to become a content creator myself. The main reason is that much of the content I create is already entirely free and open, and created in a blended effort between personal life and work. You can find much of it either in the papers and open-source software that I contribute to, or in the public student-resources GitHub repository that I currently maintain (https://github.com/djgroen/student-resources). So following that logic, there would be no need for me to become a Coil content-creator.

However, there is at least one reason why it could useful for me to be here. Some of you might not be interested in what I have already shared, but might want to know my thoughts on a particular topic within my range of expertise. If that is the case, then please drop me a line, and I will respond publicly through a Coil blog post, and make any prototype data or software associated with the response available through the student-resources repository.

So what is my range of expertise? Well, the full list is here (http://people.brunel.ac.uk/~csstddg/research.html) but in a nutshell I specialize in the following:

  • Developing simulation codes, for a wide range of purposes.
  • Coupling different simulation codes or models to each other.
  • Parallelizing software programs (I ran a cosmological simulation across four supercomputers in four countries a good ten years ago).
  • Making complicated simulation workflows easier to handle, modify and reproduce.
  • Modelling the movements of people, particularly in a (forced) migration context.

So, let me know if you want me to shed light on any particular matter. You can reach me either through Twitter (@whydoitweet) or via e-mail (djgroennl@gmail.com). And who knows, perhaps there will be a contact me function here on Coil directly one day...

Read more...