A web game for logistics students. Four levels of questions are played with lives and a time limit, complete with a leaderboard and an admin panel for lecturers.
Logistics topics such as transport mode selection, Vehicle Running Cost, the Gravity Location Model, and distribution routing are usually taught through calculation exercises. Students work on paper, hand it in, and get it marked. Politeknik Negeri Batam wanted the same material delivered another way: played, with a time limit, and with consequences for getting it wrong.
The first version of this game was written in plain PHP. The trouble was that adding a new level meant copying a lot of code, and lecturers had no way to monitor or control a session other than opening the database. I rebuilt the whole application in Laravel and added the admin side that had not existed before.
Players sign in with their student number and land on a career map, an illustrated road with four level pins. The next pin unlocks once the previous level has been played. Each level has a briefing screen with the scenario, then moves into questions with a set number of lives and a running timer. A wrong answer costs a life; run out of lives and the run ends with whatever score you have. Every run is saved as a score row, so lecturers see the full history rather than just a final mark.
On the lecturer's side there is an admin panel: completion statistics per level, a live standings monitor during a competition, a score table per student, account management, and two important switches, the game mode and the site lock.
What took the most time was not the gameplay but making sure scores could not be gamed. Every answer key lives as a protected property on the Livewire component, so it never travels to the browser. The front end only sends the player's choice to methods such as attemptMatch or checkIteration, and the server decides right or wrong, deducts a life, and computes the score. Answers are only revealed after the player wins, through a separate property for the explanation table.
The second problem came from Champions mode. If a level can only be played once, a player who knows they are losing could press F5 or close the tab so the score is never saved. I solved it by marking the play session on the server: when a level starts, the application stores a marker with the start time and a partial score that updates on every correct answer. If the page is opened again while that marker still exists, the partial score is finalised immediately and the level is locked. In the browser, F5 is intercepted by a confirmation modal, and switching tabs during a question costs a life through the Page Visibility API.
Level 4 was the longest piece of work. The map is built with Three.js: terrain, a road network, outlet buildings, and three trucks that genuinely drive from node to node. To make the trucks follow the roads instead of flying straight, I wrote the road graph with my own A* in level4-pathfinding.js. Route order validation is still done on the server with a strict check: the outlet clicked must be exactly the next outlet on the correct route, plus a vehicle capacity check.
One small obstacle that briefly broke the application was bundling Livewire and Alpine manually in a single Vite entry. As soon as I tried splitting each level into its own entry, Livewire and Alpine were loaded twice and Alpine threw a redefine property error. Since then every level is registered from one app.js. For the Excel backup in the score reset feature, I chose to write the .xlsx file myself with PHP's built-in ZipArchive rather than add a new dependency for a single seven-column table.
I did every part myself: migrating from the plain PHP version, the database schema, all four gameplay modes and their scoring, the 3D map, the admin panel, and preparing the deployment to hosting.
The application is deployed on hosting with its own domain, and player data from the earlier version was moved across through an import seeder. Champions mode is used during competition sessions, Casual mode for ordinary practice.
What is not finished: the three bonus games on the career map are still Coming Soon cards, and their bonus badges are waiting on those games. The plan after this is to add those bonus games and move each level's question figures into the database, so lecturers can change the scenarios without touching code.