Build the Game's View
00:00 In the previous lesson, I showed you how to use a TOML file to populate the initial patterns in the game. In this lesson, I’ll cover how to animate the frames in the terminal.
00:11 Curses is a library supported by most operating systems that allows you to arbitrarily position the cursor in your terminal and write a string. Python includes a wrapper to curses in the standard library.
00:23 As a module, you use curses to clear the screen, position a cursor, and write a string. By doing this in a loop, you can create an animated visualization of the game.
00:37 Curses expects a reference to a function or method that takes no arguments. It then calls this method to print out the state, so our code will determine which pattern to use, how many iterations to display, and the size of the bounding box.
00:53 It also has to consider a frame rate, how many frames per second to display. Then it enters a display loop. With each iteration of the loop, it calculates the current state.
01:03 Based on the last state, refreshes the screen with the current content, then sleeps until it is time to display the next frame. Let’s go look at the code.
01:15 I’ve designed the code to separate the presentation of the grid from the grid itself. This is a common pattern in visualization code. If you’ve ever heard of a model-view-controller, it’s similar to this approach.
01:28 The key reason for doing this is you can now add a different visualizer, say a GUI library, without making any changes to the grid code. The class on screen here encapsulates the code needed to display our grid using calls to the curses library.
01:44 Its constructor takes the pattern to display, the number of generations to evolve, the frame rate, and, like with a string, a bounding box for the grid to display.
01:55
All of these values get stored in the class for later reference. The entry point to all of our views is the show()
method. If you write another view as part of the homework, yes, there’s homework.
02:07
The key part of it is to implement a view named show
for a curses view implementation. The show()
method calls the curses library wrapper method, which expects a reference.
02:19 Curses calls this referred method on your behalf, and the actual screen rendering code goes in there.
02:27 This is the method that will get called by curses. Curses passes it a handle to a screen object. This object is what you operate on in order to manipulate the terminal.
02:38
The draw()
method only gets called once. The loop and frame calculations are embedded inside of here. The first step in draw()
is to create a grid, no different from how it was done in the REPL in previous lessons.
02:53 Then there’s a bit of setup work. This call makes the screen’s cursor invisible so you don’t see it when it is writing to the terminal. And before writing to the screen, you need a blank tableau.
03:04
So clear()
gets called. Scrolling down.
03:10
The addstr()
method is what draws the screen. The two zeros are the coordinates of the starting position, and then it is simply a call to the grid’s as_string()
method, which returns the string representation of the grid.
03:24
Now that you’ve drawn it, once you enter the game loop and draw each subsequent frame once for each generation specified in the constructor. Once drawn, you calculate the frame’s next data set, repeat the call to addstr()
to print it out, and tell curses to update the screen.
03:44
The addstr()
method is actually writing to a buffer, and the refresh()
call prints the whole buffer at a time. This is done to avoid the terminal blinking.
03:52 As content is rendered, it makes the animation smoother.
03:57
Finally, before looping again, you want to pause for a moment. How long is a moment? Well, it depends on the frame rate in the constructor. sleep()
takes seconds, so one over the frame rate tells you how many fractions of a second to pause until the next frame is due.
04:13 Let me open up a REPL, import the class
04:22 and get the pattern factory
04:29 and in one line, instantiate the class. And
04:53 calls are great and all, but I want a program. Let’s do that next.
Become a Member to join the conversation.