Creating a Console Game In Java
Tags: java, gamedev, terminal, beginners
Published: 2024-02-04 12:15:51

Hello everyone, I have been learning Java for a while, and I was thinking about making a project to practice programming. After giving it some thought, I decided to make a Java snake game that runs on consoles.

To create the game, I first had to figure out exactly where to move the cursor and print in the terminal. After some research, I found [ANSI Escape Sequences] (https://en.wikipedia.org/wiki/ANSI_escape_code), which are incredibly powerful and simple-to-use escape sequences that can manipulate your terminal with just a few lines of code. Basically, you can print "ESC[{x};{y}H" to move the cursor to a specific x, y position, and it will move there. There are many commands of this type, such as those that manipulate color, change the background and foreground colors, sound an alarm, clear the screen, etc. I've found a good list of commands created by [fnky] (https://github.com/fnky). You can see it here. (https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797)

I attempted to use a for loop to move the cursor to random locations after fiddling with the ANSI codes, as you can see here.
```
for (int i = 0; i<10; i++) {
   System.out.print("\e[i;iH");
}
```
The code above is merely an example; you can see that I am telling the terminal that the text that follows is an escape sequence by using the escape character "\e."

The next big task was figuring out the width and height of the terminal so that the game's boundaries and play area could be defined. This was actually a pretty challenging task because every operating system has a different way of figuring out the size of the terminal, like:
You may utilize tput in Linux:
```
tput lines # get number of lines (Height)
tput cols # get number of columns (Width)
```
and for PowerShell on Windows:
```
$Host.UI.RawUI.WindowSize.Width
$Host.UI.RawUI.WindowSize.Height
```

So, After researching, I've found multiple libraries, such as 

- [Jline](https://github.com/jline/jline3))
- [Lanterna](https://github.com/mabe02/lanterna)
- [Picocli](https://picocli.info/)
- [Jansi](https://github.com/fusesource/jansi)

And after doing some research, I ultimately decided on JLine because it was neither too complicated, which would help me learn new things, nor too simple, which would have prevented me from creating the main game logic. As a result of my decision, I now know how to create a terminal and obtain its height and width, essentially.

```
Terminal terminal = TerminalBuilder.builder().system(true).dumb(false).build());
System.out.println(terminal.getWidth());
System.out.println(terminal.getHeight());
```
Everything is enclosed in a try block in order to utilize all of Jline's features and prevent errors from occurring when a terminal is created that is not supported. To enable full functionality for cross-platform apps, it requires an additional library (https://github.com/jline/jline3?tab=readme-ov-file#jni-vs-jansi-vs-jna-vs-ffm-vs-exec), which may be jansi or jna.

After choosing and learning about JLine, I had to choose a build tool. Since I had never used Gradle before, I opted to use it so that I could become familiar with it. Next, I added JLine and Jna as dependencies.

Finally, after all of this, it was time to actually create the game. To do this, I started by creating a game class that will manage all of the main game mechanisms and an input handler that will handle user input. The input handler was created in a new thread because it required user input to be entered in a nonblocking manner, meaning that while the game class updates the screen, the input handler will continue to receive user input in order to move the snake.

I first created the bit class, which is the single bit of the snake, and the x and y coordinates of each bit are what will be used to print it. This is because the snake is composed of bits.

Syncing the movement of two printed pieces together posed an extremely significant challenge. In order to make it appear as though it was moving, I first tried printing a single bit. To do this, I printed it while increasing its x or y coordinates by one, then I printed it while printing space in its previous coordinates. To achieve this, I added prevX and prevY to the Bit class. Next, I added a second bit by matching the current x and y coordinates with the previous x and y of the first bits. In this way, I only updated the first bit, and everything will move in that direction.

In order to provide direction, I made an Enum Direction that will be mapped to the W A S D keys and contains the UP, DOWN, LEFT, and RIGHT directions.
Thus, the enum value will change to UP, and the snake will move upward when a user clicks the W key in this manner. I have therefore devised a clever mechanism to move the snake by simply adding and subtracting the x and y so that

```
UP: x--
Down: x++
Left: y--
Right: y++
```

The snake will move in that direction if the aforementioned operations are carried out on it in x and y coordinates, so I added a switch case and ran the loop to account for this.

In order to make the food appear, I made a new food class with x and y coordinates that were also randomly selected. This way, each time the snake eats the food, it will appear in a different location.

In order to determine when the snake is eating the food, I added checks to the snake class by observing the xy of the snake's initial bite and the xy of the food. The food will reappear at a random location if both are the same.

I also added a border check, noting that it was necessary in case the snake crossed it, so the game ends when the snake touches the border.

and also when it touched its body, but I decided to leave that feature in place since I thought it was cool that a snake could move in any direction even when it touched its body.

Thus, the main logic of the game was finally implemented. Colors and design were all that were lacking. After doing some research, I discovered that the jline ansi library or the ansi escape sequence can be used to implement colors. As a result, I chose to use the jline ansi library, and colors are now randomly selected from a color array. Additionally, I learned about ASCII art, or text-based art. After that, I came upon [Ascii Art] (https://asciiart.eu), a website that allows you to manipulate any text using ASCII art. I decided to use it to create my game's logo, Start Menu, Pause Menu, and Game Over Menu.

To make the snake appear like a rectangular box, I just colored a blank space a different color. For the food, I used various Unicode character sheet symbols, such as circles and zeros.[Unicode Character Sheet](https://en.wikipedia.org/wiki/List_of_Unicode_characters)

And so, I have finally finished the game after much modification and refactoring of all the functions in various classes.
So you can see the game's code in my Github page 
Repo:- https://github.com/zedai00/snakez

This was my experience making the snake game, and although I apologize for not going into too much detail, I will do so in the comments section if you have any. Thanks.