Klemen's Stuff
| Miško 3 | Maze | Interactions | Case | Source code
Making a maze game on STM32G4
The goal of the project is to implement a maze game using C programing language. I'll be using a demo project1 that allready has implemented some basic functionalities and additional drivers.
1 https://github.com/LAPSyLAB/Misko3_Docs_and_Projects
Video of aA maze game gameplay on Miško 3

Parts list

Miško 3

Miško 3 is a development board for learning programming on microcontroller. The name stands for Microcontroller Student Kit (MIkrokrmilniški Študentski KOmplet) 3rd generation and it's powerd by STM32G474QETx, an ARM-based 32-bit MCU. It has 12 PWM pins, 4 serial communications (RX, TX), 30 digital pins, 12 analog pins and can supply 3.3V, 5V and 12V.
You can learn more about the MCU hereon https://www.st.com/resource/en/reference_manual/rm0440-stm32g4-series-advanced-armbased-32bit-mcus-stmicroelectronics.pdf .

Before we can use the board, we need to solder on 7 buttons, 8 leds, connector headers and the screen (XPT2046) as well as connect the joystick and srew it in place.

Miško 3 - soldering reset button
The final product should looks like this:
Miško 3 - front side
Miško 3 - back side

Maze

There are many different types of mazes1: standard, circular, block, number... For this project we'll implement a standard maze without entrance and exit where the goal is to find the shortest path from one position to another. For storing the maze we'll use an array where 1 will be a wall and 0 will be a path.
1 https://en.wikipedia.org/wiki/Maze
Standard maze with entrance and exit
Circular maze
Block maze

Generating

There are many different aproches for randomly generating mazes1 such as randomized depth-first search, Kruskal's algorithm, Prim's algorithm... The algorithm that we are going to implement is randomized depth-first search with iterative implementation. Before we implement everything from scratch we should search the web if there is allredy something similar allready made. I found just what we need on Figglewatts's github2 which we'll modifiy to suit our needs.

Since iterative implementation of random dept-first search is implemented using a stack, we have to implement it first before we even think of making algorithm. Stack is an abstract data type which serves as a collection of elements that has two main operations: push (adds element) and pop (removes last added element). These two operations are implemented as stack_push and stack_pop. Since there is no built-in implementation of array that automatically resizes in C, we use a pointer to allocated space, which we realocate to double size when it runs out of allocated capacity.
1 https://en.wikipedia.org/wiki/Maze_generation_algorithm

2 https://github.com/Figglewatts/mazegen
After implementation of stack, we can implement a interative random dept-first search. The maze that we're implementing has 2 different axis, x and y axis, therefore we need 2 stacks to store each axis without over complicating functionality of the stack. For implementation of the algorithm we run a while loop until one of the stack is empty. Which stack we check is not important since they are always the same size. To make the algorithm random we first shuffle the directions of posible moves in current position. If the planned direction is at the edge or it is allready a path (zero), nothing happens, otherwise a path is made (zero is written in two next cells on planned directions) and the position with that direction is pushed to a stack. Since the above algorithm is just for carving the maze, we also need a function to initialize all variables and allocate enough space for a maze of defined size and fill it with walls (ones). When the algorithm is done carving the maze, the generated maze is returned to the main program where we implement the logic for player movement and visualise it.

Visualising

For visualising the maze, we will use μGUI1, a free and open source library for embedded systems. The algorithm used for drawing the walls will draw white lines of defined size with UG_DrawLine. To optimize the algorithm, the bottom and right edge are drawn outside the double for loops. At the end we draw a target position, a green rectangle with UG_DrawFrame which only draws a border, on top right corner.
1 http://embeddedlightning.com/download/reference-guide/

Interactions

The program contains of two main interactive scenes, main menu and maze game, with a posibility to quickly add new types of game and controlls for it.

Main menu

