January 28, 2022

The Simulation Pattern

edit

Disclaimer: If this pattern has an established name, let me know. I’ll update the article to reflect that.

In December, I participated in Advent of Code. It was mostly fun, but I did have some extremely difficult days. If you follow me on Twitter, you know of my travails with day 19. Oof.

That said, there was a common pattern I used to solve several of the puzzles that I want to share with you. I call it the “simulation pattern” and if I hadn’t tried to learn the tiniest amount of game dev a few years back, I may have never learned it. Thus, I want to pass it on to you in the hopes you find it useful.

What is a “simulation”?

A “simulation”, in my own words, is where we create some conditions and store some state, and then add a tick method to advance that state to the next discrete increment. Conway’s Game of Life is an excellent example of a simulation and I encourage you to take this pattern and try to build Conway’s Game of Life with it when you’re done reading this post.

The pieces of the pattern

In its most basic form, a simulation is a closure which exposes a tick method to advance to the next state. Using a factory function, it looks like this:

function createSimulation(initialState) {
  let state = initialState

  return {
    tick() {
      // Add functionality to update state
    },
    getState() {
      return state
    },
  }
}

const sim = createSimulation()
sim.tick()
sim.getState()

We create a function that holds our state in closure. We add functionality in our exposed tick method to advance the state, and we create a way to access the current state with getState. All simulations are some variation of this pattern.

Let’s create a simple example to demonstrate this. All of us have to deal with money in some capacity, so a loan payoff calculator can be a useful tool to have. We can create a simulation that takes in a principal, an interestRate, and expose a tick method that receives a payment to determine how many payments are necessary to pay off a loan.

First, let’s create our factory function:

function createLoanPayoffSimulation(principal, interestRate) {
  let total = principal

  return {
    tick(payment) {
      const interest = total * interestRate

      if (payment <= interest) {
        throw new Error('Impossible to pay off loan. Increase payment amount.')
      }

      const diff = interest - payment
      total += diff

      return this
    },
    getTotal() {
      return total
    },
  }
}

// Dividing by 12 gives us a monthly interest rate
const sim = createLoanPayoffSimulation(10000, 0.05 / 12)

Now that we have our simulation, we can create a function that uses a simulation to determine how many payments are needed to payoff the loan.

function getTotalPayments(payoffSimulation, payment) {
  let payments = 0
  let paid = false

  while (!paid) {
    payoffSimulation.tick(payment)
    payments++

    const total = payoffSimulation.getTotal()
    paid = total <= 0
  }

  return payments
}

console.log(getTotalPayments(sim, 500)) // 21

We can pass different simulations with different conditions into getTotalPayments to solve other loan payoff scenarios.

Now that you’ve seen the basics, give it a try on some other problems. Or try it on an Advent of Code problem, like this one.

Related

You can also use generator functions to create a similar effect, just with next as the method instead of tick. The difference being that you yield the next state with each run of the function. I make a loan payment calculator in that post as well, so you can hopefully compare the two.


Liked the post?
Give the author a dopamine boost with a few "beard strokes". Click the beard up to 50 times to show your appreciation.
Need help with your software problems?

My team and I are ready to help you. Hire Agathist to build your next great project or to improve one of your existing ones.

Get in touch
Kyle Shevlin's face, which is mostly a beard with eyes

Kyle Shevlin is the founder & lead software engineer of Agathist, a software development firm with a mission to build good software with good people.

Agathist
Good software by good people.
Visit https://agath.ist to learn more
Sign up for my newsletter
Let's chat some more about TypeScript, React, and frontend web development. Unsubscribe at any time.
Logo for Introduction to State Machines and XState
Introduction to State Machines and XState
Check out my courses!
If you enjoy my posts, you might enjoy my courses, too. Click the button to view the course or go to Courses for more information.