Simulating a Theme Park: Understanding queue times with R

Author:Murphy  |  View: 25314  |  Time: 2025-03-23 13:01:06
Photo by Thomas Kelley on Unsplash

Long lines are always off-putting, especially when you are waiting to soar through space or sail along the Great Barrier Reef. As the summer holidays continue, I'm sure nearly everyone will be queueing for something, and hopefully, you're lucky enough to be heading straight for the Magic Kingdom. Maybe you're in one of those queues while you read this blog!

Some code is included to support the examples, but the full code can be found on my GitHub, which is linked at the end of the article. The project uses R and the simmer package for Discrete Event Simulation. Please enjoy!


Concept Review – Discrete Event Simulation

So what will it take to simulate a theme park on my laptop? And will it look anything like Game Central Station from Wreck-it-Ralph?

I'm afraid not … the code written in R will use Discrete Event Simulation or DES, which really just shows what could happen in a process over time. The major use case of DES is to optimize processes which is why it is commonly used in operations research. Simulations allow decision-makers to view a typical process after many iterations and see how it could be improved. For example, would adding extra machines to a factory line reduce bottlenecks in producing a product?

This article will apply DES to a hypothetical Disney World. This version of the park will be a bit simpler and will have some extra assumptions to make the modelling easier.

As this blog is focussed more on the application, there will be lots of code examples over theory, but this concept review of the components involved should help get us up to speed.

Components

There are some basic components that are required for DES to work. Each one is something we will create using simmer but let's review them here before the coding begins:

  • Trajectory: A trajectory is a path that a guest will take through the simulation. For example, when a guest arrives, they may queue up for a ride, go on a ride and then leave. This would be their trajectory.
  • Resource: A resource is a thing that is used during the trajectory. In the example above, the ride would be a resource.
  • Generator: A generator is an item we use to generate the guests, and generators will typically generate many guests over the course of a simulation. The generator also needs a trajectory to know where generated guests should go.
  • Environment: The environment is where this whole simulation runs, in our case this is the Theme Park itself. It encapsulates all of our resources, generators and trajectories. With simmer there is an added advantage that the environment will also track and report our resource use, which makes the analysis of the simulation much simpler.

With that review completed, let's get hands-on! The simulations below build on one another and increase in complexity as they go on. For illustrative purposes, the code is not refactored until the final simulation which takes advantage of some extra R functions and features to clean up the simulation.

Simulation One: Meeting Lilo and Stitch

The first simulation will serve as an introduction. In this simulation, guests arrive and start to queue to see Lilo and Stitch. Before coding, it's helpful to review what the components will be:

  • Trajectory: Arrive at the park, queue, use character resource (Lilo or Stitch), release character resource and leave the park.
  • Resource: Character that guests can meet. Either Lilo or Stitch, therefore, capacity is set to two for this resource, meaning it can be seized by two guests at once.
  • Generator: Will generate guests periodically arriving at the park.
  • Environment: The park itself.

Now let's think about the code for each element. R allows us to take advantage of other packages like dplyr to organize the code we write. This makes trajectory building much simpler:

First, the guest seizes a character locking them from use by any other guest. Then they time out with that resource. Time out simply means they cannot do anything else. The length of time out in most simulations will be based on a real process. However, in this made-up scenario, the length of the timeout is sampled from a normal distribution. Finally, the guest releases the resource and it can once again be used by another guest in the queue.

The trajectory is the most complicated concept. The environment, resources and generator are all defined together in one block of code:

This set of components is again very basic. The important notes are that the resource has a capacity of two, as per the specification. The guests arrive according to an exponential function and are given the trajectory defined above. The arrival of guests would also typically be modeled after a real event but for the purpose of the example, the exponential will work just fine!

The last step is running the simulation and doing some analysis. Running the simulation is as simple as typing run and providing a number of timesteps to run for. In this case, the park will open for 15 hours. For analysis, we will use simmer.plot an extension of the simmer library that builds some simple visualizations for us to use:

The first plot below shows the resource utilization. For this simulation, our utilization sits at just 20%. This percentage represents the proportion of the simulation time that the resource was in use.

Image by author

The 20% utilization value is fairly low and ideally, we would have a higher utilization as this means more happy guests! The next visualization represents wait times, with no guests waiting for more than 5-time steps. This makes sense as our resource time out is based on a sample from a normal distribution around 5 steps. The average wait time, though, is much lower, represented by the blue line.

Image by author

Simulation Two: Meeting Lilo and Stitch with fast passes

Now let's add some complexity, starting slowly. The simulation will remain the same as above. However, we will add priority customers. These will mimic the ‘FastPass' system in Disney Parks. Really all we do is generate a new customer who has priority:

Note that this customer has a priority of one, as opposed to the default customer which defaults to 0. A higher priority value means that this customer will skip the queue and ride before all customers of lower priority. The other addition is that these customers arrive every 50-time steps. In reality, they would have pre-booked a pass for a certain evenly spaced out time.

Image by author

