Traffic lights
A junction of two roads with a walk button, run second by second; and an audit that checks every second of a log against six rules and reports every violation - on the run itself, the run with a fault put in, or a log typed in.
Every screen below was recorded under CPython. When this page was built, the EML interpreter replayed each session from the same input and printed the same bytes.
About
A junction of two roads, north-south (NS) and east-west (EW), with a walk button, run second by second. Then an audit that checks every second of a log against six rules and reports every violation, not just the first. It audits the run itself, the run with a fault put in, or a log typed in. The program starts with 120 seconds and the button pressed at seconds 5 and 70.
main.eml- the menu, the run table, the audit report, presses and timingslights.eml- the controller, the audit, the faults, and reading a typed log
How each part works:
- The controller, as in the corpus case
traffic-light-simulator: each phase is held for its time, and an index moves on by hand: NS green, NS yellow, all red, EW green, EW yellow, all red. A press of the button is kept until the next all red ends. Then come walk, flashing, and the next green. A press during a walk is already answered. - A log is one entry a second: NS, EW and the walk signal.
- The audit, as in the corpus case
state-machine-trace-audit: every second is checked against every rule, and every violation is listed. The rules: 1. a light goes green, yellow, red, green, with no other change; 2. never both roads not red; 3. walk or flashing only while both roads are red; 4. the walk signal goes don't walk, walk, flashing, don't walk; 5. a yellow lasts at least the yellow time; 6. a road turns green only after the other has been red for the all red time.
A yellow already showing at second 0, or still showing at the end, is not timed. The report ends with clean seconds plus seconds with a violation, which add up to the seconds checked, and the count for each rule. - The faults: - stuck: NS stays green for the first 2 seconds of EW green; - no-yellow: NS's first yellow shows green instead; - short-yellow: NS's first yellow lasts 1 second; - early-walk: the first walk starts a second before the all red.
Each is put in only where it will show before the log ends.
What is checked: menu choices 1 to 5; a log name; 2 to 60 typed entries of three letters (G, Y or R twice, then D, W or F); up to 10 presses, each within the run, or none; green 5 to 60, yellow 3 to 6, all red 1 to 5, walk 4 to 20 and flashing 3 to 20 seconds, and a run of 30 to 300 seconds. An empty answer cancels.
Sessions: sessions/basic.in gives: - the run, with waits of 20 and 17 seconds; its audit, which is clean; - each fault and the violations it causes: 3, 1, 1 and 1; - a typed log with 8 violations in 5 of its 8 seconds; - presses at 30, 52 and 115: a wait, a press that is already walking, and one with no walk before the run ends; - shorter timings and a 60-second run, which drops the press at 115; its run and clean audit.
sessions/bad-input.in gives: - menu choices 0 and x; the log lizard; a cancel; - typed logs with one entry, a bad letter, and four letters; - presses abc, 120, eleven of them; a cancel; none, then a run with no presses and an early-walk with no walk; - timings out of range for green, yellow, all red and walk; then cancels.
Built on the verified corpus cases traffic-light-simulator (phases held for set times, the index moved on by hand) and state-machine-trace-audit (every event judged, not just the first bad one, and the totals that tie the verdicts together).
Recorded sessions
What the screen shows while someone uses the program. Each typed line appears after its prompt, the way a terminal shows it.
bad-input
interpreter: byte-equal== Traffic lights ==
A junction with a walk button, second by second - and an audit that checks every second of a log against every rule.
1) run 2) audit 3) walk button 4) timings 5) quit
choice> 0
Pick a number from 1 to 5.
1) run 2) audit 3) walk button 4) timings 5) quit
choice> x
Pick a number from 1 to 5.
1) run 2) audit 3) walk button 4) timings 5) quit
choice> 2
Audit the run as it is (run), the run with a fault (stuck, no-yellow, short-yellow, early-walk), or a log you type (own).
log (run, stuck, no-yellow, short-yellow, early-walk or own)> lizard
Type run, stuck, no-yellow, short-yellow, early-walk or own.
1) run 2) audit 3) walk button 4) timings 5) quit
choice> 2
Audit the run as it is (run), the run with a fault (stuck, no-yellow, short-yellow, early-walk), or a log you type (own).
log (run, stuck, no-yellow, short-yellow, early-walk or own)>
Cancelled.
1) run 2) audit 3) walk button 4) timings 5) quit
choice> 2
Audit the run as it is (run), the run with a fault (stuck, no-yellow, short-yellow, early-walk), or a log you type (own).
log (run, stuck, no-yellow, short-yellow, early-walk or own)> own
One entry a second, three letters each: NS and EW (G, Y or R), then the walk signal (D don't walk, W walk, F flashing). For example: GRD YRD RRD RGD.
log (2 to 60 entries)> GRD
Type 2 to 60 entries like GRD, separated by spaces.
1) run 2) audit 3) walk button 4) timings 5) quit
choice> 2
Audit the run as it is (run), the run with a fault (stuck, no-yellow, short-yellow, early-walk), or a log you type (own).
log (run, stuck, no-yellow, short-yellow, early-walk or own)> own
One entry a second, three letters each: NS and EW (G, Y or R), then the walk signal (D don't walk, W walk, F flashing). For example: GRD YRD RRD RGD.
log (2 to 60 entries)> GRD GXD
Type 2 to 60 entries like GRD, separated by spaces.
1) run 2) audit 3) walk button 4) timings 5) quit
choice> 2
Audit the run as it is (run), the run with a fault (stuck, no-yellow, short-yellow, early-walk), or a log you type (own).
log (run, stuck, no-yellow, short-yellow, early-walk or own)> own
One entry a second, three letters each: NS and EW (G, Y or R), then the walk signal (D don't walk, W walk, F flashing). For example: GRD YRD RRD RGD.
log (2 to 60 entries)> GRDD RRD
Type 2 to 60 entries like GRD, separated by spaces.
1) run 2) audit 3) walk button 4) timings 5) quit
choice> 3
Seconds the walk button is pressed, 0 to 119, up to 10 of them; or none.
presses> abc
Type up to 10 seconds from 0 to 119, separated by spaces, or none.
1) run 2) audit 3) walk button 4) timings 5) quit
choice> 3
Seconds the walk button is pressed, 0 to 119, up to 10 of them; or none.
presses> 120
Type up to 10 seconds from 0 to 119, separated by spaces, or none.
1) run 2) audit 3) walk button 4) timings 5) quit
choice> 3
Seconds the walk button is pressed, 0 to 119, up to 10 of them; or none.
presses> 1 2 3 4 5 6 7 8 9 10 11
Type up to 10 seconds from 0 to 119, separated by spaces, or none.
1) run 2) audit 3) walk button 4) timings 5) quit
choice> 3
Seconds the walk button is pressed, 0 to 119, up to 10 of them; or none.
presses>
Cancelled.
1) run 2) audit 3) walk button 4) timings 5) quit
choice> 3
Seconds the walk button is pressed, 0 to 119, up to 10 of them; or none.
presses> none
No presses.
1) run 2) audit 3) walk button 4) timings 5) quit
choice> 1
North-south (NS) and east-west (EW), 120 seconds: green 20, yellow 3, all red 2, walk 7, flashing 5.
seconds NS EW walk
0-19 green red don't walk
20-22 yellow red don't walk
23-24 red red don't walk
25-44 red green don't walk
45-47 red yellow don't walk
48-49 red red don't walk
50-69 green red don't walk
70-72 yellow red don't walk
73-74 red red don't walk
75-94 red green don't walk
95-97 red yellow don't walk
98-99 red red don't walk
100-119 green red don't walk
Nobody pressed the walk button.
The button is answered when the next all red ends: both roads have stopped, then walk, flashing, and the next green.
1) run 2) audit 3) walk button 4) timings 5) quit
choice> 2
Audit the run as it is (run), the run with a fault (stuck, no-yellow, short-yellow, early-walk), or a log you type (own).
log (run, stuck, no-yellow, short-yellow, early-walk or own)> early-walk
Nobody walks in this run; press the button first.
1) run 2) audit 3) walk button 4) timings 5) quit
choice> 4
green (5 to 60)> 4
Type a whole number from 5 to 60.
green (5 to 60)> 61
Type a whole number from 5 to 60.
green (5 to 60)> 20
yellow (3 to 6)> 2
Type a whole number from 3 to 6.
yellow (3 to 6)> 7
Type a whole number from 3 to 6.
yellow (3 to 6)> 3
all red (1 to 5)> 0
Type a whole number from 1 to 5.
all red (1 to 5)> 6
Type a whole number from 1 to 5.
all red (1 to 5)> 4
walk (4 to 20)> 21
Type a whole number from 4 to 20.
walk (4 to 20)>
Cancelled.
1) run 2) audit 3) walk button 4) timings 5) quit
choice> 5
Bye.
What was typed (41 lines)
0
x
2
lizard
2
2
own
GRD
2
own
GRD GXD
2
own
GRDD RRD
3
abc
3
120
3
1 2 3 4 5 6 7 8 9 10 11
3
3
none
1
2
early-walk
4
4
61
20
2
7
3
0
6
4
21
5
basic
interpreter: byte-equal== Traffic lights ==
A junction with a walk button, second by second - and an audit that checks every second of a log against every rule.
1) run 2) audit 3) walk button 4) timings 5) quit
choice> 1
North-south (NS) and east-west (EW), 120 seconds: green 20, yellow 3, all red 2, walk 7, flashing 5.
seconds NS EW walk
0-19 green red don't walk
20-22 yellow red don't walk
23-24 red red don't walk
25-31 red red walk
32-36 red red flashing
37-56 red green don't walk
57-59 red yellow don't walk
60-61 red red don't walk
62-81 green red don't walk
82-84 yellow red don't walk
85-86 red red don't walk
87-93 red red walk
94-98 red red flashing
99-118 red green don't walk
119 red yellow don't walk
Button at second 5: walk at second 25, after 20 seconds.
Button at second 70: walk at second 87, after 17 seconds.
The button is answered when the next all red ends: both roads have stopped, then walk, flashing, and the next green.
1) run 2) audit 3) walk button 4) timings 5) quit
choice> 2
Audit the run as it is (run), the run with a fault (stuck, no-yellow, short-yellow, early-walk), or a log you type (own).
log (run, stuck, no-yellow, short-yellow, early-walk or own)> run
Every second against every rule:
1 a light goes green, yellow, red, green - no other change
2 never both roads not red
3 walk or flashing only while both roads are red
4 the walk signal goes don't walk, walk, flashing, don't walk
5 a yellow lasts at least 3 seconds
6 a road turns green only after the other has been red for 2 seconds
No violations.
120 seconds checked: 120 clean + 0 with a violation.
1) run 2) audit 3) walk button 4) timings 5) quit
choice> 2
Audit the run as it is (run), the run with a fault (stuck, no-yellow, short-yellow, early-walk), or a log you type (own).
log (run, stuck, no-yellow, short-yellow, early-walk or own)> stuck
Fault: NS stays green for the first 2 seconds of EW green, like a stuck relay.
Every second against every rule:
1 a light goes green, yellow, red, green - no other change
2 never both roads not red
3 walk or flashing only while both roads are red
4 the walk signal goes don't walk, walk, flashing, don't walk
5 a yellow lasts at least 3 seconds
6 a road turns green only after the other has been red for 2 seconds
Every violation, not just the first:
second 37, rule 2: NS green and EW green at once
second 38, rule 2: NS green and EW green at once
second 39, rule 1: NS went green to red
120 seconds checked: 117 clean + 3 with a violation.
3 in all - rule 1: 1, rule 2: 2.
1) run 2) audit 3) walk button 4) timings 5) quit
choice> 2
Audit the run as it is (run), the run with a fault (stuck, no-yellow, short-yellow, early-walk), or a log you type (own).
log (run, stuck, no-yellow, short-yellow, early-walk or own)> no-yellow
Fault: NS's first yellow shows green instead.
Every second against every rule:
1 a light goes green, yellow, red, green - no other change
2 never both roads not red
3 walk or flashing only while both roads are red
4 the walk signal goes don't walk, walk, flashing, don't walk
5 a yellow lasts at least 3 seconds
6 a road turns green only after the other has been red for 2 seconds
Every violation, not just the first:
second 23, rule 1: NS went green to red
120 seconds checked: 119 clean + 1 with a violation.
1 in all - rule 1: 1.
1) run 2) audit 3) walk button 4) timings 5) quit
choice> 2
Audit the run as it is (run), the run with a fault (stuck, no-yellow, short-yellow, early-walk), or a log you type (own).
log (run, stuck, no-yellow, short-yellow, early-walk or own)> short-yellow
Fault: NS's first yellow lasts 1 second, with red after.
Every second against every rule:
1 a light goes green, yellow, red, green - no other change
2 never both roads not red
3 walk or flashing only while both roads are red
4 the walk signal goes don't walk, walk, flashing, don't walk
5 a yellow lasts at least 3 seconds
6 a road turns green only after the other has been red for 2 seconds
Every violation, not just the first:
second 21, rule 5: NS yellow lasted only 1 s
120 seconds checked: 119 clean + 1 with a violation.
1 in all - rule 5: 1.
1) run 2) audit 3) walk button 4) timings 5) quit
choice> 2
Audit the run as it is (run), the run with a fault (stuck, no-yellow, short-yellow, early-walk), or a log you type (own).
log (run, stuck, no-yellow, short-yellow, early-walk or own)> early-walk
Fault: the first walk starts a second before the all red, while a road is still yellow.
Every second against every rule:
1 a light goes green, yellow, red, green - no other change
2 never both roads not red
3 walk or flashing only while both roads are red
4 the walk signal goes don't walk, walk, flashing, don't walk
5 a yellow lasts at least 3 seconds
6 a road turns green only after the other has been red for 2 seconds
Every violation, not just the first:
second 22, rule 3: walk showing while NS is yellow
120 seconds checked: 119 clean + 1 with a violation.
1 in all - rule 3: 1.
1) run 2) audit 3) walk button 4) timings 5) quit
choice> 2
Audit the run as it is (run), the run with a fault (stuck, no-yellow, short-yellow, early-walk), or a log you type (own).
log (run, stuck, no-yellow, short-yellow, early-walk or own)> own
One entry a second, three letters each: NS and EW (G, Y or R), then the walk signal (D don't walk, W walk, F flashing). For example: GRD YRD RRD RGD.
log (2 to 60 entries)> GRD GRD YRD RRD RGD RGW GYD RRD
Every second against every rule:
1 a light goes green, yellow, red, green - no other change
2 never both roads not red
3 walk or flashing only while both roads are red
4 the walk signal goes don't walk, walk, flashing, don't walk
5 a yellow lasts at least 3 seconds
6 a road turns green only after the other has been red for 2 seconds
Every violation, not just the first:
second 3, rule 5: NS yellow lasted only 1 s
second 4, rule 6: EW turned green after only 1 s of NS red
second 5, rule 3: walk showing while EW is green
second 6, rule 2: NS green and EW yellow at once
second 6, rule 4: walk signal went walk to don't walk
second 6, rule 6: NS turned green after only 0 s of EW red
second 7, rule 1: NS went green to red
second 7, rule 5: EW yellow lasted only 1 s
8 seconds checked: 3 clean + 5 with a violation.
8 in all - rule 1: 1, rule 2: 1, rule 3: 1, rule 4: 1, rule 5: 2, rule 6: 2.
1) run 2) audit 3) walk button 4) timings 5) quit
choice> 3
Seconds the walk button is pressed, 0 to 119, up to 10 of them; or none.
presses> 30 52 115
Presses at 30, 52, 115.
1) run 2) audit 3) walk button 4) timings 5) quit
choice> 1
North-south (NS) and east-west (EW), 120 seconds: green 20, yellow 3, all red 2, walk 7, flashing 5.
seconds NS EW walk
0-19 green red don't walk
20-22 yellow red don't walk
23-24 red red don't walk
25-44 red green don't walk
45-47 red yellow don't walk
48-49 red red don't walk
50-56 red red walk
57-61 red red flashing
62-81 green red don't walk
82-84 yellow red don't walk
85-86 red red don't walk
87-106 red green don't walk
107-109 red yellow don't walk
110-111 red red don't walk
112-119 green red don't walk
Button at second 30: walk at second 50, after 20 seconds.
Button at second 52: already walking.
Button at second 115: no walk before the run ends.
The button is answered when the next all red ends: both roads have stopped, then walk, flashing, and the next green.
1) run 2) audit 3) walk button 4) timings 5) quit
choice> 4
green (5 to 60)> 10
yellow (3 to 6)> 3
all red (1 to 5)> 1
walk (4 to 20)> 5
flashing (3 to 20)> 3
seconds to run (30 to 300)> 60
Green 10, yellow 3, all red 1, walk 5, flashing 3; 60 seconds.
The run now ends at second 59; dropped: 115.
1) run 2) audit 3) walk button 4) timings 5) quit
choice> 1
North-south (NS) and east-west (EW), 60 seconds: green 10, yellow 3, all red 1, walk 5, flashing 3.
seconds NS EW walk
0-9 green red don't walk
10-12 yellow red don't walk
13 red red don't walk
14-23 red green don't walk
24-26 red yellow don't walk
27 red red don't walk
28-37 green red don't walk
38-40 yellow red don't walk
41 red red don't walk
42-46 red red walk
47-49 red red flashing
50-59 red green don't walk
Button at second 30: walk at second 42, after 12 seconds.
Button at second 52: no walk before the run ends.
The button is answered when the next all red ends: both roads have stopped, then walk, flashing, and the next green.
1) run 2) audit 3) walk button 4) timings 5) quit
choice> 2
Audit the run as it is (run), the run with a fault (stuck, no-yellow, short-yellow, early-walk), or a log you type (own).
log (run, stuck, no-yellow, short-yellow, early-walk or own)> run
Every second against every rule:
1 a light goes green, yellow, red, green - no other change
2 never both roads not red
3 walk or flashing only while both roads are red
4 the walk signal goes don't walk, walk, flashing, don't walk
5 a yellow lasts at least 3 seconds
6 a road turns green only after the other has been red for 1 second
No violations.
60 seconds checked: 60 clean + 0 with a violation.
1) run 2) audit 3) walk button 4) timings 5) quit
choice> 5
Bye.
What was typed (28 lines)
1
2
run
2
stuck
2
no-yellow
2
short-yellow
2
early-walk
2
own
GRD GRD YRD RRD RGD RGW GYD RRD
3
30 52 115
1
4
10
3
1
5
3
60
1
2
run
5
Modules
The program as written, entry module first. Each module transpiles to its own Python file, which is what eml project run executes.
main.eml(entry)
eml# P097 traffic lights: a junction with a walk button, run second by second,
# and an audit that checks every second of a log against every rule -
# the run itself, the run with a fault put in, or a log typed in.
import lights
def plain(s):
0 => i
len(s) => j
while i < j and s[i] == " ":
i + 1 => i
while j > i and s[j - 1] == " ":
j - 1 => j
return s[i:j]
def number(s, most):
if len(s) == 0 or len(s) > most:
return -1
0 => n
for c in s:
if not (c >= "0" and c <= "9"):
return -1
n * 10 + int(c) => n
return n
def pad(s, n):
while len(s) < n:
s + " " => s
return s
def lpad(s, n):
while len(s) < n:
" " + s => s
return s
def seconds(n):
if n == 1:
return "1 second"
return str(n) + " seconds"
def ask(prompt, low, high):
while True:
plain(input(prompt + " (" + str(low) + " to " + str(high) + ")> ")) => a
if a == "":
return -1
number(a, 6) => n
if n >= low and n <= high:
return n
("Type a whole number from " + str(low) + " to " + str(high) + ".") ^0
def show_run(cfg, presses):
lights.run(cfg, presses) => log
("North-south (NS) and east-west (EW), " + seconds(cfg[5]) + ": green " + str(cfg[0]) + ", yellow " + str(cfg[1]) + ", all red " + str(cfg[2]) + ", walk " + str(cfg[3]) + ", flashing " + str(cfg[4]) + ".") ^0
" seconds NS EW walk" ^0
for g in lights.runs(log):
" " => span
if g[1] > g[0]:
"-" + pad(str(g[1]), 4) => span
(" " + lpad(str(g[0]), 3) + span + pad(lights.name(g[2][0]), 8) + pad(lights.name(g[2][1]), 8) + lights.name(g[2][2])) ^0
if len(presses) == 0:
"Nobody pressed the walk button." ^0
for w in lights.waits(log, presses):
if w[1] == -1:
("Button at second " + str(w[0]) + ": no walk before the run ends.") ^0
elif w[1] == w[0]:
("Button at second " + str(w[0]) + ": already walking.") ^0
else:
("Button at second " + str(w[0]) + ": walk at second " + str(w[1]) + ", after " + seconds(w[1] - w[0]) + ".") ^0
"The button is answered when the next all red ends: both roads have stopped, then walk, flashing, and the next green." ^0
def report(log, cfg):
lights.audit(log, cfg[1], cfg[2]) => found
"Every second against every rule:" ^0
" 1 a light goes green, yellow, red, green - no other change" ^0
" 2 never both roads not red" ^0
" 3 walk or flashing only while both roads are red" ^0
" 4 the walk signal goes don't walk, walk, flashing, don't walk" ^0
(" 5 a yellow lasts at least " + seconds(cfg[1])) ^0
(" 6 a road turns green only after the other has been red for " + seconds(cfg[2])) ^0
if len(found) == 0:
"No violations." ^0
else:
"Every violation, not just the first:" ^0
{} => bad
[0, 0, 0, 0, 0, 0, 0] => by_rule
for f in found:
(" second " + lpad(str(f[0]), 3) + ", rule " + str(f[1]) + ": " + f[2]) ^0
True => bad[f[0]]
by_rule[f[1]] + 1 => by_rule[f[1]]
len(bad) => n
(str(len(log)) + " seconds checked: " + str(len(log) - n) + " clean + " + str(n) + " with a violation.") ^0
if len(found) > 0:
"" => line
for r in [1:6]:
if by_rule[r] > 0:
if line != "":
line + ", " => line
line + "rule " + str(r) + ": " + str(by_rule[r]) => line
(str(len(found)) + " in all - " + line + ".") ^0
def audit_menu(cfg, presses):
lights.run(cfg, presses) => log
"Audit the run as it is (run), the run with a fault (stuck, no-yellow, short-yellow, early-walk), or a log you type (own)." ^0
plain(input("log (run, stuck, no-yellow, short-yellow, early-walk or own)> ")) => s
if s == "":
"Cancelled." ^0
return
if s == "own":
"One entry a second, three letters each: NS and EW (G, Y or R), then the walk signal (D don't walk, W walk, F flashing). For example: GRD YRD RRD RGD." ^0
lights.parse(plain(input("log (2 to 60 entries)> "))) => log
if len(log) == 0:
"Type 2 to 60 entries like GRD, separated by spaces." ^0
return
elif s == "stuck" or s == "no-yellow" or s == "short-yellow" or s == "early-walk":
lights.inject(log, s, cfg) => faulty
if len(faulty) == 0:
if s == "stuck":
"EW never turns green with 2 seconds to spare in this run." ^0
elif s == "early-walk":
"Nobody walks in this run; press the button first." ^0
else:
"NS never finishes a yellow in this run." ^0
return
faulty => log
if s == "stuck":
"Fault: NS stays green for the first 2 seconds of EW green, like a stuck relay." ^0
elif s == "no-yellow":
"Fault: NS's first yellow shows green instead." ^0
elif s == "short-yellow":
"Fault: NS's first yellow lasts 1 second, with red after." ^0
else:
"Fault: the first walk starts a second before the all red, while a road is still yellow." ^0
elif s != "run":
"Type run, stuck, no-yellow, short-yellow, early-walk or own." ^0
return
report(log, cfg)
def set_presses(cfg, presses):
("Seconds the walk button is pressed, 0 to " + str(cfg[5] - 1) + ", up to 10 of them; or none.") ^0
plain(input("presses> ")) => t
if t == "":
"Cancelled." ^0
return presses
if t == "none":
"No presses." ^0
return []
{} => seen
[] => out
"" => w
True => ok
for c in t + " ":
if c == " ":
if w != "":
number(w, 3) => v
if v < 0 or v >= cfg[5]:
False => ok
elif not (v in seen):
True => seen[v]
out + [v] => out
"" => w
else:
w + c => w
if not ok or len(out) > 10:
("Type up to 10 seconds from 0 to " + str(cfg[5] - 1) + ", separated by spaces, or none.") ^0
return presses
# in order
for i in [1:len(out) - 1]:
i => j
while j > 0 and out[j - 1] > out[j]:
out[j] => x
out[j - 1] => out[j]
x => out[j - 1]
j - 1 => j
"" => line
for v in out:
if line != "":
line + ", " => line
line + str(v) => line
("Presses at " + line + ".") ^0
return out
def timings(cfg):
ask("green", 5, 60) => g
if g == -1:
"Cancelled." ^0
return cfg
ask("yellow", 3, 6) => y
if y == -1:
"Cancelled." ^0
return cfg
ask("all red", 1, 5) => a
if a == -1:
"Cancelled." ^0
return cfg
ask("walk", 4, 20) => w
if w == -1:
"Cancelled." ^0
return cfg
ask("flashing", 3, 20) => f
if f == -1:
"Cancelled." ^0
return cfg
ask("seconds to run", 30, 300) => n
if n == -1:
"Cancelled." ^0
return cfg
("Green " + str(g) + ", yellow " + str(y) + ", all red " + str(a) + ", walk " + str(w) + ", flashing " + str(f) + "; " + seconds(n) + ".") ^0
return [g, y, a, w, f, n]
[20, 3, 2, 7, 5, 120] => cfg
[5, 70] => presses
"== Traffic lights ==" ^0
"A junction with a walk button, second by second - and an audit that checks every second of a log against every rule." ^0
True => running
while running:
"" ^0
"1) run 2) audit 3) walk button 4) timings 5) quit" ^0
plain(input("choice> ")) => choice
if choice == "1":
show_run(cfg, presses)
elif choice == "2":
audit_menu(cfg, presses)
elif choice == "3":
set_presses(cfg, presses) => presses
elif choice == "4":
timings(cfg) => new
if new[5] != cfg[5]:
[] => kept
for v in presses:
if v < new[5]:
kept + [v] => kept
if len(kept) < len(presses):
"" => line
for v in presses:
if v >= new[5]:
if line != "":
line + ", " => line
line + str(v) => line
("The run now ends at second " + str(new[5] - 1) + "; dropped: " + line + ".") ^0
kept => presses
new => cfg
elif choice == "5":
False => running
else:
"Pick a number from 1 to 5." ^0
"Bye." ^0
Python projection (main.py)
import lights
def plain(s):
i = 0
j = len(s)
while i < j and s[i] == " ":
i = i + 1
while j > i and s[j - 1] == " ":
j = j - 1
return s[i:j]
def number(s, most):
if len(s) == 0 or len(s) > most:
return -1
n = 0
for c in s:
if not (c >= "0" and c <= "9"):
return -1
n = n * 10 + int(c)
return n
def pad(s, n):
while len(s) < n:
s = s + " "
return s
def lpad(s, n):
while len(s) < n:
s = " " + s
return s
def seconds(n):
if n == 1:
return "1 second"
return str(n) + " seconds"
def ask(prompt, low, high):
while True:
a = plain(input(prompt + " (" + str(low) + " to " + str(high) + ")> "))
if a == "":
return -1
n = number(a, 6)
if n >= low and n <= high:
return n
print("Type a whole number from " + str(low) + " to " + str(high) + ".")
def show_run(cfg, presses):
log = lights.run(cfg, presses)
print("North-south (NS) and east-west (EW), " + seconds(cfg[5]) + ": green " + str(cfg[0]) + ", yellow " + str(cfg[1]) + ", all red " + str(cfg[2]) + ", walk " + str(cfg[3]) + ", flashing " + str(cfg[4]) + ".")
print(" seconds NS EW walk")
for g in lights.runs(log):
span = " "
if g[1] > g[0]:
span = "-" + pad(str(g[1]), 4)
print(" " + lpad(str(g[0]), 3) + span + pad(lights.name(g[2][0]), 8) + pad(lights.name(g[2][1]), 8) + lights.name(g[2][2]))
if len(presses) == 0:
print("Nobody pressed the walk button.")
for w in lights.waits(log, presses):
if w[1] == -1:
print("Button at second " + str(w[0]) + ": no walk before the run ends.")
elif w[1] == w[0]:
print("Button at second " + str(w[0]) + ": already walking.")
else:
print("Button at second " + str(w[0]) + ": walk at second " + str(w[1]) + ", after " + seconds(w[1] - w[0]) + ".")
print("The button is answered when the next all red ends: both roads have stopped, then walk, flashing, and the next green.")
def report(log, cfg):
found = lights.audit(log, cfg[1], cfg[2])
print("Every second against every rule:")
print(" 1 a light goes green, yellow, red, green - no other change")
print(" 2 never both roads not red")
print(" 3 walk or flashing only while both roads are red")
print(" 4 the walk signal goes don't walk, walk, flashing, don't walk")
print(" 5 a yellow lasts at least " + seconds(cfg[1]))
print(" 6 a road turns green only after the other has been red for " + seconds(cfg[2]))
if len(found) == 0:
print("No violations.")
else:
print("Every violation, not just the first:")
bad = {}
by_rule = [0, 0, 0, 0, 0, 0, 0]
for f in found:
print(" second " + lpad(str(f[0]), 3) + ", rule " + str(f[1]) + ": " + f[2])
bad[f[0]] = True
by_rule[f[1]] = by_rule[f[1]] + 1
n = len(bad)
print(str(len(log)) + " seconds checked: " + str(len(log) - n) + " clean + " + str(n) + " with a violation.")
if len(found) > 0:
line = ""
for r in range(1, 7):
if by_rule[r] > 0:
if line != "":
line = line + ", "
line = line + "rule " + str(r) + ": " + str(by_rule[r])
print(str(len(found)) + " in all - " + line + ".")
def audit_menu(cfg, presses):
log = lights.run(cfg, presses)
print("Audit the run as it is (run), the run with a fault (stuck, no-yellow, short-yellow, early-walk), or a log you type (own).")
s = plain(input("log (run, stuck, no-yellow, short-yellow, early-walk or own)> "))
if s == "":
print("Cancelled.")
return
if s == "own":
print("One entry a second, three letters each: NS and EW (G, Y or R), then the walk signal (D don't walk, W walk, F flashing). For example: GRD YRD RRD RGD.")
log = lights.parse(plain(input("log (2 to 60 entries)> ")))
if len(log) == 0:
print("Type 2 to 60 entries like GRD, separated by spaces.")
return
elif s == "stuck" or s == "no-yellow" or s == "short-yellow" or s == "early-walk":
faulty = lights.inject(log, s, cfg)
if len(faulty) == 0:
if s == "stuck":
print("EW never turns green with 2 seconds to spare in this run.")
elif s == "early-walk":
print("Nobody walks in this run; press the button first.")
else:
print("NS never finishes a yellow in this run.")
return
log = faulty
if s == "stuck":
print("Fault: NS stays green for the first 2 seconds of EW green, like a stuck relay.")
elif s == "no-yellow":
print("Fault: NS's first yellow shows green instead.")
elif s == "short-yellow":
print("Fault: NS's first yellow lasts 1 second, with red after.")
else:
print("Fault: the first walk starts a second before the all red, while a road is still yellow.")
elif s != "run":
print("Type run, stuck, no-yellow, short-yellow, early-walk or own.")
return
report(log, cfg)
def set_presses(cfg, presses):
print("Seconds the walk button is pressed, 0 to " + str(cfg[5] - 1) + ", up to 10 of them; or none.")
t = plain(input("presses> "))
if t == "":
print("Cancelled.")
return presses
if t == "none":
print("No presses.")
return []
seen = {}
out = []
w = ""
ok = True
for c in t + " ":
if c == " ":
if w != "":
v = number(w, 3)
if v < 0 or v >= cfg[5]:
ok = False
elif not v in seen:
seen[v] = True
out = out + [v]
w = ""
else:
w = w + c
if not ok or len(out) > 10:
print("Type up to 10 seconds from 0 to " + str(cfg[5] - 1) + ", separated by spaces, or none.")
return presses
for i in range(1, len(out)):
j = i
while j > 0 and out[j - 1] > out[j]:
x = out[j]
out[j] = out[j - 1]
out[j - 1] = x
j = j - 1
line = ""
for v in out:
if line != "":
line = line + ", "
line = line + str(v)
print("Presses at " + line + ".")
return out
def timings(cfg):
g = ask("green", 5, 60)
if g == -1:
print("Cancelled.")
return cfg
y = ask("yellow", 3, 6)
if y == -1:
print("Cancelled.")
return cfg
a = ask("all red", 1, 5)
if a == -1:
print("Cancelled.")
return cfg
w = ask("walk", 4, 20)
if w == -1:
print("Cancelled.")
return cfg
f = ask("flashing", 3, 20)
if f == -1:
print("Cancelled.")
return cfg
n = ask("seconds to run", 30, 300)
if n == -1:
print("Cancelled.")
return cfg
print("Green " + str(g) + ", yellow " + str(y) + ", all red " + str(a) + ", walk " + str(w) + ", flashing " + str(f) + "; " + seconds(n) + ".")
return [g, y, a, w, f, n]
cfg = [20, 3, 2, 7, 5, 120]
presses = [5, 70]
print("== Traffic lights ==")
print("A junction with a walk button, second by second - and an audit that checks every second of a log against every rule.")
running = True
while running:
print("")
print("1) run 2) audit 3) walk button 4) timings 5) quit")
choice = plain(input("choice> "))
if choice == "1":
show_run(cfg, presses)
elif choice == "2":
audit_menu(cfg, presses)
elif choice == "3":
presses = set_presses(cfg, presses)
elif choice == "4":
new = timings(cfg)
if new[5] != cfg[5]:
kept = []
for v in presses:
if v < new[5]:
kept = kept + [v]
if len(kept) < len(presses):
line = ""
for v in presses:
if v >= new[5]:
if line != "":
line = line + ", "
line = line + str(v)
print("The run now ends at second " + str(new[5] - 1) + "; dropped: " + line + ".")
presses = kept
cfg = new
elif choice == "5":
running = False
else:
print("Pick a number from 1 to 5.")
print("Bye.")
lights.eml
eml# P097 traffic lights - a junction of two roads, north-south (NS) and
# east-west (EW), with a walk button, run second by second as in the corpus
# case traffic-light-simulator (phases held for set times, an index moved
# on by hand). Then an audit of the log, as in the corpus case
# state-machine-trace-audit: every second against every rule, reporting
# every violation, not just the first.
#
# A log is one entry per second: [NS, EW, walk], the lights "G", "Y" or "R",
# the walk signal "D" (don't walk), "W" (walk) or "F" (flashing).
def name(c):
{"G": "green", "Y": "yellow", "R": "red", "D": "don't walk", "W": "walk", "F": "flashing"} => names
return names[c]
def run(cfg, presses):
# cfg: [green, yellow, all red, walk, flashing, seconds to run]. The
# cars go NS green, NS yellow, all red, EW green, EW yellow, all red. A
# press of the button (not during a walk) is kept until the next all
# red ends; then walk, flashing, and on to the next green.
[["G", "R", cfg[0]], ["Y", "R", cfg[1]], ["R", "R", cfg[2]], ["R", "G", cfg[0]], ["R", "Y", cfg[1]], ["R", "R", cfg[2]]] => phases
{} => pressed
for t in presses:
True => pressed[t]
[] => log
0 => p
"D" => walk
0 => elapsed
False => pending
for t in [0:cfg[5] - 1]:
if t in pressed and walk != "W":
True => pending
if walk == "D":
log + [[phases[p][0], phases[p][1], "D"]] => log
else:
log + [["R", "R", walk]] => log
elapsed + 1 => elapsed
if walk == "W":
if elapsed == cfg[3]:
"F" => walk
0 => elapsed
elif walk == "F":
if elapsed == cfg[4]:
"D" => walk
0 => elapsed
(p + 1) % 6 => p
elif elapsed == phases[p][2]:
0 => elapsed
if (p == 2 or p == 5) and pending:
"W" => walk
False => pending
else:
(p + 1) % 6 => p
return log
def same(a, b):
return a[0] == b[0] and a[1] == b[1] and a[2] == b[2]
def runs(log):
# Seconds in a row with the same lights: [first, last, entry].
[] => out
0 => first
for t in [1:len(log)]:
if t == len(log) or not same(log[t], log[first]):
out + [[first, t - 1, log[first]]] => out
t => first
return out
def waits(log, presses):
# For each press, the second the walk shows (the press itself if it is
# already walking), or -1 if no walk comes before the log ends.
[] => out
for t in presses:
-1 => at
t => s
while at == -1 and s < len(log):
if log[s][2] == "W":
s => at
s + 1 => s
out + [[t, at]] => out
return out
def copy(log):
[] => out
for e in log:
out + [[e[0], e[1], e[2]]] => out
return out
def inject(log, fault, cfg):
# A copy of the log with one fault put in, or [] if the log has no place
# where the fault would show before it ends.
copy(log) => out
if fault == "stuck":
# NS stays green for the first 2 seconds of EW green
for t in [1:len(log) - 3]:
if log[t][1] == "G" and log[t - 1][1] == "R":
"G" => out[t][0]
"G" => out[t + 1][0]
return out
return []
if fault == "no-yellow" or fault == "short-yellow":
# NS's first yellow: shown green, or cut to 1 second with red after
-1 => first
for t in [0:len(log) - 1]:
if first == -1 and log[t][0] == "Y":
t => first
if first == -1:
return []
first => t
while t < len(log) and log[t][0] == "Y":
t + 1 => t
if t == len(log):
return []
first => t
while t < len(log) and log[t][0] == "Y":
if fault == "no-yellow":
"G" => out[t][0]
elif t > first:
"R" => out[t][0]
t + 1 => t
return out
if fault == "early-walk":
# the first walk starts a second before the all red
for t in [1:len(log) - 1]:
if log[t][2] == "W" and log[t - 1][2] == "D":
if t - cfg[2] - 1 < 0:
return []
for k in [t - cfg[2] - 1:t - 1]:
"W" => out[k][2]
return out
return []
return []
def audit(log, yellow, allred):
# Every second against every rule; returns [second, rule, what] for each
# violation, in order. 1 a light goes green, yellow, red, green only;
# 2 never both roads not red; 3 walk or flashing only while both roads
# are red; 4 the walk signal goes don't walk, walk, flashing, don't
# walk; 5 a yellow lasts at least `yellow` seconds; 6 a road turns
# green only after the other has been red for `allred` seconds. A
# yellow already showing at second 0, or one still showing at the end,
# is not timed; nor is a green before second `allred`.
{"G": "Y", "Y": "R", "R": "G"} => after
{"D": "W", "W": "F", "F": "D"} => walk_after
["NS", "EW"] => road
[] => found
[-1, -1] => ystart
for t in [0:len(log) - 1]:
log[t] => s
if t > 0:
log[t - 1] => q
for r in [0:1]:
if s[r] != q[r] and s[r] != after[q[r]]:
found + [[t, 1, road[r] + " went " + name(q[r]) + " to " + name(s[r])]] => found
if s[0] != "R" and s[1] != "R":
found + [[t, 2, "NS " + name(s[0]) + " and EW " + name(s[1]) + " at once"]] => found
if s[2] != "D":
for r in [0:1]:
if s[r] != "R" and (r == 0 or s[0] == "R"):
found + [[t, 3, name(s[2]) + " showing while " + road[r] + " is " + name(s[r])]] => found
if t > 0:
if s[2] != q[2] and s[2] != walk_after[q[2]]:
found + [[t, 4, "walk signal went " + name(q[2]) + " to " + name(s[2])]] => found
for r in [0:1]:
if q[r] == "Y" and s[r] != "Y" and ystart[r] > 0 and t - ystart[r] < yellow:
found + [[t, 5, road[r] + " yellow lasted only " + str(t - ystart[r]) + " s"]] => found
for r in [0:1]:
if q[r] == "R" and s[r] == "G" and t - allred >= 0:
1 - r => o
t - 1 => k
while k >= t - allred and log[k][o] == "R":
k - 1 => k
if k >= t - allred:
found + [[t, 6, road[r] + " turned green after only " + str(t - 1 - k) + " s of " + road[o] + " red"]] => found
for r in [0:1]:
if s[r] == "Y" and (t == 0 or log[t - 1][r] != "Y"):
t => ystart[r]
return found
def parse(text):
# "GRD YRD RRD": one entry a second, NS and EW (G, Y, R), then the walk
# signal (D, W, F); 2 to 60 of them, or [] if anything is wrong.
[] => out
"" => w
for c in text + " ":
if c == " ":
if w != "":
if len(w) != 3 or not (w[0] in {"G": 1, "Y": 1, "R": 1}) or not (w[1] in {"G": 1, "Y": 1, "R": 1}) or not (w[2] in {"D": 1, "W": 1, "F": 1}):
return []
out + [[w[0], w[1], w[2]]] => out
"" => w
else:
w + c => w
if len(out) < 2 or len(out) > 60:
return []
return out
Python projection (lights.py)
def name(c):
names = {"G": "green", "Y": "yellow", "R": "red", "D": "don't walk", "W": "walk", "F": "flashing"}
return names[c]
def run(cfg, presses):
phases = [["G", "R", cfg[0]], ["Y", "R", cfg[1]], ["R", "R", cfg[2]], ["R", "G", cfg[0]], ["R", "Y", cfg[1]], ["R", "R", cfg[2]]]
pressed = {}
for t in presses:
pressed[t] = True
log = []
p = 0
walk = "D"
elapsed = 0
pending = False
for t in range(0, cfg[5]):
if t in pressed and walk != "W":
pending = True
if walk == "D":
log = log + [[phases[p][0], phases[p][1], "D"]]
else:
log = log + [["R", "R", walk]]
elapsed = elapsed + 1
if walk == "W":
if elapsed == cfg[3]:
walk = "F"
elapsed = 0
elif walk == "F":
if elapsed == cfg[4]:
walk = "D"
elapsed = 0
p = (p + 1) % 6
elif elapsed == phases[p][2]:
elapsed = 0
if (p == 2 or p == 5) and pending:
walk = "W"
pending = False
else:
p = (p + 1) % 6
return log
def same(a, b):
return a[0] == b[0] and a[1] == b[1] and a[2] == b[2]
def runs(log):
out = []
first = 0
for t in range(1, len(log)+1):
if t == len(log) or not same(log[t], log[first]):
out = out + [[first, t - 1, log[first]]]
first = t
return out
def waits(log, presses):
out = []
for t in presses:
at = -1
s = t
while at == -1 and s < len(log):
if log[s][2] == "W":
at = s
s = s + 1
out = out + [[t, at]]
return out
def copy(log):
out = []
for e in log:
out = out + [[e[0], e[1], e[2]]]
return out
def inject(log, fault, cfg):
out = copy(log)
if fault == "stuck":
for t in range(1, len(log) - 3+1):
if log[t][1] == "G" and log[t - 1][1] == "R":
out[t][0] = "G"
out[t + 1][0] = "G"
return out
return []
if fault == "no-yellow" or fault == "short-yellow":
first = -1
for t in range(0, len(log)):
if first == -1 and log[t][0] == "Y":
first = t
if first == -1:
return []
t = first
while t < len(log) and log[t][0] == "Y":
t = t + 1
if t == len(log):
return []
t = first
while t < len(log) and log[t][0] == "Y":
if fault == "no-yellow":
out[t][0] = "G"
elif t > first:
out[t][0] = "R"
t = t + 1
return out
if fault == "early-walk":
for t in range(1, len(log)):
if log[t][2] == "W" and log[t - 1][2] == "D":
if t - cfg[2] - 1 < 0:
return []
for k in range(t - cfg[2] - 1, t):
out[k][2] = "W"
return out
return []
return []
def audit(log, yellow, allred):
after = {"G": "Y", "Y": "R", "R": "G"}
walk_after = {"D": "W", "W": "F", "F": "D"}
road = ["NS", "EW"]
found = []
ystart = [-1, -1]
for t in range(0, len(log)):
s = log[t]
if t > 0:
q = log[t - 1]
for r in range(0, 2):
if s[r] != q[r] and s[r] != after[q[r]]:
found = found + [[t, 1, road[r] + " went " + name(q[r]) + " to " + name(s[r])]]
if s[0] != "R" and s[1] != "R":
found = found + [[t, 2, "NS " + name(s[0]) + " and EW " + name(s[1]) + " at once"]]
if s[2] != "D":
for r in range(0, 2):
if s[r] != "R" and (r == 0 or s[0] == "R"):
found = found + [[t, 3, name(s[2]) + " showing while " + road[r] + " is " + name(s[r])]]
if t > 0:
if s[2] != q[2] and s[2] != walk_after[q[2]]:
found = found + [[t, 4, "walk signal went " + name(q[2]) + " to " + name(s[2])]]
for r in range(0, 2):
if q[r] == "Y" and s[r] != "Y" and ystart[r] > 0 and t - ystart[r] < yellow:
found = found + [[t, 5, road[r] + " yellow lasted only " + str(t - ystart[r]) + " s"]]
for r in range(0, 2):
if q[r] == "R" and s[r] == "G" and t - allred >= 0:
o = 1 - r
k = t - 1
while k >= t - allred and log[k][o] == "R":
k = k - 1
if k >= t - allred:
found = found + [[t, 6, road[r] + " turned green after only " + str(t - 1 - k) + " s of " + road[o] + " red"]]
for r in range(0, 2):
if s[r] == "Y" and (t == 0 or log[t - 1][r] != "Y"):
ystart[r] = t
return found
def parse(text):
out = []
w = ""
for c in text + " ":
if c == " ":
if w != "":
if len(w) != 3 or not w[0] in {"G": 1, "Y": 1, "R": 1} or not w[1] in {"G": 1, "Y": 1, "R": 1} or not w[2] in {"D": 1, "W": 1, "F": 1}:
return []
out = out + [[w[0], w[1], w[2]]]
w = ""
else:
w = w + c
if len(out) < 2 or len(out) > 60:
return []
return out