Python comes with a turtle graphics module right there in the standard library. A new and improved version was introduced with Python 2.6, and it is quite good. I think its real strength could be in the initial stages of teaching kids to program in Python.
For my use I found two major drawbacks. First, there is no built-in way to export your drawing to an image file (taking a screenshot is the solution I’ve been using). More importantly, the turtle drawing is intolerable slow for complex images.
I did manage to create this rather nice recursive tree:
(The leaves are dead turtles)
Nevertheless I think I will concentrate on some other turtle graphics implementation for now. After all, I already program in Python so I won’t be learning anything new there. Also did I mention slow? So slow.
I would be interested in learning UCBLogo as the language itself would be interesting for me to learn (A far more turtle-y Lisp dialect). Also from my initial tests it seems to be so much faster than turtle.py. However it also has limited capacity to save drawings as images (only eps output with no filled shapes 😦 ). The output window also appears buggy on my Ubuntu Linux system… The image in the window does not refresh after being obscured or minimised, which pretty much makes it useless to me until this issue has been addressed. I figured out that turning on window compositing fixes this somewhat. The output is no longer deleted after the output window is obscured by another window. If you minimise or resize the window the image disappears, but seems to come back most of the time if you type “REFRESH” into the Logo session.
Well I haven’t yet made a roguelike game, but I have completed a short tutorial on using the libtcod library.
Libtcod is a library with wrappers available for numerous programming languages which aids in the creation of roguelike games. I happen to be using the Python wrapper.
It provides a special true-colour console with plenty of neat tricks like customizable fonts, blitting of images and off screen drawing areas. These provide the potential for some stunning visuals that still have a blocky, old-school, feel.
While I’m sure these facts alone would make it useful for a great many styles of game, libtcod also provides numerous helpful toolkits which, for the most part, are aimed at roguelike developers.
Basically all of the standard routines that you would otherwise need to figure out for yourself before you can start designing your unique game. These include:
- Field of view toolkit. What can you see in the dungeon… where does your torchlight fall?
- Path finding toolkit. Compute the path from A to Z in your dungeon.
- Keyboard and mouse support
- BSP dungeon generation toolkit. Tweak the parameters for dungeons, towns, forests or whatever.
Plus a few more neat features you may not have ever imagined you would need for a roguelike:
- BMP and PNG support
- Heightmap toolkit 😯
- Perlin noise support. Hmmm… ocean anyone?
- Random number and compression toolkits
It’s probably important to note that you can use any or all of the toolkits that you choose… If you would prefer to implement your own dungeon generator for example, that’s fine too. In fact there is still plenty of work still to be done as a game developer. For instance, libtcod won’t implement the monster AI or combat for you (yet).
Lately I’ve been focusing on learning C++, and if I’m to learn one new language a year (which seems a reasonable goal) then this is certainly the year of C++. However, I would also like to have fun with a language that I already know well (ie. Python), and this is where I see libtcod fitting in. My verdict so far is that I like this library and I think I could have a lot of fun tinkering with it.
I’ll post my game here when it’s awesome 😀
Crikey, I nearly forgot! The tutorial that I was blathering on about at the top of this post is here. At the time of writing this only the first part has been written, but that is enough to create the explorable dungeon level seen in the screenshot above. Here’s hoping that the authors finish the remainder of the tutorial sometime soon.
So I’m hooked on SPOJ again. For this burst of enthusiasm I’m using C++, both to increase my skills and to clean up some of the problems that my Python solutions could not solve in the given time limit.
Of course this process does not produce many (any) pretty pictures, therefore not much content for this fledgling blog.
Still, maybe I’ll discover some sort of algorithm or something.
I was rather proud of myself for figuring out the relationship between the curved perimeter of a semi-circle and its area (yes, pretty advanced stuff, I know :-P). Where “p” is the curved part of the semi-circle’s perimeter, and “A” is the area:
I don’t want to give away exactly which problem this was for, but it would sure be useful if you needed to enclose the maximal space possible against an infinitely long wall by adding a fence of a given length and arbitrary shape which touches the wall at exactly two points.. *cough* 😉
These Buddhabrot scripts can sure be processor intensive. I would be better off programming them in C or C++, much as I do love Python. They are rather trivial, so it shouldn’t be too difficult.
One purpose of this blog is to openly record my musings of the various things that I think I ought to do—in the hope that I may remember to actually do them. This is but one such open musing.
I’ve now rendered my first Mandelbrot set, which was unexpectedly easy.
The concept of how to render the Mandelbrot set really only crystallized in my mind when I saw the following image:
This is in fact the lead illustration on Wikipedia’s Complex Number page (redistributed here under the Creative Commons Attribution ShareAlike 3.0 License). To my mind it provides a clearer demonstration of how the Mandelbrot set is formed than anything on Wikipedia’s Mandelbrot Set page.
Points in the Mandelbrot set are just complex numbers with the real and imaginary parts mapped to the x and y axis respectively (known as the complex plane or Argand diagram). A simple repeated calculation then determines whether a given point is part of the Mandelbrot set:
That is, starting with z at the origin, square z and add a complex constant c. The process of squaring the result and then adding the constant c is repeated until it is apparent that the the point z is either orbiting close to the origin (therefore indicating that the constant c is a member of the Mandelbrot set) or diverging to infinity (c is not a part of the Mandelbrot set). Simply plot on the complex plane which values of c are included in this set and the classic Mandelbrot set fractal should appear before your eyes as if by magic.
The procedures for such operations as multiplying and adding complex numbers are quite straight forward. You can get the gist of the kind of things that may happen when multiplying by a complex number by examining the image above (The point rotates on the complex plane). However, it is even easier when programming in a language such as Python which has built-in support for complex numbers.
Consider the following interactive Python session
>>> a = (2 + 3j) >>> b = (1 - 8j) >>> a * b (26-13j) >>> z = a**2 + a >>> z = z**2 + a >>> z (-214-87j)
The first four lines assign two complex numbers a and b and then display their product. The next four lines show exactly the kind of calculation one would need to do to find the Mandelbrot set. Easy.
Another useful tool is Python’s built in abs function. For real numbers this returns the absolute value of that number. For a complex number however, abs returns the modulus of the number. That is, it’s distance from the origin on the complex plane.
Abs is extremely useful in calculating the Mandelbrot set, as any orbit which strays more than distance 2 from the origin is known to be not part of the Mandelbrot set. Further testing of the point c is unnecessary and the next point can then be tested.
My first Python script for rendering the Mandelbrot set simply looped through each pixel in the image one at a time. Each pixel is assigned a complex number from a 4 x 4 grid. That is, a number in the square bounded by (-2 – 2j) and (2 + 2j). For each pixel I then applied the “square and add a constant” dynamic for a certain number of iterations to determine if it is a member of the Mandelbrot set.
This technique works, but is quite slow. Here is the code in its entirety:
#! /usr/bin/python import png import numpy as np import datetime def mandelbrot_set(size, exit_limit): # initialize array img_array = np.zeros([size, size], int) for y in range(size): for x in range(size): c = complex(x / float(size) * 4 - 2, y / float(size) * 4 - 2) z = c for i in range(exit_limit): z = (z**2) + c img_array[y, x] += 1 if abs(z) > 2: # z is escaping to infinity, so point is not in set break else: # if loop is exausted, point is inside the set img_array[y, x] = 0 return img_array if __name__ == "__main__": start = datetime.datetime.now() # size of final image: size = 1600 # after this many iterations, point is assumed to be within the # mandelbrot set: exit_limit = 64 imgWriter = png.Writer(size, size, greyscale=True, alpha=False, bitdepth=16) img_array = mandelbrot_set(size, exit_limit) # normalise array (16 bit range) img_array = np.array(img_array/float(img_array.max())*(2**16), int) # save to final render to png file f = open("mandelbrot.png", "wb") imgWriter.write(f, img_array) f.close() print datetime.datetime.now()-start print "Done."
If you are wondering where the different shades of color come from, It it simply that the lighter shades take more iterations for z to escape to a distance of 2 from the origin. The black pixels in the middle of the shape indicate that z does not escape.
Now how could I speed this program up? Looping through each pixel is inherently slow. I decided to try again, this time using a Numpy array of complex numbers (yes, complex numbers are a valid data-type in Numpy!). Now I no longer need to loop through the pixel of my image, only through the iterations of the “square and add a constant” dynamic.
This reduces the rendering time to about a third of that needed for the pixel-looping solution above! The only drawback seems to be that this method can use a lot of memory while its running. This is a problem if you want to create a very large image. For example, attempting to render a 3200 x 3200 image results in an error on my computer just due to the vast size of the complex number array that is required. That problem could easily be solved by rendering the image in several smaller chunks that are more RAM friendly. The image array is okay at that size (it’s just integers after all), so there would be no need to be manually stitching the pieces together afterwards!
Here is the code for my Numpy solution:
import numpy as np import png from datetime import datetime start = datetime.now() size = 800 iterations = 63 # turn range(0, size**2) into an array of complex values encompassing the render area complex_val = lambda p: complex(p%size/float(size)*4-2, p/size/float(size)*4-2) c = np.array([complex_val(p) for p in range(size**2)])\ .reshape(size, size) # This array is used to mask out values that have already escaped: operation_mask = np.ones((size, size), np.bool) # This will become the final image: iteration_array = np.zeros((size, size), np.int32) # our starting "z" values. Note I don't start at '0' because 0**2 + c is clearly c. z = np.copy(c) for x in range(iterations): print "iteration %d" % x z[operation_mask] = (z[operation_mask]**2) + c[operation_mask] iteration_array[operation_mask] += 1 # remove values form the "operation mask" if their calculation has diverged operation_mask[operation_mask] = np.absolute(z[operation_mask]) < 2 # finally, paint points in the mandelbrot set black. iteration_array[operation_mask] = 0 imgwriter = png.Writer(size, size, greyscale=True, alpha=False, bitdepth=8) f = open('m.png', 'wb') imgwriter.write(f, iteration_array) f.close() print datetime.now() - start
Where to from here? There a few things I could try next:
- Smooth coloration – get rid of the color banding
- Interactive zooming and exploration, using PyGame perhaps
- Render the Buddhabrot attractor, because that looks cool
- More speed optimizations.
Lately I’ve seen some colorized strange attractors around the web that could not have been produced simply by applying a colored gradient to a greyscale image… The distribution of color is too intricate and the colors seem to follow the different forms and branches of the attractor.
I’m not yet 100% sure how this is done, but I have one idea that I’d like to try.
Imagine that I introduce a third dimension, ‘Z’ to the strange attractor, and I use a pixel’s depth into this dimension to determine its color of the attractor at that point.
The pixels would be gathered with an additive effect in an RGB array. Hopefully this would result in a nice “colored smoke” effect.
I imagine it something like this:
| / hue | / |/ x-----+----- /| / | / | y
In a similar vein, I have been thinking of using 3D attractors to produce an animation. My plan is that each frame of the animation is a 2D slice of the attractor, moving back through the Z axis, like this:
| / time | / |/ x-----+----- /| / | / | y