Gray Soft

20

I've now written two articles covering low-level curses and some higher abstractions. We know what curses can do at this point, but we haven't really seen how to put everything together and build a full application. Let's do that today by examining an example of moderate size.

11

In the previous article, I showed how you can accomplish some terminal input and output operations using the (n)curses(w) library. What I showed for output were mostly low-level writing routines though. Included in curses are some higher level abstractions that can be used to manage your program's output. This time, I want to show some of those tools.

Windows

The primary abstraction used in curses is the concept of a window. We actually already used them last time, but we stuck with stdscr (standard screen) which is just a window that fills the entire terminal. That's more for using curses without thinking much about windows.

When you want them though, you can section off the terminal screen into rectangular chunks. Each chunk is a window that you can print output inside of. Here's the most basic example that I could dream up:

require"ffi-ncurses"beginstdscr=FFI::NCurses.initscrFFI::NCurses.cbreakFFI::NCurses.noechowindow=FFI::NCurses.newwin(5,15,4,2)# make a new windowFFI::NCurses.waddstr(window,"Hello world!")FFI::NCurses.wrefresh(stdscr)# still need thisFFI::NCurses.wrefresh(window)FFI::NCurses.getchensureFFI::NCurses.endwinend

28

In my last article, I showed off some of the various magic commands a Unix terminal recognizes and how you can use those commands to move the cursor around, change the colors of your output, and read input in a character by character fashion. It's not very common to manipulate things manually as I did in those examples. There are libraries that wrap these mechanisms and add abstractions of their own. Using one of them can be easier and less error prone.

Probably the most famous of these higher abstraction libraries is curses. However, your version won't be called that. curses was the original library for System V UNIX. These days you are far more likely to have ncurses which a replacement library that emulates the original. The truth is, you probably don't have exactly that library either. Instead, you may have ncursesw, which is the same library with wide character (read: non-ASCII) support. Also, curses is often discussed with several add on libraries: panel, menu, and form. Documentation often covers these separate units together.

30

I've recently been playing around with fancy terminal output in Ruby. I've learned quite a bit about this arcane magic. I've also realized that the documentation is pretty spotty. I want to see if I can improve that with a few blog posts, so let's dive right in.

Output

Program output typically happens in a linear order from top to bottom. For example, this code:

puts"onez"puts"twos"puts"threes"

generates this output:

onez
twos
threes

But what if you need to change some output? Could you replace the z above with an s if you needed to? Yes, but it can get a little involved.

ANSI Escape Codes

In many cases, we just push some characters to $stdout (the stream Kernel#puts is writing to above) and your terminal program happily shows them to the user. However, your terminal is watching these characters for special sequences that it understands. Some of those sequences of characters can cause your terminal to take actions other than just writing some output to the screen.

31

I meet a lot of programmers that tell me they got started because they wanted to build games. However, when I ask most of them which games they have built, the list rarely includes anything more than mostly unplayable toy projects.

I can only guess at the reasons for this oddity, but I suspect it might be due to the fact that games are fairly complex. Even if you want to rebuild a fairly simple classic like Space Invaders or Snake you need to know at least a little about event loops, keyboard handling, animation, and collision detection. If your day job involves a different kind of programming, like Web application development, odds are good that you don't get a lot of practice with these concepts.

That may not be your story, but it was definitely mine. This year I decided that it was finally time to learn how to build games. I used several sources to gain this knowledge and some helped more than others, but the biggest win by far was a book called Game Programming Patterns by Bob Nystrom.

31

I've been playing with Dart quite a bit lately. I really enjoy the language, but there are always snags that trip up those coming from other backgrounds. Here are the top three issues that have bit me in Dart, in the hopes of saving others some pain:

The Truth and Nothing But the Truth… Literally!

One of the challenges of any language is figuring out what it considers to be truthy in conditional expressions. Each system has its twists, but I find Dart to be extra strict in this case.

Here's some code illustrating the rule:

boolisTruthy(Objectcondition){return!!condition;}voidmain(){vartests=[true,false,null,42,0,"",[],newObject()];for(vartestintests){print("$test is ${isTruthy(test)}");}}

That outputs:

$ dart truthiness.dart
true is true
false is false
null is false
42 is false
0 is false
is false
[] is false
Instance of 'Object' is false

As you can see the literal true (just that one object) is truthy in Dart and everything else is considered false. I'm in the habit of playing pretty fast and loose with truthiness from all of my time working with Ruby, so this has surprised me a few times.

24

Yesterday I showed a newer programmer some code like scores.sort_by(&:reverse). This provoked a comment about how they where going to look up sort_by() later to figure out what magic is involved here. It made me sad to realize how many cool tricks they weren't going to see in that bit of documentation.

Allow me to enumerate those tricks for you, but first let's flesh out an example. Consider this code:

In this case, the magic method call (scores.sort_by(&:reverse)) has reordered a list of Cribbage hands first by point value and then alphabetically ("ASCIIabetically" in truth). How this happens is a pretty interesting journey though.

20

In Rails, methods like underscore() and camelize() use several regexen to transform the String under the hood. Many people have asked if you can do it with a single regex though. These specs I borrowed from Rails seem to say yes:

About

James Edward Gray II was a part the Ruby community before Rails
ever shipped. He wrote code and documentation that now come with
the language. He ran two Red Dirt Ruby Conferences and is now
a regular on the Ruby Rogues podcast. He does all of this just
because he loves to program. This site is where he writes about
that.