CS303E Project 3: Build a Simulator for a Market

Instructor: Dr. Bill Young
Due Date: Wednesday, December 4, 2024 at 11:59pm

It is common, particularly in scientific computing, to simulate the behavior of a physical system. You make certain assumptions about the environment in which the system operates and then run the simulation to predict how the system would behave under different scenarios. Many simulations, including the one constructed in this assignment, are "event driven," which means that the input to the system is a list of "events." The event list serves both to provide input data and also as the simulator "clock." That is, if the event list contains 20 items the simulator runs for 20 clock "ticks." To tick the simulator clock really just means to read an event from the list and update the state of the simulation accordingly. There's no expectation that each tick of the simulator clock corresponds to any specific period of time or that events happen at regular intervals.

The system simulated may be as complex as the earth's climate system or the evolution of the solar system. Or it might be quite simple, like the one you're building. I personally worked with a team building a simulator for the U.S. Army to model the performance of Army medical units in battlefield scenarios. That simulator is still widely used during Army "war games." If you'd care to read about that, here's a link: S-MDS paper.

Simulating a Market

In this project, you will simulate a very simple neighborhood grocery store. There is only one checkout line. An event in this system might be the checker scanning a customer's item or a new customer joining the checkout line (or both). You will track how the line changes as customers are added at the end of the line and move forward through the line.

During each tick of the simulator clock, the cashier scans one item from the basket of the customer at the head of the line. When that customer's basket is empty, they exit the line. On any tick, a new customer may also be added at the end of the line. To be realistic, most simulations, including ours, involve inputs arriving randomly but under some specified probability distribution; customers arrive at random intervals and purchase random numbers of items.

You can see that such a simulation might be useful if, for example, you were considering opening a new market in your neighborhood. If customers arrive too frequently or buy too many items, the line would grow longer and longer. If customers arrive infrequently or don't buy many items, the checker might be idle most of the time. Different assumptions might lead to different decisions: should the market have more checkout counters, should there be self-checkout, is there sufficient customer demand to make it profitable, etc.

The similation is driven by an "event list." Each item on the event list for our market simulator is a non-negative integer. Reading an item from the list is interpreted as ticking the simulator clock. At each tick, the line advances. That means to remove one item from the basket of the customer at the head of the line (unless the line is empty) and, if their basket becomes empty, they exit the line. But something else potentially happens on a tick: if the current event is non-zero, a new customer is added at the rear of the checkout line with that many items in their basket. For example, if the event item is 5, a new customer is created with a basket containing 5 items and the customer joins the checkout line. Number customers consecutively as you create them. At the end of each tick, you should also print out the current line. See the sample output below.

Finally, to make it easier for the TAs to grade your program, the event list will be supplied in a file, one event per line. (This is also to give you practice in creating and reading files.) You will process the file to create the event list. That is, create the event list all at once prior to running the simulation, by reading successive items from the file and storing them in a list. You will write one function to generate the file and another function to read items from the file and store them on a list. The function to create the file is parameterized so that you can control the length of the list, the frequency with which new customers arrive and how many items they can purchase. See the link for a template below.

Details of the Implementation

This file contains a template for the code you need to construct: Project.py (Partial). Your task is to complete the functions and classes in this file; call it Project.py. Code you need to supply is indicated with pass; replace pass with your code.

Sample Behavior:

Below is some sample behavior calling the functions from Project3.py. We first show using the classes Customer and CheckoutLine, and then show the driver in use.

The common pattern for using the driver (simulator) is to use generateEventsFile to write a file of events to drive the simulator. The parameters control: how many events, what percentage of the events are non-zero, the maximum size of any event (i.e., max number of items in a basket), and the filename. Once you have generated this file, you can extract the events from it into list form using the function generateEventListFromFile. Printing this list shows you the event list. Finally, pass this list to the simulateCheckoutLine function to simulate the behavior of our system for this sequence of events.

>>> from Project3 import *
>>> c1 = Customer(15, 5)                    # testing Customer
>>> print(c1)
C15(5)
>>> c1.getCustomerNumber()
15
>>> c1.getItemsCount()
5
>>> c1.decrementItemsCount()
>>> print(c1)
C15(4)
>>> c1.customerFinished()
False
>>> c2 = Customer(83, 1)
>>> c2.customerFinished()
False
>>> c2.decrementItemsCount()
>>> print(c2)
C83(0)
>>> c2.customerFinished()
True
>>> line = CheckoutLine()                  # testing CheckoutLine
>>> line.customerJoinsLine( c1 )           # customer joins
Customer C15 joining line.
>>> print(line)                            # printing line
   Line: [ C15(4) ]
>>> line.customerLeavesLine()              # customer leaves
Customer C15 leaving line.
>>> print(line)
   Line: [ ]
