Using curses in Games Programming

January 8, 2015


Around 1980, a library called curses was created at Berkeley for a BSD operating system. It was designed for the purpose of making it easier to create visual programs in a terminal environment. By ‘82, a freeware clone called pcurses was created by Pavel Curtis. By 1991 New curses (better known as ncurses is released, supplanting the popular pcurses library. It is still maintained to this day by Thomas E. Dickey, though you may know him for another popular program which he maintains: xterm.

Now that the history lesson has concluded, I want to talk about how the curses family can be used for game programming. In most cases, you will probably be using the ncurses library, but I will refer to it generally by its original name because almost all of the same concepts should be applicable to the original library. Since the creation of the original library, curses has been used in the creation of games. Probably the most famous game created with the library would be Rogue, the procedurally generated dungeon exploration game.

Rogue

For those who really (and I mean really) love working in a terminal environment, programs written with curses allow for complex interfaces to be created with relatively few lines of code, and even decently intricate games can be made by those with enough patience to learn the how to use it properly. For games like Rogue that advance on a timer or keypress, the library is perfect. Mathematical calculations (or manual placement) can be used with a simple addch() function to place individual characters to represent individual pieces of the game, or to make up a larger picture for the player. Characters are placed on a two-dimensional coordinate plane, making most calculations no more difficult to perform than basic geometry, or trigonometry if a more complemented function must be performed. That being said, that is only to represent the data which is created. Say a ball was being thrown into the distance. Naturally, the ball’s calculation could be considerably more complicated, but displaying it on the screen would only require the knowledge of how large the resulting circle was, that is unless one wanted to use the vast array of colors available to give the ball some depth. Still, not too difficult to calculate, I suppose. I should try that sometime just for fun…

Generally speaking, any game that is more than just a Choose-Your-Own-Adventure game is going to involve some sort of conditional looping that won’t end until a certain condition is met. Before starting this loop is where your window initialization should be. An important concept of curses is that everything is placed in a window, the default of which (in case you are attempting to do anything specific with it as I am) is stdscr. Your initialization will likely contain something like:

initscr();             // This initializes curses and allows curses to take over the terminal
cbreak();              // Disables character buffering, but allows control characters to generate signals
noecho();              // Prevents echoing of typed characters
keypad(stdscr, TRUE);  // Allows for input from the keypad, arrow keys, function keys, etc.
nodelay(stdscr, TRUE); // Causes getch() to return ERR and carry on when no other input is available

You can read more about what the individual initialization options are, as well as a rather comprehensive tutorial at The Linux Documentation Project. Once in the loop, you will likely perform actions which result in adding characters to the screen using addch() or some variant thereof. Once these actions have been performed, it is necessary to call refresh(), or wrefresh() to refresh only a specific window. This will redraw the screen, making the appropriate changes. In my case, there are a couple of things going on. First, the game gets input from the keyboard, and if it is an arrow-key, then the paddle is moved left or right accordingly. Next, an if statement checks to see if the appropriate number of ticks have passed before updating the ball’s location on screen. If not, one is added to the current total, and if it is, the value is reset, and the ball is moved, all the while checking with the collision system to determine in the ball should bounce or break any bricks. Lastly, the bricks are refreshed, removing any which have been destroyed. After all of these things have taken place, the screen is refreshed. This process occurs faster than you can blink, and it is one way that you can have both autonomous elements, and manually controlled elements in your game. Alternatively, the game could wait on getch() by setting nodelay(stdscr, FALSE) so that the game only updates when a key is pressed. As for the game itself, that can be anything for which you can imagine the code, whether that involves dungeon delving, constant action, or just rolling a random number and advancing on a virtual board. Oh, and did I mention that curses has mouse support? That’s right! You can use the mouse to click on elements in the terminal. This requires a bit more work, and I haven’t actually used it yet myself, but you can read more about it at the link that I posted above.

I hope that this has peaked your interest in curses, and maybe it will give your next terminal based game exactly what it needs to stand above the rest.