Main menu is the first thing that you can see when you power-up the Miško. The main part of the main menu are top text, that says "MENU", and blue rectangles with white arrow on each side of the screen.
Main menu
To draw it we first use UG_FillScreen to make the whole screen black. Then we set a foreground and background color to draw the text with UG_PutString and use UG_FillFrame to draw blue rectangles on each side. Since there are multiple settings for maze game I implemented "pages" witch contains a rounded square border with some graphics, title and sequential page number on top left. If you want to add a new page, you simply change the amount of pages in predefined variable "pages" and add a new case in the switch statement. Bellow function is used for refreshing the contetn of the page, which means changing sequential number, title, graphics and border. If the page is not defined a default page with a blue border and title "SOON" will be displayed. We also need to implement menu controlls to switch between the pages. To increment the page we will use right button or move the joystick to the right and to decrement the page we will use left button or move the joystick to the left. Page selection we will implement by ok button or joystick press, which will also call a function to draw all the static content of the game page. joystickSensitivity is used for setting how far you need to move joystick before action is made. We can also implemented touch controls, where pressing the left blue rectangle will decrement the page, right blue rectengle will increment the page and anywhere else will select the page. The touch controlls are not the most accurate on this miško and pressing the touch screen will most likely select the page even if we accurately press the blue rectangles. This is most likely caused by either slightly damaged screen or poor soldering of the screen connector. To draw all the static (not changing) content of the games, we can implement a function that will only be called when the page is selected. Since we allready defined all the maze variables on pages, we can just group the three cases and use the same functionality for drawing the maze.

Maze game

Maze game contains a maze, move counter and a yellow circle which represent current player position.

We will implemented three different presets of the maze: easy, normal and hard; which is defined in MainMenuRefresh() shown above. Easy dificulty will of size 21x13 and have a cell size of 31, with a (5,43) offset and a player circle radius 7. Normal dificulty will of size 29x19 and have a cell size of 21, with a (12,40) offset and a player circle radius 5. Hard dificulty will of size 51x33 and have a cell size of 12, with a (10,40) offset and a player circle radius 2.

Easy dificulty
Normal dificulty
Hard dificulty
For controlls we will use up, down, left and right button or a joystick to move in given direction. Since in a maze you cannot freely move in all directions we also need to implement a logic which will check if in a given direction is a wall and move the player if there isn't one. When moving a player, we first need to clear the drawn circle in previous position and then draw it on a new position. When player gets to the green square, a new maze get's generated and player position is set back to left bottom square. When testing the game I noticed that my joystick has a constant offset of (-6,6) so I added 2 more variables to joystick.c and joystick.h which fixes this issue and gives more accurate joystick controlls.

Case

Since standalone Miško is not the most comfortable thing to hold in hands I aslo designed a cases for easier useage of it. The cases were designed in blender and you can find them herehttps://github.com/Klemen2/VIN-Projekt/tree/main/case.

Version 1

Version 1 of the case has a few flaws. Before printing the case needs to be scaled to match 3D printer tolerances, since it's made for a super tight fit and will need a lot of sanding if you decide not to do so. There are some other areas that needs sanding eaither way such as joystick hole, since it's too small and shifted, holes for the pins on the back, due to one of them being shifted, and the buttons guides, they are way too big. The front buttons are also too small for comfortable use, but it's much better than to use it without a case.

If the case doesn't stay toggether you'll also need to widen 3 holes on top and bottom side (the hole next to the reset button does not work well) and use appropiet screws, i'm using 10mm long screws Ø2 that I got from an old Lenovo laptop.

Case version 1 - front side
Case version 1 - back side

Version 2

In version 2 of the case I fixed issues that I had with the version 1 of the case, such as scaling, bigger hole for pins, bigger front buttons (the back reset button is still a small so you don't press it by accident). I also removed the screw hole next to the reset button since there just isn't enough space for it.

The only problem that I had was the same as before where the hole for joystick is too small, even though I made it bigger. I allready made it bigger in uploaded files, but haven't tested if it's big enough.

Case version 2 - front side
Case version 2 - back side