Skip to content
ProjectsDigital & Software Systems

An investment game built in my own time

Kotlin and Android Studio, written for the practice rather than for a course

Image needed

/images/investors-game/hero.webp

The investment game's start screen: a stylised map showing farmland plots, a bank building and a skyscraper, with Continue, Start New Game and Save buttons overlaid

hero.webp
The title screen, with farmland acres on the left and the bank and residential assets visible on the map.
Context
Personal project
Year
2020
Role
Sole author
Team size
1
Tools
Kotlin · Android Studio · Threading · SharedPreferences · ConstraintLayout · Google AdMob · Wireframes and flowcharts
Contribution
Sole author, built in my own time outside coursework

A mobile game where the player buys and sells four assets whose prices move under changing market conditions, plus farmland that pays out on a harvest cycle. I built it in my own time in order to practise programming, and it runs a threaded game loop, saves locally, and simulates the time that passed while the app was closed.

Why I built it

This was not a hand-in and nobody set it, since I wanted to actually practise writing code rather than only reading about it, and a game seemed like the most useful thing to build due to it needing several things to work at the same time, meaning state, timing, persistence and an interface. Here I started from what I had learned in Interaction Design, drawing flowcharts and wireframes of the screens before writing anything, which is the habit I have kept from that course more than any specific method.

How it works

The player starts with 500 and can buy into four assets, being telecom, residential, bank and store, each with its own starting value and its own dividend paid on a yearly cycle, alongside farmland that is bought by the acre at an increasing price and pays a random harvest each round. A background thread runs the game loop once a second, recalculating every price, updating the interface, and flashing each value field green or red for half a second so that the player can see which way the market moved without reading the numbers. The prices do not simply drift randomly. A market condition is drawn every ten ticks and each asset responds differently to it, so telecom is volatile while the bank is comparatively stable, and each asset also has a dampening function that reduces its growth rate as its value climbs, which stops the whole thing running away exponentially within a few minutes. Furthermore there is a low-probability event that gives telecom a much larger swing, in order to make the market occasionally do something the player cannot plan for.

The part I am most pleased with

The game continues while it is closed. On saving it records the actual calendar year, day, hour and minute, and when the player returns it works out how much real time has passed, converts that into the number of ticks that should have happened, and then replays them on a background thread before showing the result. This means that coming back after two days shows a market that moved for two days rather than one frozen where it was left, and it was the first time I had to think about a program's behaviour when it is not running. Alongside this the state is persisted with SharedPreferences on every interaction rather than only when the player presses save, since the save button existing at all seemed likely to make people assume their progress was lost if they forgot it.

Small things from Interaction Design that ended up in it

The New Game button asks "Are you sure?" and changes colour before it will actually wipe a save, which is a two-step confirmation on the only destructive action in the game. Furthermore the shop sells one item that does nothing at all and says so in its own description, which is there because a purchase that is honestly useless is more entertaining than one that pretends not to be.

What I would do differently

The game state lives in local variables and is persisted by reading values back out of the text fields on screen, which means the interface is effectively the source of truth rather than the model behind it. This works at this size, however it is the thing I would change first, since separating the state from the view is what would make the logic testable and would stop a display change from being able to break the game. Furthermore the balancing numbers are written directly into the code rather than being collected in one place, which made every adjustment slower than it needed to be.