Mods
Before this: Reacting to the Game
Next steps: Chips, Tuples, Option and Result, Ref Parameters, Every Player, Testing
Parts that use this: Combination Lock, Checkpoints, Race Timer, Shop, Voting, Obstacle Course, Whack-a-Light, Card Table
A mod gives a piece of circuit a name, so you can write it once and use it
in many places. Every place that uses it gets its own copy of the gates.
Contents
zone1 Occupied -> chip busy1
zone2 Occupied -> chip busy2
chip sign1 -> decal1 Text
chip sign2 -> decal2 Text
Code
@left in busy1: bool
@left in busy2: bool
mod status(name: string, busy: bool) -> string {
return if busy then "${name}: busy" else "${name}: free"
}
@right out sign1 = status("Workshop", busy1)
@right out sign2 = status("Storage", busy2)
mod status(name: string, busy: bool)declares a mod with two inputs, like the pins of a small chip.-> stringsays it gives back one value, a piece of text.returnnames that value. Here it is oneif ... then ... elseexpression:if busy then A else BisAwhilebusyis true andBotherwise. Choices can chain:if a then X else if b then Y else Z.status("Workshop", busy1)is a call. It runs the mod’s body withnameset to"Workshop"andbusyset tobusy1.
The mod is written once and called twice. How many gates does the chip hold?
Which of them belongs to status?
Run
- Compile the chip and paste it in-game. Build two small rooms. Give a brick in each an Attached Zone that fills the room, and place a Text Decal by each door.
- Wire the first zone’s Occupied to
busy1and the second’s tobusy2. - Wire
sign1andsign2to the two Text Decals’ Text. - Walk into the workshop and its sign says
Workshop: busy. Walk out and it saysWorkshop: free. The storage sign does not change.
Under the Hood
Open the chip. There are two Select gates and nothing named status.
Each call became its own Select. Click one. Its two text inputs are typed
in, Workshop: free and Workshop: busy. The other Select holds the storage
texts. The name was a constant, so the compiler worked out both texts ahead
of time and typed them into that call’s copy.
The compiler pastes a mod’s body in at each call and fills in the inputs.
One call is one Select, and ten calls would be ten. In a code editor, hover over status to see “inlined per
call”, the compiler’s reminder of this.
A mod can also choose its value step by step, with if statements and an
early return:
@left in busy1: bool
mod status(name: string, busy: bool) -> string {
if busy { return "${name}: busy" }
return "${name}: free"
}
@right out sign1 = status("Workshop", busy1)
Error: [WS007] `status` returns from inside a branch, so choosing between its returns needs an exec context: call it from a handler or a `mod` body, or pass `exec = ...`
if statements run one step after another, so they need a pulse to run on.
An out pin has none. Write the choice as one expression, as in the first
version, or call the mod inside an on handler, where there is a pulse.
Modify
-
Add a third room, an office. Add
@left in busy3: booland@right out sign3 = status("Office", busy3). Open the chip again and there is a third Select. -
Give
statustwo outputs by naming them. A light by each door shows whether the room is free:@left in busy1: bool @left in busy2: bool mod status(name: string, busy: bool) -> (text: string, lit: bool) { out text = if busy then "${name}: busy" else "${name}: free" out lit = !busy } let workshop = status("Workshop", busy1) let storage = status("Storage", busy2) @right out sign1 = workshop.text @right out sign2 = storage.text @right out free1 = workshop.lit @right out free2 = storage.litWith named outputs, a call gives back a record, and
workshop.textandworkshop.litread its two values. Wirefree1andfree2to two Point Lights’ Enabled. -
Count how many times each room has been used. A mod can change a variable it is given, if the input is marked with
*:@left in busy1: bool @left in busy2: bool var visits1: int = 0 var visits2: int = 0 mod status(name: string, busy: bool) -> string { return if busy then "${name}: busy" else "${name}: free" } mod bump(count: *int, by: int = 1) { count += by } on busy1 { bump(visits1) } on busy2 { bump(visits2) } @right out sign1 = "${status("Workshop", busy1)}<br>${visits1.Value} visits" @right out sign2 = "${status("Storage", busy2)}<br>${visits2.Value} visits"count: *intmeans “a variable holding an int”, not a copy of its value, sobump(visits1)adds tovisits1itself. Ref Parameters covers*in full.by: int = 1is a default, so a call that leavesbyout uses 1.<br>in the sign’s text starts a new line. Change the storage room to count each visit twice, withbump(visits2, 2).on busy1fires when the room goes from empty to occupied, so a second person walking in while the first is still there is not counted.
Make
Make a sign for a parking area with three bays, each with its own zone. Write
a mod that takes a bay’s number and whether it is taken, and returns one line
of text such as Bay 2: taken. Show all three lines on one sign, and add a
fourth line that says Full when every bay is taken.
zone1 Occupied -> chip bay1
zone2 Occupied -> chip bay2
zone3 Occupied -> chip bay3
chip sign -> decal Text
Good to Know
- Every call is a full copy. A mod of 50 gates called from ten places is 500 gates, and nothing warns you. Counting the Cost shows how to check.
- A
static var, a variable declared inside a mod that keeps its value between runs, is copied too, so each call has its own. Two calls never share a count. - A zone’s Occupied stays on while anyone is inside, so the sign shows
busyuntil the last person leaves.
See also: mod: Inline Chips,
Default and Named Arguments and
return.
Next: Chips.