My first taste of greedy programming
Diamonds was the first group assignment in our Algorithm Strategies course. We were given a competitive grid game we had never played before and asked to design a bot using a greedy algorithm.
That constraint made this my first real taste of greedy programming. The bot could not plan an entire match perfectly. It had to look at the board now, assign value to the available choices, and commit to the move that appeared best in that moment.
Our team name was SarikatJava. I am Javanese, and both of my teammates really liked Java, so the name became an easy joke that stayed with the project.
Each of us explored a different strategy. I developed the density-based approach that we eventually submitted, while we tested the alternatives against one another to see which behavior held up inside actual matches.
Treating distance as opportunity cost
I thought about every move as carrying an opportunity cost. Walking toward one diamond meant giving up the chance to spend those same steps reaching another one. Looking only at points would send the bot toward valuable but distant targets, while looking only at distance could ignore a better reward nearby.
The greedy function combined both ideas by measuring points per Manhattan step. Because diamond values could differ, the ratio gave the bot a simple way to ask which destination offered the most potential return for the movement it required.
We compared our strategies through repeated games, and the density heuristic performed best. I think its simplicity was part of why it won. It recomputed a clear local priority on every turn without carrying a complicated plan that could quickly become outdated.
The final bot added focused rules discovered through testing. It learned when to bank its inventory, avoided wasting the final slot on the wrong diamond value, and used an experimental time buffer to decide when collecting had to stop.
The portal, and why fast does not mean best
Teleporters seemed intuitive because their positions never changed. We could compare the direct route with a route through the fixed portal pair and choose whichever looked cheaper.
Then one test exposed a wonderfully foolish behavior: the bot entered a portal, emerged, and decided that entering the portal again was still its best move toward the same diamond. It became trapped in its own greedy reasoning. Watching that loop made the weakness visible immediately, and we adjusted the heuristic after finding the case.
We considered opponent awareness, but after discussing it with the course assistant, we were concerned that a more involved opponent model might move beyond the greedy strategy required by the assignment. Some of that behavior was also difficult to express as one consistent greedy function.
We kept the submitted bot focused on collection, distance, teleporters, inventory, and time. That boundary was not proof that opponents were unimportant. It was a design decision shaped by what the assignment was intended to teach.
I remember our bot finishing around the top ten in the course evaluation. More important than the ranking was seeing a small formula create behavior that could compete, fail, and improve through experiments.
The project taught me that greedy algorithms are fast and practical, but they are definitely not always best. If I rebuilt the bot today, I would add opponent awareness and explore strategies beyond greedy selection. The original constraint taught me how much a local choice can accomplish, while its failures showed exactly where local reasoning ends.
What stayed with me
- Every move carries an opportunity cost.
- Simple heuristics can outperform elaborate ones.
- Testing reveals what a greedy function cannot see.
- Greedy is fast, but it is definitely not always best.
Run it locally
Clone the source repository or download the project ZIP, then run these commands from the project folder.
Explore the complete project
Download the source, assets, and included documentation as a ZIP archive.
1. Game engine (separate repository)
# github.com/haziqam/tubes1-IF2211-game-engine, release v1.1.0
$ cd tubes1-IF2211-game-engine-1.1.0
$ ./scripts/copy-env.sh
$ docker compose up -d database
$ ./scripts/setup-db-prisma.sh
$ yarn install
$ yarn tsc --build packages/backend packages/frontend
$ yarn start
# API on :3000, match viewer on :8082
2. The bot
$ cd src
$ pip install -r requirements.txt
$ python main.py --logic SarikatJava --email=sj1@test.com --name=SJ-1 --password=123456 --team etimo
# bot names are capped at 10 characters — longer ones are rejected
# with a bare 'Bad Request Exception'. Start four for a full match.



