Created
March 13, 2022 18:31
-
-
Save jscalo/3a9315f1169fe6f2b0dba086ffa89a4b to your computer and use it in GitHub Desktop.
Simulation that shows how often a public toilet lid needs to be touched based on different "policies".
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
enum Gender { | |
case female, male | |
static func random() -> Gender { | |
return Int.random(in: 0...1) == 0 ? .female : .male | |
} | |
} | |
enum LidState { | |
case up, down | |
} | |
enum LidPolicy: String { | |
case leaveAfter | |
case returnToDown | |
func description() -> String { | |
switch self { | |
case .leaveAfter: return "Leave the lid as is when finished." | |
case .returnToDown: return "Men close the lid if necessary when finished." | |
} | |
} | |
} | |
struct Person { | |
let gender: Gender | |
let isPooping: Bool | |
static func random() -> Person { | |
return Person(gender: Gender.random(), | |
isPooping: Int.random(in: 0..<20) == 0) // about 1 in 20? | |
} | |
func desiredLidState() -> LidState { | |
if isPooping { | |
return .down | |
} else { | |
return gender == .female ? .down : .up | |
} | |
} | |
func beginBusiness(context: inout SimulationContext) { | |
if desiredLidState() == .up { | |
context.openLidIfNecessary() | |
} else if desiredLidState() == .down { | |
context.closeLidIfNecessary() | |
} | |
} | |
func endBusiess(context: inout SimulationContext) { | |
if gender == .female { | |
// Nothing to do here. | |
} else /* male */ { | |
if context.lidPolicy == .returnToDown { | |
context.closeLidIfNecessary() | |
} | |
} | |
} | |
} | |
struct SimulationContext { | |
let lidPolicy: LidPolicy | |
var lidState = LidState.down | |
var lidTouches = 0 | |
let iterations: Int | |
mutating func openLidIfNecessary() { | |
if lidState == .down { | |
lidState = .up | |
lidTouches += 1 | |
} | |
} | |
mutating func closeLidIfNecessary() { | |
if lidState == .up { | |
lidState = .down | |
lidTouches += 1 | |
} | |
} | |
func printResult() { | |
print("Policy: \(lidPolicy.description())") | |
print("Iterations: \(iterations)") | |
print("Lid touches: \(lidTouches)") | |
let ratio = Double(lidTouches)/Double(iterations) | |
print("Lid touch %: \(ratio.percentageStr)%") | |
print("\n") | |
} | |
} | |
func simulateWithPolicy(_ lidPolicy: LidPolicy, iterations: Int) { | |
var context = SimulationContext(lidPolicy: lidPolicy, iterations: iterations) | |
for _ in 0..<iterations { | |
let nextPerson = Person.random() | |
nextPerson.beginBusiness(context: &context) | |
nextPerson.endBusiess(context: &context) | |
} | |
context.printResult() | |
} | |
extension Double { | |
var percentageStr: String { | |
let perc = self * 100.0 | |
return String(format: "%0.1f", perc) | |
} | |
} | |
// Main | |
simulateWithPolicy(.leaveAfter, iterations: 1000000) | |
simulateWithPolicy(.returnToDown, iterations: 1000000) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment