Langjam Gamejam seemed like a fun challenge. Game development and programming language design? What kind of game and language should I make? The possibilities are endless. My first idea was to make a simple state machine language and write a piece of interactive fiction. The focus would be in the game and storytelling, as the technical aspects of the language wouldn’t be interesting.
I thought it would be more challenging and fun to work with some sort of constraint; making a language with a familiar syntax wouldn’t be very interesting. Also, how would I handle the interaction between the language and the graphical and input elements? That was also something to consider. How to embed graphics, sound, color, and input capabilities to the game?
A thought came to me way before the jam started. I only started to write down the spec when the deadline countdown began.
My idea is: What if the screen contained the game’s state?
Each pixel of this hypothetical screen could be in one of many states. Each state would have an associated color. Pixels, or cells as I’m going to call them, would be able to interact with neighbors and react based on inputs.
For example, to simulate a falling pixel, I came up with this syntax:
# Define some states for the cells CELL empty fill white CELL particle fill black GRID 10 10 empty # Fill a 10x10 grid with empty cells ACTION particle fall # Define an action for particle cells, similar to methods in OOP # current cell will become "empty" SET empty # the cell under it (+0 x, +1 y) will become a particle EXECUTE appear 0 1 END ACTION empty appear SET particle END
Now every time the fall action is triggered on particle cells, the particle will move down.
Particles should fall as time passes, so I added two new keywords.
TICK 100 ms # Run tick every 100 ms INPUT tick ALL particle fall END
Here’s the first interactive example.
CELL empty fill white CELL particle fill black TICK 100 ms GRID 10 10 empty ACTION particle fall SET empty EXECUTE appear 0 1 END ACTION empty appear SET particle END INPUT tick ALL particle fall END SET particle 2 2 SET particle 4 4
Every tick, the fall action will be run on all cells (Only particle cells will be affected. Other input directives can be added, e.g. to detect key presses.
With this, we have the base of a programming language:
INPUT ArrowLeft)Is this enough to make a game?
Snake was my first thought. After all, the entire game’s state is present on the screen:
The only thing missing is its current direction, but that can be represented as four different states, all having the same color.
CELL food fill red CELL empty fill white CELL head_up fill green CELL head_down fill green CELL head_left fill green CELL head_right fill green
Every tick, the head should move in the current direction it’s pointing at. Additionally, it can react to key presses to change directions.
CELL food fill red CELL empty fill white CELL head_up fill green CELL head_down fill green CELL head_left fill green CELL head_right fill green GRID 30 30 empty SET head_up 15 15 SET food 20 20 TICK 100 INPUT tick ALL head_up move ALL head_down move ALL head_left move ALL head_right move END ACTION head_up move SET empty SET head_up 0 -1 END ACTION head_down move SET empty SET head_down 0 1 END ACTION head_left move SET empty SET head_left -1 0 END ACTION head_right move SET empty SET head_right 1 0 END ACTION head_left go_up SET head_up END ACTION head_right go_up SET head_up END ACTION head_left go_down SET head_down END ACTION head_right go_down SET head_down END INPUT ArrowUp ALL head_left go_up ALL head_right go_up END INPUT ArrowDown ALL head_left go_down ALL head_right go_down END ACTION head_up go_left SET head_left END ACTION head_down go_left SET head_left END ACTION head_up go_right SET head_right END ACTION head_down go_right SET head_right END INPUT ArrowLeft ALL head_up go_left ALL head_down go_left END INPUT ArrowRight ALL head_up go_right ALL head_down go_right END
When the snake eats a piece of food, it grows. This new segment will keep moving in the same direction that the head was moving.
In the move action, instead of simply changing the state, additional behavior must be added. Also, the RAND keyword is introduced, to turn a random pixel into a piece of food.
CELL food fill red CELL empty fill white CELL head_up fill green CELL head_down fill green CELL head_left fill green CELL head_right fill green # Tail cells CELL tail_up fill yellow CELL tail_down fill yellow CELL tail_left fill yellow CELL tail_right fill yellow GRID 30 30 empty SET head_up 15 15 SET food 20 20 SET food 20 10 TICK 100 INPUT tick ALL head_up move ALL head_down move ALL head_left move ALL head_right move END ACTION empty become_food SET food END ACTION empty move_up # Snake moves up into an empty space SET head_up SET empty 0 1 EXECUTE move_tail_up 0 2 # Move the tail segment thats closest to the head END ACTION empty move_down SET head_down SET empty 0 -1 EXECUTE move_tail_down 0 -2 END ACTION empty move_left SET head_left SET empty 1 0 EXECUTE move_tail_left 2 0 END ACTION empty move_right SET head_right SET empty -1 0 EXECUTE move_tail_right -2 0 END ACTION food move_up # Snake moves up into a piece of food SET head_up # The head takes the place of the food SET tail_up 0 1 # New tail segment RAND empty become_food END ACTION food move_down SET head_down SET tail_down 0 -1 RAND empty become_food END ACTION food move_right SET head_right SET tail_right -1 0 RAND empty become_food END ACTION food move_left SET head_left SET tail_left 1 0 RAND empty become_food END ACTION head_up move EXECUTE move_up 0 -1 END ACTION head_down move EXECUTE move_down 0 1 END ACTION head_left move EXECUTE move_left -1 0 END ACTION head_right move EXECUTE move_right 1 0 END ACTION head_left go_up SET head_up END ACTION head_right go_up SET head_up END ACTION head_left go_down SET head_down END ACTION head_right go_down SET head_down END ACTION tail_up move_tail_up SET tail_up 0 -1 SET empty EXECUTE move_tail_up 0 1 END ACTION tail_down move_tail_down SET tail_down 0 1 SET empty EXECUTE move_tail_down 0 -1 END ACTION tail_left move_tail_left SET tail_left -1 0 SET empty EXECUTE move_tail_left 1 0 END ACTION tail_right move_tail_right SET tail_right 1 0 SET empty EXECUTE move_tail_right -1 0 END INPUT ArrowUp ALL head_left go_up ALL head_right go_up END INPUT ArrowDown ALL head_left go_down ALL head_right go_down END ACTION head_up go_left SET head_left END ACTION head_down go_left SET head_left END ACTION head_up go_right SET head_right END ACTION head_down go_right SET head_right END INPUT ArrowLeft ALL head_up go_left ALL head_down go_left END INPUT ArrowRight ALL head_up go_right ALL head_down go_right END
There’s some behavior missing, when turning, the tail segments don’t follow the head. More states need to be created to properly make the snake work.
It was at this point that I realized that implementing Snake was proving to be more complicated than expected. The biggest challenge is making sure the tail segments “know” where the previous segment is, so they can move together every tick.
I didn’t want to further enhance the language so implementing Snake would become easier (or possible, I still don’t know if it is!)
I opted for implementing an easier game. A TRON clone for two players.
Use WASD and the arrow keys to move.
CELL player1_up fill red CELL player1_down fill red CELL player1_left fill red CELL player1_right fill red CELL player1_trail fill pink CELL player2_up fill blue CELL player2_down fill blue CELL player2_left fill blue CELL player2_right fill blue CELL player2_trail fill cyan CELL empty fill black GRID 40 40 empty SET player1_right 2 20 SET player2_left 37 20 TICK 50 INPUT tick ALL player1_up move ALL player1_down move ALL player1_left move ALL player1_right move ALL player2_up move ALL player2_down move ALL player2_left move ALL player2_right move END ACTION player1_up move SET player1_trail EXECUTE collision 0 -1 SET player1_up 0 -1 END ACTION player1_down move SET player1_trail EXECUTE collision 0 1 SET player1_down 0 1 END ACTION player1_left move SET player1_trail EXECUTE collision -1 0 SET player1_left -1 0 END ACTION player1_right move SET player1_trail EXECUTE collision 1 0 SET player1_right 1 0 END ACTION player2_up move SET player2_trail EXECUTE collision 0 -1 SET player2_up 0 -1 END ACTION player2_down move SET player2_trail EXECUTE collision 0 1 SET player2_down 0 1 END ACTION player2_left move SET player2_trail EXECUTE collision -1 0 SET player2_left -1 0 END ACTION player2_right move SET player2_trail EXECUTE collision 1 0 SET player2_right 1 0 END ACTION player1_trail collision HALT END ACTION player2_trail collision HALT END INPUT w ALL player1_left go_up ALL player1_right go_up END INPUT a ALL player1_up go_left ALL player1_down go_left END INPUT d ALL player1_up go_right ALL player1_down go_right END INPUT s ALL player1_left go_down ALL player1_right go_down END INPUT ArrowUp ALL player2_left go_up ALL player2_right go_up END INPUT ArrowLeft ALL player2_up go_left ALL player2_down go_left END INPUT ArrowRight ALL player2_up go_right ALL player2_down go_right END INPUT ArrowDown ALL player2_left go_down ALL player2_right go_down END ACTION player1_up go_left SET player1_left END ACTION player1_up go_right SET player1_right END ACTION player1_down go_left SET player1_left END ACTION player1_down go_right SET player1_right END ACTION player1_left go_up SET player1_up END ACTION player1_left go_down SET player1_down END ACTION player1_right go_up SET player1_up END ACTION player1_right go_down SET player1_down END ACTION player2_up go_left SET player2_left END ACTION player2_up go_right SET player2_right END ACTION player2_down go_left SET player2_left END ACTION player2_down go_right SET player2_right END ACTION player2_left go_up SET player2_up END ACTION player2_left go_down SET player2_down END ACTION player2_right go_up SET player2_up END ACTION player2_right go_down SET player2_down END # Walls CELL wall fill gray SET wall 0 0 SET wall 0 39 ACTION wall collision HALT END # Draw top wall ACTION wall grow_right SET wall 1 0 EXECUTE grow_right 1 0 END ACTION wall grow_down SET wall 0 1 EXECUTE grow_down 0 1 END ALL wall grow_right EXECUTE grow_down 0 0 EXECUTE grow_down 39 0
The language is very verbose, lots of similar code must be repeated to handle the user inputs and all the possible movement directions. But hey! It works. Forget about making a game with more advanced math. Proving if this language is even Turing complete seems like a fun challenge.
I think I achieved my goal of making a very simple language (easy to parse and implement) that could show interesting behaviors. Perhaps adding stuff such as variables and functions can enable making more complex games.
Next time I should stick with a more traditional language…
CELL zero fill white CELL one fill black GRID 8 1 zero ACTION zero count SET one END ACTION one count SET zero EXECUTE count -1 0 END TICK 100 INPUT tick EXECUTE count 7 0 END
To implement a simple sokoban game the following behaviors must be implemented:
Handle when the player…
Use the arrow keys to move.
CELL player fill red CELL wall fill black CELL box fill gray CELL empty fill white TICK 100 GRID 20 20 empty SET player 10 10 SET box 8 8 SET box 4 2 SET wall 5 5 SET wall 5 6 SET wall 5 7 SET wall 16 16 SET wall 17 16 INPUT ArrowUp ALL player move_up END INPUT ArrowDown ALL player move_down END INPUT ArrowLeft ALL player move_left END INPUT ArrowRight ALL player move_right END ACTION player move_up EXECUTE player_move_up 0 -1 END ACTION player move_down EXECUTE player_move_down 0 1 END ACTION player move_left EXECUTE player_move_left -1 0 END ACTION player move_right EXECUTE player_move_right 1 0 END ACTION empty player_move_up SET player SET empty 0 1 END ACTION empty player_move_down SET player SET empty 0 -1 END ACTION empty player_move_left SET player SET empty 1 0 END ACTION empty player_move_right SET player SET empty -1 0 END ACTION box player_move_up EXECUTE box_move_up 0 -1 END ACTION box player_move_down EXECUTE box_move_down 0 1 END ACTION box player_move_left EXECUTE box_move_left -1 0 END ACTION box player_move_right EXECUTE box_move_right 1 0 END ACTION empty box_move_up SET box SET empty 0 2 SET player 0 1 END ACTION empty box_move_down SET box SET empty 0 -2 SET player 0 -1 END ACTION empty box_move_left SET box SET empty 2 0 SET player 1 0 END ACTION empty box_move_right SET box SET empty -2 0 SET player -1 0 END
When the player attempts an invalid move (e.g. Moving into a wall) An action is called on the cell to which the player or the box wants to move. If the action is not defined, nothing happens.
I decided to call the language InterState (Interacting States)
# Comment GRID <width> <height> <default state> # Cell definition CELL <name> <shape> <color> # Tick time definition TICK <duration in milliseconds> # Action definition ACTION <cell> <action> SET <cell> [dx] [dy] # Defauly X Y values are 0, 0 EXECUTE <action> [dx] [dy] ALL <cell> <action> END # Input definition INPUT <input name> # When outside an action or inside an input, coordinates are absolute SET <cell> <x> <y> EXECUTE <action> <x> <y> END # Execute <action> on a random <cell> RAND <cell> <action> # Stop the program HALT
I only implement a single shape type for the cells: fill. However, SVG allows to use more than pixels.
ALL executes actions on cells in a very specific order (row by row, left to right, up to down). This can cause problems in some cases. Adding an option to specify the order in which cells are updated is a good idea.