Being a Programming Language Hipster with Céu

I did a little game project recently in a new-to-me language. You’ve probably never heard of it.

Hipster doggo

It’s called Céu! The SDL bindings I used were added to the language super-recently. I wanted to try working in Céu as I’ve been learning (somewhat passively) about concurrency in Python, and making something in a language that is all about synchronous design and concurrency appealed to me. So it was an interesting and challenging experience to make a game in a couple days with these tools:

Céu is a young language, and there is sparse to no documentation for the SDL environment. There is a nice manual available online, and some academic papers. But both of those have to do with the Céu language more generally.

Error messages in Céu are not Googleable. You are not going to be able to find many helpful answers for issues that come up in Céu development on StackOverflow. There is a Google Group where Céu is discussed, the documentation mentioned above, and a Gitter chat group, but getting an immediate solution to a specific problem is tough using the usual tried and true method of a quick search.

Another error-related feature I take for granted is that the console error message will help me locate and track down bugs as I develop. But Céu has unhelpful console error messages. Console error messages provide a line number in Céu, but it is a wild goose chase. I am speculating, but I think the line number refers to compiled code. So it’s not much help in tracking down bugs, especially small syntactic errors like missing semicolons and end. It’s not entirely pointless, as errors do at least tell you what issue you are looking for, if not where to find it.

unhelpful error messages

On the plus side, there were twelve example programs that shipped with the SDL environment for Céu. Those were what I referenced whenever I wanted to get something accomplished. Still, there were some really hard problems - getting a string variable that would be updated with the score turned out to be a huge problem. After trying to get it working for a few hours (!), I worked around the issue of having a text variable rendered to screen with a graphical score display.

graphical score display

Another plus was having someone to work with. @Johnicholas has used Céu before, and he was helpful when it came to language features that were new to me. Such as:

This is what a helper function looks like in Céu:

// A simple rect-rect collision checking function
code/tight Collision (var SDL_Rect r1, var SDL_Rect r2) -> bool
do
  if r1.x+r1.w < r2.x then
    escape false;
  end
  if r1.y+r1.h < r2.y then
    escape false;
  end
  if r2.x+r2.w < r1.x then
    escape false;
  end
  if r2.y+ r2.h < r1.y then
    escape false;
  end
  escape true;
end

And here’s what a class looks like:

code/await Particle (var& _SDL_Renderer ren, var SDL_Rect rect, var int ship_velx, var int ship_vely, var SDL_Color c) -> void do
  // Generate particles with randomized velocity that take into account the ship's velocity on impact
  var int velx = _rand() % 6 - 3 + ship_velx;
  var int vely = _rand() % 6 - 3 + ship_vely;

  par/or do
    // Shrink the particles every quarter-second
    every 250ms do
      if rect.w > 1 then
        rect.w = rect.w - 1;
        rect.h = rect.h - 1;
      else
        break;
      end
    end
  with
    // Check that the particle is still on-screen and move it
    // If offscreen, break the whole execution for this particle
    every 50ms do
      if rect.x > 0 and rect.x < 800 and rect.y > 0 and rect.y < 480 then
        rect.x = rect.x + velx;
        rect.y = rect.y + vely;
      else
        break;
      end
    end
  with
    // Draw the particle
    every SDL_REDRAW do
      _SDL_SetRenderDrawColor(&&ren, c.r, c.g, c.b, c.a);
      _SDL_RenderFillRect(&&ren, (&&rect as _SDL_Rect&&));
    end
  end
end

The main feature of Céu is trails of execution, which are marked by blocks starting with par, par/and, par/or, and connected blocks starting with with. These trails execute in parallel, and allow the program to model simultaneous threads of logic waiting for multiple events. The three different parallel block keywords indicate different behavior when one of the blocks terminates, which you can see above in the Particle class example.

Céu provides parallel compositions to allow multiple lines of execution, aka trails, to coexist and wait for multiple events. The await is available to suspend a trail and wait for an event from the outer environment that Céu is running it, and the emit statement signals an output event back to the environment. Parallel compositions allow the code to wait for and react to multiple events from the environment and from the program itself really fast.

My finished program has some errors at compile time. The late frame warnings are because my antivirus is quite defensive about these programs, so nothing troubling there. But Céu warns me that I have tight loops, or unbounded loops. I would have been pretty concerned about these without Johnicholas’s help.

Compile time errors

Céu wants each path inside the body of a loop to contain at least one await or break statement. This is to ensure that reaction chains never run in an arbitrary amount of time.

The compiler detects and warns when trails can run in unbounded time: loops that do not await or break, the so-called tight loops.

One way to rewrite the tight loops which Céu is warning me about would be Ceu’s await async (variable) do. But the warnings do not negatively impact this small program, so it’s more of an imperative-programmer style incompatibility issue here. Something to overcome next time!

Overall, I really enjoyed working with Céu, and was proud of the end result in proportion to how difficult it was to develop without some of my usual toolkit. I particularly like how the language pushes the developer toward object-oriented design. I could easily see becoming a Céu evangelist; it is an amazing project, and the more I learn the more impressed I am. I’m looking forward to doing more with Céu soon!

The game itself isn’t super interesting, but there’s a gif of gameplay below. The source code is in this repo, and if you have a Windows environment, you can download the complied code there and play the game.

Gameplay gif