In the next lesson I’m going to start writing a little command-line game, but before I get into that I want to discuss the general concept of handling “state” in software applications.
Every non-trivial application maintains some sort of state. For instance, the state of a word processing application is the current document, along with whether the document has been saved or not (whether the document state is “clean” or “dirty”). Similarly, the state of a spreadsheet application is the spreadsheet and its clean/dirty state. Web versions of these applications have additional state, such as who the current user is, when they logged in, what their IP address is, etc.
Even voice applications like Siri and Amazon Echo have state. As I learned in writing SARAH, one thing you need to do is to maintain speaking/listening state, otherwise the computer will hear itself talking, then respond to itself, eventually kicking off an endless loop.
Siri and others are also gaining a concept that I call context, or the “context of a conversation,” which also requires state management. Imagine asking Siri to order a pizza. It will respond by asking what toppings you want, where you want to order the pizza from, how you want to pay, etc. This is “conversational state.”
This post is sponsored by my new book,
Learn Functional Programming Without Fear.
Handling state in a game
In my spare time I work on developing an Android football game where I play against a computer opponent. If you know American Football (as opposed to what we Americans call “soccer”), in between each play you can think of the state of a football game as having these attributes:
- Which team has the ball (you are on offense or defense)
- Current field position
- Down and distance (such as “1st and 10”)
- Current score
- Time remaining
There are more state variables than this, but I’m keeping this example simple.
In Scala you might model this game state like this:
case class GameState (
iHaveTheBall: Boolean,
fieldPosition: Int,
down: Int,
distance: Int,
myScore: Int,
computerScore: Int,
timeRemaining: Int
)
On the first play of the game the initial state might look like this:
GameState (
iHaveTheBall: true,
fieldPosition: 25,
down: 1,
distance: 10,
myScore: 0,
computerScore: 0,
timeRemaining: 3600
)
Then, after the next play the state might look like this:
GameState (
iHaveTheBall: true,
fieldPosition: 29,
down: 2,
distance: 6,
myScore: 0,
computerScore: 0,
timeRemaining: 3536
)
A football game typically has about 150 plays, so in my game there is a GameState
instance for each of those plays.
Why state is important
State is important for many reasons, not the least of which is to know when the game is over and who won. An important part about state in my football game is that I use it to help the computer make decisions about what plays it calls.
When the computer is playing on offense is uses a function that looks like this:
val possiblePlays: List[OffensivePlay] =
OffensiveCoordinator.determinePossiblePlays(gameState)
The determinePossiblePlays
function is a pure function. I pass GameState
into it, and with thousands of lines of purely functional code behind it, it returns a list of all the possible plays that the algorithms believe make sense for the state that was passed in.
For instance, if it’s fourth down and goal at the opponent’s one-yard line with five seconds left in the game and the computer is down 21-17, it’s smart enough to know that it needs to try to score a touchdown rather than kick a field goal. This is what I mean by “state” in the context of a football game.
As the game gets smarter I also maintain a history of all previously-called plays, so the computer can adjust its play calls based on the player’s tendencies.
More state
As you can imagine, a point of sales application for a pizza store will have state that includes:
- The number and types of pizzas ordered
- Customer contact information
- Customer payment information
- The date and time of the order
- Who took the order
- More ...
Once you begin to think about it this way, you’ll see that every application maintains state of some sort.
State and functional programming
As I mentioned, my football game has about 150 GameState
instances for every game. In the context of functional programming, this raises an interesting question: In Scala/FP I can only have val
instances, so how can I possibly create 150 new variables for each game? Put another way, if you assume that I keep all of the plays in a List
, the question becomes, “How do I append GameState
values to an immutable List
?”
Questions like this bring you to a key point I got to when I was learning FP:
- How am I supposed to handle I/O, which by its very nature is impure?
- How am I supposed to handle state?
In the next lesson I’ll show one way to handle state in a simple game by building on what you just learned in the previous lessons: recursion.