Introduction
In this article, we are going to see how to make a quick Snake game using the Grace language. This tutorial introduces many specificities about the language in practice that you might have seen in Grace in one page.
This tutorial will show the main points of the Snake implementation for you to try by yourself and it will not be entirely implemented.
We highly recommend using Minigrace Online to practice, this implementation of the Grace language includes arrays and graphics content. Also, you can find the full code in the samples when you look for “DOM Snake”.
The environment
In Grace, you can import pre-made modules or make your own that can be useful for coding.
For this program, we will need the “dom” one in order to use the Document Object Model elements for the graphics and user interaction. Also, the math module will be needed for the random events as the position of the apple. Modules can be imported like this :
1 2 |
import "dom" as dom import "math" as math |
Now we can successfully use DOM elements to create the canvas in another tabulation, and display a white rectangle as a background.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
def document = dom.document def ts = document.getElementById("output-select") for (0..(ts.options.length-1)) do { i -> if (ts.options.item(i).value == "canvas") then { ts.selectedIndex := i dom.window.outputswitch } } def canvas = dom.document.getElementById("standard-canvas") def ctx = canvas.getContext("2d") ctx.fillStyle := "white" ctx.fillRect(0, 0, 500, 500) |
Classes and objects
First of all, we need to define the main objects of the game, the state of the game, the snake (which will be an array of “dots”) and the apple to be eaten. For the implementation, we will create a class which will make dots, the body of the snake.
1 2 3 4 5 6 7 8 9 |
def document = dom.document class dot.ofX ( xp : Number) ofY ( yp : Number) { var x is readable, writable := xp var y := yp method getY{ return y } } |
The class is made, and each object “dot” generated by it has to be given the x and y coordinate with numbers. You can change the accessibility of the attributes from outside of the class by making them readable or writable, or by implementing getter et setters as seen right before. For the rest of the implementation, the attributes will be readable and writable.
You also can define objects only with attributes and methods, which will be useful as we use only one apple and one snake.
The body of the snake is an array, which contains methods like push or length
1 2 3 4 |
def snake = object { var size := 3 var elements is readable := [] } |
The apple and the state of the game will be also objects, the fruit will reappears randomly after being eaten.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
def game = object { var live is readable, writable := true var score is readable, writable := 1 } def apple = object { var x is readable, writable var y is readable, writable method appears { x := math.random * 50 x := x - (x % 1) x := x * 10 y := math.random * 50 y := y - (y % 1) y := y * 10 } } } |
Using DOM elements with Grace
Grace can implement easily the DOM elements we need for the game. As we saw first with the background, we can easily draw the objects needed for with rectangles. This is implemented as methods of the objects.
The apple and the state of the game will be also objects, the fruit will reappears randomly after being eaten.
1 2 3 4 5 6 7 8 |
def apple = object { // Rest of the code method draw { ctx.fillStyle := "red" ctx.fillRect(x, y , 10, 10) } } |
Premade Javascript functions can also be used in order to use events listeners to the document. It is essential here for the interaction with the user and their keyboard.
1 2 3 4 |
def keyboardListener = { ev -> direction.changeDirection(ev.keyCode) } document.addEventListener("keydown", keyboardListener, true) |
Arrays, loops and conditions
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
def snake = object { // Rest of the code elements.push(dot.ofX(250) ofY(250)) elements.push(dot.ofX(240) ofY(250)) method draw { ctx.fillStyle := "black" for (1..elements.size) do { i -> ctx.fillRect(elements[i].x, elements[i].y , 10, 10) } } } |
1 2 3 4 5 6 7 8 9 10 11 12 |
def direction = object { // Rest of the code method changeDirection(keyCode) { if (keyCode == 38) then { // Do something } elseif {keyCode == 37} then { // Do something else } elseif // etc... } |
Promises
The game is divided in two parts, at first when the game occurs then when the snake got hit, meaning game over.
This will be done with promises from javascript.
1 2 3 4 5 6 7 8 9 10 |
dom.while {game.live} waiting 42 do { // Code here ctx.fillStyle := "white" ctx.fillRect(0, 0, 500, 500) snake.draw apple.draw }.then { // When the while loop terminates, run this block. ctx.fillText("Score : {game.score}", 250, 125) } } |