>>> c3 = Customer(1, 4)
>>> c4 = Customer(2, 2)
>>> line.customerJoinsLine( c3 )
Customer C1 joining line.
>>> line.customerJoinsLine( c4 )
Customer C2 joining line.
>>> print(line)
   Line: [ C2(2) C1(4) ]
>>> line.advanceLine()                     # line advances
>>> print(line)
   Line: [ C2(2) C1(3) ]
>>> line.advanceLine()
>>> print(line)
   Line: [ C2(2) C1(2) ]
>>> line.advanceLine()
>>> print(line)
   Line: [ C2(2) C1(1) ]
>>> line.advanceLine()
Customer C1 leaving line.
>>> print(line)
   Line: [ C2(2) ]
>>> len( line )                            # how long is the line
1
>>> generateEventsFile( 10, 25, 6, "EventsFile" )        # generate file of events
>>> events = generateEventListFromFile( "EventsFile" )   # create list from file
>>> events          
[0, 0, 0, 0, 0, 4, 0, 0, 1, 0]
>>> generateEventsFile( 10, 45, 6, "EventsFile" )        # re-generate the file
>>> events = generateEventListFromFile( "EventsFile" )   # and list of events
>>> events
[3, 4, 1, 3, 0, 2, 1, 0, 6, 0]
>>> simulateCheckoutLine( events )                       # drive simulation from
Simulating a simple market, with one cashier.            # list of events

Step: 1
Customer C1 joining line.
   Line: [ C1(3) ]

Step: 2
Customer C2 joining line.
   Line: [ C2(4) C1(2) ]

Step: 3
Customer C3 joining line.
   Line: [ C3(1) C2(4) C1(1) ]

Step: 4
Customer C1 leaving line.
Customer C4 joining line.
   Line: [ C4(3) C3(1) C2(4) ]

Step: 5
   Line: [ C4(3) C3(1) C2(3) ]

Step: 6
Customer C5 joining line.
   Line: [ C5(2) C4(3) C3(1) C2(2) ]

Step: 7
Customer C6 joining line.
   Line: [ C6(1) C5(2) C4(3) C3(1) C2(1) ]

Step: 8
Customer C2 leaving line.
   Line: [ C6(1) C5(2) C4(3) C3(1) ]

Step: 9
Customer C3 leaving line.
Customer C7 joining line.
   Line: [ C7(6) C6(1) C5(2) C4(3) ]

Step: 10
   Line: [ C7(6) C6(1) C5(2) C4(2) ]
>>> 

Turning in the Assignment:

The program should be in a file named Project3.py. Submit the file via Canvas before the deadline shown at the top of this page. Submit it to the assignment project3 under the assignments sections by uploading your Python file.

Your file must compile and run before submission. It must also contain a header with the following format:

# File: Project3.py
# Student: 
# UT EID:
# Course Name: CS303E
# 
# Date Created:
# Description of Program: 

Programming Tips:

This project is similar to one I assigned when I taught CS313E some years ago. That assignment was a bit more complicated, but not that much. For example, I assumed that it was a supermarket instead of a simple neighborhood market. There were initially k checkout lines. A new customer always joined the shortest line. (Shortest line could mean the one with the fewest customers or the line where number of items in baskets was minimum.) This more closely models customer behavior in a real supermarket. Also, if the average length of all lines exceeded a certain threshold n, then a new checkout line was added.

After you're finished with the base assignment, you are invited to play with such extensions. Personal projects like that are a great way to increase your programming competence. But make sure that any extensions aren't included in your submission.

Program incrementally: This is another classic example where programming the whole project before testing any part would be crazy. Some possible steps include:

  1. Write and test the function to generate the events file. Make sure that it is generating the correct number of lines, that each line is a non-negative integer less than or equal to maxItems, and that it appears that the percentage of non-zero lines is as expected.
  2. Write and test the function to read lines from the events file into a list. Make sure that it's reading all lines.
  3. Write and test the Customer class.
  4. Write and test the CheckoutLine class.
  5. Write and test the simulateCheckoutLine function possibly using an events list you generate by hand.
  6. (Optionally) Make sure it all works together, possibly by writing a main() function that: accepts a filename from the user, generates a file of events with that name, generates an event list from the file, simulates the market using that event list. Be sure to comment out or remove this part.

Here's a possible main() function to test everything:

def main():
    # Accept a filename from the user.
    filename = input("Enter a filename: ").strip()

    # Populate the file with 10 events (integers).  Approximately
    # 50 percent are non-zero.  Each is between [1..7] (items).
    # (Play with those parameters.)
    generateEventsFile( 10, 50, 7, filename )
    
    # From the file, generate a list of events (integers)
    eventlist = generateEventListFromFile( filename )
    print("The eventlist:", eventlist)

    # Use the event list to simulate the market behavior.
    simulateCheckoutLine( eventlist )

# Be sure to comment this out before submitting.
main()