Movie Renege
A ticket counter sells tickets for three films, and a film that sells out sends its queue away. This is SimPy's Movie Renege example, written in desflow.
Before You Start
SimPy's theatre has one ticket counter and three films with 50 tickets each. A moviegoer arrives about every 0.5 minutes (exponential gaps), picks a film and 1 to 6 tickets, and joins the queue only while the film has tickets. At the counter, a moviegoer who asks for more tickets than are left argues for 0.5 minutes and leaves. Otherwise the sale takes 1 minute. When fewer than 2 tickets are left, the film is sold out and everyone still queueing for it leaves (reneges). The run lasts 120 minutes. desflow reads each of SimPy's minutes as one second.
The example needs only desflow, which each Quick Start in Run It installs, and no other service. desflow examples get simpy-movie-renege --api yaml downloads it, as Get an Example shows. SimPy Examples says how these pages read SimPy's times and numbers. SimPy's own page for the example is on its documentation site.
Steps
Theater
SimPy's theater holds the counter, the tickets left for each film, the time each film sold out and the number of moviegoers who left each queue.
| docs/snippets/simpy/movie_renege.py:theater | |
|---|---|
available, when_sold_out and num_renegers are variables that hold one number for each film. counter is a resource with one unit. sold_out is a signal family with one member for each film, and once: true makes a member fire one time.
| examples/yaml/simpy/movie_renege.yaml:theater | |
|---|---|
add_resource, add_variable and add_signal declare the same parts.
| examples/builder/simpy/movie_renege_example.py:theater | |
|---|---|
The low-level version has SimPy's simpy.Resource for the counter, a dictionary for the tickets left and one env.event() for each film.
Moviegoers Arrive
SimPy's customer_arrivals starts a moviegoer after each exponential gap, with a random film and a number of tickets.
The arrival goers has exponential gaps at the rate 2 a second. Each moviegoer has two attributes: movie, one of the three films, and tickets, an integer from 1 to 6.
add_arrival takes the same attributes, written as generators.
customer_arrivals is SimPy's function. It draws the gap, the film and the number of tickets from desflow's Sampler.
| examples/low-level/simpy/movie_renege_example.py:moviegoers-arrive | |
|---|---|
Moviegoer Waits or Leaves
SimPy's moviegoer requests the counter. It waits for either its turn or the film to sell out. If the film sells out first, the moviegoer leaves.
balk stops a moviegoer from joining when the film has no tickets left. open places a request for the counter and calls it slot, and wait: any waits for the turn or for the sold_out signal of the moviegoer's film, whichever comes first. If the turn came first the moviegoer goes to serve. Otherwise it goes to leave, which cancels the request and counts a moviegoer who reneged.
The same two tasks, with open, wait and next as in the YAML.
moviegoer is SimPy's function, with turn | sold_out[movie]. This block shows its first part, up to the moviegoer who leaves.
Counter Sells or Argues
At the counter SimPy's moviegoer checks the tickets left. A moviegoer who asks for too many argues and leaves. Otherwise the tickets are bought, and the film is sold out when fewer than 2 are left.
serve goes to argue when the film has fewer tickets left than the moviegoer asks for. argue takes 0.5 seconds and releases the counter. Otherwise buy takes the tickets from available, and check goes to sold when fewer than 2 are left.
The tasks serve, argue, buy and check, with the same rules.
The middle of the same moviegoer function.
Film Sells Out
When a film is sold out, SimPy's moviegoer fires the film's event, which sends the queue away, and records the time. The sale takes 1 minute and the counter is released.
sold fires the sold_out signal of the film, which ends the wait of everyone queueing for it. It also records the time and sets the tickets left to 0, so later moviegoers balk. finish takes 1 second and releases the counter.
The tasks sold and finish, with the same rules.
The end of the same moviegoer function.
| examples/low-level/simpy/movie_renege_example.py:film-sells-out | |
|---|---|
Run It
desflow run loads the blueprint and runs it unpaced, because it sets factor: 0. Each record is printed to the terminal.
Quick Start
desflow examples get downloads the example at the release you install, into a folder of its own. Pick uv or pip:
The script builds the model with the builder API, runs it unpaced, and prints each record to the terminal.
Quick Start
desflow examples get downloads the example at the release you install, into a folder of its own. Pick uv or pip:
The script wires the model by hand on DynamicRealtimeEnvironment, runs it unpaced, and prints each record to the terminal.
Quick Start
desflow examples get downloads the example at the release you install, into a folder of its own. Pick uv or pip:
Check the Result
Kill Process sells out at 36.662 seconds, Python Unchained at 39.662 seconds and Pulp Implementation at 43.662 seconds. 17 moviegoers leave the queue for Python Unchained, 8 for Kill Process and 8 for Pulp Implementation. 43 sales go through, and 154 moviegoers find their film sold out and do not join a queue.
SimPy's output, below, has the films selling out at 38, 43 and 28 minutes, with 16, 5 and 5 moviegoers leaving. SimPy prints no count of moviegoers who find a film sold out. Here each one has a balked record. desflow draws each random value from its own stream, named by the place where the value is used, such as Movie.service.patience. A seed of 42 therefore gives different numbers from SimPy's random.seed(42). The system is the same. The figures are not.
The three versions in desflow print the same records, which a test checks by running each version unpaced. Each record has sim_ts, the simulation time, and a key for the entity it belongs to. The statistics and the parameters that a live run can change are in the same stream, which SimPy's file does not have.
Full Files
Full files
The blueprint is movie_renege.yaml.
| examples/yaml/simpy/movie_renege.yaml | |
|---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | |
The script builds the model with the builder API.
| examples/builder/simpy/movie_renege_example.py | |
|---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 | |
The script wires the model by hand on DynamicRealtimeEnvironment, with SimPy's own processes.
| examples/low-level/simpy/movie_renege_example.py | |
|---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 | |
This is SimPy 4.1.2's movie_renege.py, with SimPy's licence notice added at the top and comment lines that mark the pieces shown above. Run with Python, it prints the output shown under Check the Result.
| docs/snippets/simpy/movie_renege.py | |
|---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | |
Sources
- SimPy 4.1.2, Movie Renege: the original code and its output.
- SimPy's licence: MIT, Copyright (c) 2013 Ontje Lünsdorf and Stefan Scherfke. The SimPy file above carries this notice.
examples/in the desflow repository: the three versions in desflow, undersimpy/in each folder.