Note that resource utilization has increased drastically! This is because there is now a guaranteed customer arriving who will pre-empt all other customers at the fixed interval. Conversely, wait times have increased, which again follows logically as there are not only more guests but more FastPass guests which increases the wait time for the original group.

Simulation Three: Meeting Lilo and Stitch with reneging

Waiting in queues can be frustrating, and people often just give up. This can be accounted for mathematically through the idea of reneging. If a customer waits in a queue for a long time, we can model the chance that they will simply give up. This is done by adding to the trajectory:

The time it takes for a customer to renege is again sampled from a normal distribution, this time around the value of 2 (typically, this would be estimated and would be higher, for this example, we have very impatient customers to help understand reneging). Importantly we must abort the renege once the guest has got hold of a resource. Guests wouldn't leave as soon as they reached the end of the waiting period!

Image by author

This reneging has ultimately reduced the utilization of the resources as more guests are leaving hence not using the resource. However, it has also reduced the wait times as many guests simply give up, so their wait time is not included. An additional effect of guests leaving is that other guests experience a shorter queue which also reduces wait times. For the rest of the simulations, we will increase the time it takes for guests to renege.

Simulation Four: Riding Space Mountain with one cart using batching

Now let's get to the fun bit: the rides. The rides in the theme park will take advantage of the batching feature in simmer. Batching allows us to group guests together into a Space Mountain cart, for example:

The parameter n indicates how many guests can be batched into this group, so in this case, each cart can hold 6 guests. The timeout parameter indicates how long a batch should wait to be filled before moving on to the next part of the trajectory. So the batch can either reach its capacity of 6 or 10-time steps can pass before that batch seizes a cart resource.

Image by author

With the added complexity of this example and the need for guests to be batched, or at least to wait an appropriate time before leaving without a full batch, the utilization drops. These conditions also cause a problem for wait times which drastically vary compared to the character wait times. A guest could arrive and leave immediately or need to wait a whole 10-time steps for a new batch to leave. To address this problem in reality the size or number of carts would be increased.

Simulation Five: Space Mountain or Splash Mountain with branching

Let's make things even more realistic; no theme park would be any good with just one ride. By adding a new ride like Splash Mountain, we can model guest preferences and use branching trajectories to see which ride they will go on. This is a bit more complicated, and in the full script, one can see that the ride elements of the trajectory have been added to their own functions. For simplicity, a more concise version is shown here just to demonstrate the branching logic:

In the case above, the guests choose based on a coinflip so it is equally likely a guest will pick Space or Splash Mountain. This could be modelled from an actual preference in a more real application. Building the simulation changes a bit here too:

In this example, there are two distinct resources. This differs from the Lilo and Stitch examples above as Lilo and Stitch were both modelled as a "character" resource with capacity two. Here though, Splash Mountain is a separate resource compared to Space Mountain.

Image by author

The utilization for these two rides is very different, mostly down to the difference in ride times between the two trajectories. Riding Space Mountain takes longer and guests are more likely to renege. The added complexity has also created a more varied wait time, however from an optimization angle, we have successfully improved guest experience by reducing the average wait time in opening a new faster ride.

Simulation Six: Opening the park using schedules and queue preferences

The final simulation is bringing together everything we have created and adding a schedule to the park. Until now we have been defining resource capacity using fixed values. There is an alternative: using schedules. Schedules allow us to control the capacity of a resource based on time intervals.

The park schedule will be controlled by a gate that opens after 50-time steps and closes 50-time steps before the end of the simulation. The code below uses a lot of the refactored functions, which again are linked in the GitHub repository below. The gate schedule is the most important thing for review here:

This time our trajectory combines the rides and characters. There have been some other pieces of code added to make guests select characters they visit in a round-robin policy. There are also more guests being generated now that there is more to do.

Image by author

This simulation sees a more realistic utilization spread across all of the resources. There are more things for customers to do so each resource spends less time in use. There is also an added stipulation that customers must wait to enter the park. Note that the gate causes a lot of this reduction in utilization but actually sees little utilization itself because customers immediately seize and release it. The wait times also peak here with guests waiting for the park to open and then for rides in the park but this is a much more realistic simulation. The first group of guests is unleashed on the park to choose what they want to ride and who they want to meet, as soon as the gates open.

Conclusion and Final Thoughts

Discrete Event Simulation gives analysts and decision-makers a chance to explore business processes. In the examples above, the complexity has been able to stretch from a guest arriving, doing an activity and leaving, all the way up to accounting for preference and human behaviour like reneging.

Some of the decisions made through the process, like adding more rides, have shown how DES can be used to optimize processes. On the other hand, adding more difficult behaviour like reneging has shown how resources can be underutilized if they are not set up correctly.


I hope you enjoyed this article! If you did, please consider following my page for more Data Science content.

Resources

Code:

GitHub – josephlewisjgl/DESR: Repository holding code from the blog post Simulating a Theme Park…

Simmer Documentation:

6.1 The donut shop | Simulation and Modelling to Understand Change

Tags: AI Data Science R Simulation Statistics

Comment