Carrefour intelligent
Deux Feux tricolores et deux Boucles de détection menés par une machine à états qui sert la route qui attend, avec un vert minimum, des phases orange et rouge-orange, et l'orange clignotant la nuit.
Un carrefour dans votre ville : la route principale porte les convois de wagonnets et la circulation des villageois, une route secondaire arrive des fermes. Un cycle fixe arrête la route principale toutes les trente secondes, même quand personne n'attend sur la route secondaire. Ce projet fait ce que font les vrais contrôleurs : la route principale garde le vert, et une Boucle de détection dans la route secondaire ne demande le vert que quand quelque chose se tient dessus. Chaque changement passe par l'orange, un instant de rouge des deux côtés pour vider le carrefour, puis rouge et orange ensemble avant le vert, comme les feux britanniques et allemands. La nuit, les deux feux clignotent à l'orange.
Le programme est une machine à états : une variable retient la phase en cours, et une fonction appelée quatre fois par seconde décide s'il est temps de passer à la suivante. C'est la façon la plus claire d'écrire tout ce qui avance par étapes : des feux, une gare, un ascenseur, une suite de machines.
Ce qu'il vous faut
- Un Microcontrôleur (ou n'importe quel ordinateur à partir du Mini-ordinateur) : il est petit, et son port de Câble de données atteint tous les blocs du carrefour. Son écran fait 40 x 12 ; le programme y dessine les deux feux. Il est entraîné par le dessous.
- Deux Feux tricolores, un face à chaque route (ou plus : chaque route prend une liste de feux). Un feu réglé par un ordinateur reste sous son contrôle et ignore la redstone jusqu'à
traffic_light.release()(Feu tricolore). - Deux Boucles de détection, encastrées dans la route quelques blocs avant le carrefour. Une boucle voit les créatures, les joueurs, les wagonnets, les bateaux et les véhicules de Create Aeronautics jusqu'à deux blocs au-dessus d'elle (
Boucle de détection). - Du Câble de données sous la route, qui touche le Microcontrôleur, les deux feux et les deux boucles. Chaque bloc du câble est nommé d'après son type et sa position :
traffic_light@10,65,4,inductive_loop@10,63,9.
Vu de dessus :
| route |
| princ. |
| B1 | B1, B2 : Boucles de détection dans la route,
| | quelques blocs avant le carrefour
-----------+ +-----------
route B2 F1 F1 : Feu tricolore de la route principale
second. F2 F2 : Feu tricolore de la route secondaire
-----------+ +-----------
| | [M] M : le Microcontrôleur au coin
| |
un Câble de données sous la route relie M, F1, F2, B1 et B2Le programme, étape par étape
1. Les routes et les durées
Chaque route a un nom, une liste de feux et une liste de boucles. La première route est la route principale : elle récupère le vert quand personne n'attend nulle part.
local ROUTES = {
{nom = "Route principale", feux = {"traffic_light@10,65,4"}, boucles = {"inductive_loop@10,63,9"}},
{nom = "Route secondaire", feux = {"traffic_light@14,65,0"}, boucles = {"inductive_loop@19,63,0"}},
}
local VERT_MIN = 8 -- secondes de vert avant que le feu puisse changer
local VERT_MAX = 30 -- secondes de vert au plus pendant que l'autre route attend
local ORANGE = 3 -- secondes d'orange
local TOUT_ROUGE = 1 -- secondes avec les deux routes au rouge, pour vider le carrefour
local ROUGE_ORANGE = 1.5 -- secondes de rouge et orange avant le vert
local DEBUT_NUIT = 13000 -- os.day_time() à la tombée de la nuit (19 h)
local FIN_NUIT = 23000 -- os.day_time() à l'aube (5 h)
local PAS = 0.25 -- secondes entre deux coups d'œil aux bouclesVERT_MIN empêche un feu de changer de nouveau juste après être passé au vert ; VERT_MAX garantit qu'une route principale chargée ne laisse pas une route secondaire au rouge pour toujours.
Pour trouver les noms de vos blocs, lancez le programme avec liste après son nom : il les affiche et s'arrête. L'exemple devices (devices) les montre aussi, sur une carte.
if arg ~= nil and arg[1] == "liste" then
for _, sorte in ipairs({"traffic_light", "inductive_loop"}) do
for _, nom in ipairs(peripheral.list(sorte)) do print(nom) end
end
return
end2. Parler aux blocs
Le programme ne prend jamais les blocs une fois pour toutes. Il les appelle par leur nom avec peripheral.call, à travers pcall : un feu cassé par un creeper ou une boucle dans un chunk déchargé n'arrête pas tout le carrefour. Le nom va dans muets, et l'écran le signale.
local aspects = {"off", "off"} -- ce que montre chaque route
local comptes = {0, 0} -- ce qui se tient sur les boucles de chaque route
local attente = {false, false} -- true quand quelque chose attend sur une route
local muets = {} -- noms des blocs qui n'ont pas répondu
local function regler_route(i, aspect)
aspects[i] = aspect
for _, nom in ipairs(ROUTES[i].feux) do
local r = pcall(peripheral.call, nom, "set", aspect)
if r.ok then muets[nom] = nil else muets[nom] = true end
end
end
local function lire_boucles(i)
local total, toutes_ok = 0, true
for _, nom in ipairs(ROUTES[i].boucles) do
local r = pcall(peripheral.call, nom, "count")
if r.ok then
total = total + r.value
muets[nom] = nil
else
muets[nom] = true
toutes_ok = false
end
end
comptes[i] = total
attente[i] = total > 0 or not toutes_ok
endUne boucle qui ne répond pas compte comme une voiture qui attend. C'est le choix prudent : la route continue d'avoir son tour à intervalles réguliers, au lieu de ne plus jamais avoir le vert parce que son détecteur est cassé.
Les aspects qu'un Feu tricolore comprend sont "off", "red", "red_amber", "amber", "green" et "flashing" (orange clignotant). Tout autre mot arrête l'appel avec bad aspect 'yellow' (off, red, red_amber, amber, green, flashing).
3. La machine à états
L'état tient en trois variables : la phase, la route qui a (ou va avoir) le vert, et le moment où la phase a commencé, lu avec os.clock() (les secondes depuis le démarrage de l'ordinateur). entrer passe à une phase et note l'heure :
local phase = "vert" -- "vert", "orange", "tout_rouge", "rouge_orange" ou "nuit"
local passe = 1 -- la route qui a, ou va avoir, le vert
local depuis = 0 -- os.clock() au début de la phase
local force = false -- clignotant forcé à la main (la touche f)
local function entrer(nouvelle)
phase = nouvelle
depuis = os.clock()
end
local function est_la_nuit()
local t = os.day_time()
return force or (t >= DEBUT_NUIT and t < FIN_NUIT)
endetape tourne quatre fois par seconde. Elle lit les boucles, puis regarde la phase en cours :
| phase | feux de la route passe | passe à la suite quand | vers |
|---|---|---|---|
vert | vert | l'autre route attend et passe est vide (ou VERT_MAX est écoulé), après VERT_MIN | orange |
orange | orange | ORANGE secondes sont écoulées | tout_rouge |
tout_rouge | rouge | TOUT_ROUGE secondes sont écoulées (l'autre route devient passe) | rouge_orange |
rouge_orange | rouge et orange | ROUGE_ORANGE secondes sont écoulées | vert |
nuit | clignotant | la nuit finit | tout_rouge |
local function etape()
lire_boucles(1)
lire_boucles(2)
local autre = 3 - passe
local ecoule = os.clock() - depuis
if est_la_nuit() then
if phase ~= "nuit" then
regler_route(1, "flashing")
regler_route(2, "flashing")
entrer("nuit")
end
elseif phase == "nuit" then
regler_route(1, "red")
regler_route(2, "red")
passe = 2 -- pour que la route principale ait le prochain vert
entrer("tout_rouge")
elseif phase == "vert" then
local changer = false
if ecoule >= VERT_MIN then
if attente[autre] and (not attente[passe] or ecoule >= VERT_MAX) then changer = true end
if passe ~= 1 and not attente[passe] then changer = true end -- retour à la route principale
end
if changer then
regler_route(passe, "amber")
entrer("orange")
end
elseif phase == "orange" and ecoule >= ORANGE then
regler_route(passe, "red")
entrer("tout_rouge")
elseif phase == "tout_rouge" and ecoule >= TOUT_ROUGE then
passe = autre
regler_route(passe, "red_amber")
entrer("rouge_orange")
elseif phase == "rouge_orange" and ecoule >= ROUGE_ORANGE then
regler_route(passe, "green")
entrer("vert")
end
end3 - passe est l'autre route : 2 quand passe vaut 1, 1 quand passe vaut 2. Les feux ne changent que quand la phase change : le reste du temps, etape ne coûte presque rien.
4. L'écran
La moitié gauche de l'écran liste les routes avec leur aspect et leur boucle ; la moitié droite dessine les deux têtes de feux avec gfx : un corps gris et trois lampes rondes, allumées ou noires. En mode clignotant, la lampe orange suit os.clock(), allumée une demi-seconde, éteinte une demi-seconde.
local TEXTE_ASPECT = {off = "ÉTEINT", red = "ROUGE", red_amber = "ROUGE+ORANGE", amber = "ORANGE",
green = "VERT", flashing = "CLIGNOTANT"}
local COULEUR_ASPECT = {off = "gray", red = "red", red_amber = "orange", amber = "orange",
green = "lime", flashing = "orange"}
local TEXTE_PHASE = {vert = "vert", orange = "orange", tout_rouge = "tout rouge",
rouge_orange = "rouge et orange", nuit = "nuit, clignotant"}
local function heure_texte()
local t = (os.day_time() + 6000) % 24000 -- 0 = 6 h du matin
return string.format("%02d:%02d", t // 1000, t % 1000 * 60 // 1000)
end
local function lampe(x, y, allumee, couleur)
if allumee then gfx.circle(x, y, 8, couleur, true) else gfx.circle(x, y, 8, "black", true) end
end
local function dessiner_feu(x, aspect, etiquette)
local clignote = math.floor(os.clock() * 2) % 2 == 0
gfx.rect(x, 14, 26, 62, "gray", true)
lampe(x + 13, 25, aspect == "red" or aspect == "red_amber", "red")
lampe(x + 13, 45, aspect == "amber" or aspect == "red_amber" or (aspect == "flashing" and clignote), "orange")
lampe(x + 13, 65, aspect == "green", "lime")
gfx.text(x + 11, 81, etiquette, "light_gray")
end
local function dessiner()
local l = term.get_size().w
term.set_bg(term.colors.black)
term.clear()
gfx.clear()
term.set_cursor(1, 1)
term.set_bg(term.colors.blue)
term.set_fg(term.colors.white)
term.write(string.format(" %-" .. (l - 8) .. "s%6s ", "CARREFOUR", heure_texte()))
term.set_bg(term.colors.black)
for i, route in ipairs(ROUTES) do
term.set_cursor(2, i * 3)
term.set_fg(term.colors.white)
term.write(i .. " " .. route.nom)
term.set_cursor(4, i * 3 + 1)
term.set_fg(term.colors[COULEUR_ASPECT[aspects[i]]])
term.write(string.format("%-12s", TEXTE_ASPECT[aspects[i]]))
term.set_fg(term.colors.light_gray)
term.write("boucle : " .. comptes[i])
dessiner_feu(l * 6 - 84 + (i - 1) * 40, aspects[i], tostring(i))
end
term.set_cursor(2, 9)
term.set_fg(term.colors.light_gray)
term.write(string.format("%s, %d s", TEXTE_PHASE[phase], math.floor(os.clock() - depuis)))
local absents = 0
for _, _ in pairs(muets) do absents = absents + 1 end
if absents > 0 then
term.set_cursor(2, 10)
term.set_fg(term.colors.red)
term.write(absents .. " bloc(s) muet(s)")
end
term.set_cursor(2, 11)
term.set_fg(term.colors.gray)
term.write("f : clignotant oui/non")
end5. Le démarrage et la boucle principale
Au démarrage, la route principale reçoit le vert et la route secondaire le rouge. La boucle attend son minuteur, et la touche f, qui force le mode clignotant à la main : pratique pendant des travaux.
regler_route(1, "green")
regler_route(2, "red")
entrer("vert")
local minuteur = os.start_timer(PAS)
while true do
local e = os.pull_event()
if e.name == "timer" and e.id == minuteur then
etape()
dessiner()
minuteur = os.start_timer(PAS)
elseif e.name == "char" and e.char == "f" then
force = not force
end
endLe programme complet
-- Carrefour intelligent : deux routes, chacune avec ses Feux tricolores et ses
-- Boucles de détection. La route principale garde le vert jusqu'à ce que
-- quelque chose attende sur la route secondaire ; la nuit, tous les feux
-- clignotent à l'orange. "startup liste" affiche les noms des feux et des
-- boucles du câble.
local ROUTES = {
{nom = "Route principale", feux = {"traffic_light@10,65,4"}, boucles = {"inductive_loop@10,63,9"}},
{nom = "Route secondaire", feux = {"traffic_light@14,65,0"}, boucles = {"inductive_loop@19,63,0"}},
}
local VERT_MIN = 8 -- secondes de vert avant que le feu puisse changer
local VERT_MAX = 30 -- secondes de vert au plus pendant que l'autre route attend
local ORANGE = 3 -- secondes d'orange
local TOUT_ROUGE = 1 -- secondes avec les deux routes au rouge, pour vider le carrefour
local ROUGE_ORANGE = 1.5 -- secondes de rouge et orange avant le vert
local DEBUT_NUIT = 13000 -- os.day_time() à la tombée de la nuit (19 h)
local FIN_NUIT = 23000 -- os.day_time() à l'aube (5 h)
local PAS = 0.25 -- secondes entre deux coups d'œil aux boucles
if arg ~= nil and arg[1] == "liste" then
for _, sorte in ipairs({"traffic_light", "inductive_loop"}) do
for _, nom in ipairs(peripheral.list(sorte)) do print(nom) end
end
return
end
local TEXTE_ASPECT = {off = "ÉTEINT", red = "ROUGE", red_amber = "ROUGE+ORANGE", amber = "ORANGE",
green = "VERT", flashing = "CLIGNOTANT"}
local COULEUR_ASPECT = {off = "gray", red = "red", red_amber = "orange", amber = "orange",
green = "lime", flashing = "orange"}
local TEXTE_PHASE = {vert = "vert", orange = "orange", tout_rouge = "tout rouge",
rouge_orange = "rouge et orange", nuit = "nuit, clignotant"}
local aspects = {"off", "off"} -- ce que montre chaque route
local comptes = {0, 0} -- ce qui se tient sur les boucles de chaque route
local attente = {false, false} -- true quand quelque chose attend sur une route
local muets = {} -- noms des blocs qui n'ont pas répondu
local phase = "vert" -- "vert", "orange", "tout_rouge", "rouge_orange" ou "nuit"
local passe = 1 -- la route qui a, ou va avoir, le vert
local depuis = 0 -- os.clock() au début de la phase
local force = false -- clignotant forcé à la main (la touche f)
-- Blocs ---------------------------------------------------------------------------------
local function regler_route(i, aspect)
aspects[i] = aspect
for _, nom in ipairs(ROUTES[i].feux) do
local r = pcall(peripheral.call, nom, "set", aspect)
if r.ok then muets[nom] = nil else muets[nom] = true end
end
end
local function lire_boucles(i)
local total, toutes_ok = 0, true
for _, nom in ipairs(ROUTES[i].boucles) do
local r = pcall(peripheral.call, nom, "count")
if r.ok then
total = total + r.value
muets[nom] = nil
else
muets[nom] = true
toutes_ok = false
end
end
comptes[i] = total
attente[i] = total > 0 or not toutes_ok
end
-- Machine à états -----------------------------------------------------------------------
local function entrer(nouvelle)
phase = nouvelle
depuis = os.clock()
end
local function est_la_nuit()
local t = os.day_time()
return force or (t >= DEBUT_NUIT and t < FIN_NUIT)
end
local function etape()
lire_boucles(1)
lire_boucles(2)
local autre = 3 - passe
local ecoule = os.clock() - depuis
if est_la_nuit() then
if phase ~= "nuit" then
regler_route(1, "flashing")
regler_route(2, "flashing")
entrer("nuit")
end
elseif phase == "nuit" then
regler_route(1, "red")
regler_route(2, "red")
passe = 2 -- pour que la route principale ait le prochain vert
entrer("tout_rouge")
elseif phase == "vert" then
local changer = false
if ecoule >= VERT_MIN then
if attente[autre] and (not attente[passe] or ecoule >= VERT_MAX) then changer = true end
if passe ~= 1 and not attente[passe] then changer = true end -- retour à la route principale
end
if changer then
regler_route(passe, "amber")
entrer("orange")
end
elseif phase == "orange" and ecoule >= ORANGE then
regler_route(passe, "red")
entrer("tout_rouge")
elseif phase == "tout_rouge" and ecoule >= TOUT_ROUGE then
passe = autre
regler_route(passe, "red_amber")
entrer("rouge_orange")
elseif phase == "rouge_orange" and ecoule >= ROUGE_ORANGE then
regler_route(passe, "green")
entrer("vert")
end
end
-- Écran ------------------------------------------------------------------------------------
local function heure_texte()
local t = (os.day_time() + 6000) % 24000 -- 0 = 6 h du matin
return string.format("%02d:%02d", t // 1000, t % 1000 * 60 // 1000)
end
local function lampe(x, y, allumee, couleur)
if allumee then gfx.circle(x, y, 8, couleur, true) else gfx.circle(x, y, 8, "black", true) end
end
local function dessiner_feu(x, aspect, etiquette)
local clignote = math.floor(os.clock() * 2) % 2 == 0
gfx.rect(x, 14, 26, 62, "gray", true)
lampe(x + 13, 25, aspect == "red" or aspect == "red_amber", "red")
lampe(x + 13, 45, aspect == "amber" or aspect == "red_amber" or (aspect == "flashing" and clignote), "orange")
lampe(x + 13, 65, aspect == "green", "lime")
gfx.text(x + 11, 81, etiquette, "light_gray")
end
local function dessiner()
local l = term.get_size().w
term.set_bg(term.colors.black)
term.clear()
gfx.clear()
term.set_cursor(1, 1)
term.set_bg(term.colors.blue)
term.set_fg(term.colors.white)
term.write(string.format(" %-" .. (l - 8) .. "s%6s ", "CARREFOUR", heure_texte()))
term.set_bg(term.colors.black)
for i, route in ipairs(ROUTES) do
term.set_cursor(2, i * 3)
term.set_fg(term.colors.white)
term.write(i .. " " .. route.nom)
term.set_cursor(4, i * 3 + 1)
term.set_fg(term.colors[COULEUR_ASPECT[aspects[i]]])
term.write(string.format("%-12s", TEXTE_ASPECT[aspects[i]]))
term.set_fg(term.colors.light_gray)
term.write("boucle : " .. comptes[i])
dessiner_feu(l * 6 - 84 + (i - 1) * 40, aspects[i], tostring(i))
end
term.set_cursor(2, 9)
term.set_fg(term.colors.light_gray)
term.write(string.format("%s, %d s", TEXTE_PHASE[phase], math.floor(os.clock() - depuis)))
local absents = 0
for _, _ in pairs(muets) do absents = absents + 1 end
if absents > 0 then
term.set_cursor(2, 10)
term.set_fg(term.colors.red)
term.write(absents .. " bloc(s) muet(s)")
end
term.set_cursor(2, 11)
term.set_fg(term.colors.gray)
term.write("f : clignotant oui/non")
end
-- Boucle principale -------------------------------------------------------------------------
regler_route(1, "green")
regler_route(2, "red")
entrer("vert")
local minuteur = os.start_timer(PAS)
while true do
local e = os.pull_event()
if e.name == "timer" and e.id == minuteur then
etape()
dessiner()
minuteur = os.start_timer(PAS)
elseif e.name == "char" and e.char == "f" then
force = not force
end
endÀ quoi ressemble l'écran
Le code de dessin du programme sur le Microcontrôleur, au moment où la route principale passe à l'orange parce qu'un wagonnet attend sur la boucle de la route secondaire :
local ROUTES = {{nom = "Route principale"}, {nom = "Route secondaire"}}
local TEXTE_ASPECT = {off = "ÉTEINT", red = "ROUGE", red_amber = "ROUGE+ORANGE", amber = "ORANGE",
green = "VERT", flashing = "CLIGNOTANT"}
local COULEUR_ASPECT = {off = "gray", red = "red", red_amber = "orange", amber = "orange",
green = "lime", flashing = "orange"}
local TEXTE_PHASE = {vert = "vert", orange = "orange", tout_rouge = "tout rouge",
rouge_orange = "rouge et orange", nuit = "nuit, clignotant"}
local aspects = {"amber", "red"}
local comptes = {0, 1}
local muets = {}
local phase = "orange"
local depuis = os.clock() - 1
local function heure_texte()
local t = (os.day_time() + 6000) % 24000
return string.format("%02d:%02d", t // 1000, t % 1000 * 60 // 1000)
end
local function lampe(x, y, allumee, couleur)
if allumee then gfx.circle(x, y, 8, couleur, true) else gfx.circle(x, y, 8, "black", true) end
end
local function dessiner_feu(x, aspect, etiquette)
local clignote = math.floor(os.clock() * 2) % 2 == 0
gfx.rect(x, 14, 26, 62, "gray", true)
lampe(x + 13, 25, aspect == "red" or aspect == "red_amber", "red")
lampe(x + 13, 45, aspect == "amber" or aspect == "red_amber" or (aspect == "flashing" and clignote), "orange")
lampe(x + 13, 65, aspect == "green", "lime")
gfx.text(x + 11, 81, etiquette, "light_gray")
end
local function dessiner()
local l = term.get_size().w
term.set_bg(term.colors.black)
term.clear()
gfx.clear()
term.set_cursor(1, 1)
term.set_bg(term.colors.blue)
term.set_fg(term.colors.white)
term.write(string.format(" %-" .. (l - 8) .. "s%6s ", "CARREFOUR", heure_texte()))
term.set_bg(term.colors.black)
for i, route in ipairs(ROUTES) do
term.set_cursor(2, i * 3)
term.set_fg(term.colors.white)
term.write(i .. " " .. route.nom)
term.set_cursor(4, i * 3 + 1)
term.set_fg(term.colors[COULEUR_ASPECT[aspects[i]]])
term.write(string.format("%-12s", TEXTE_ASPECT[aspects[i]]))
term.set_fg(term.colors.light_gray)
term.write("boucle : " .. comptes[i])
dessiner_feu(l * 6 - 84 + (i - 1) * 40, aspects[i], tostring(i))
end
term.set_cursor(2, 9)
term.set_fg(term.colors.light_gray)
term.write(string.format("%s, %d s", TEXTE_PHASE[phase], math.floor(os.clock() - depuis)))
local absents = 0
for _, _ in pairs(muets) do absents = absents + 1 end
if absents > 0 then
term.set_cursor(2, 10)
term.set_fg(term.colors.red)
term.write(absents .. " bloc(s) muet(s)")
end
term.set_cursor(2, 11)
term.set_fg(term.colors.gray)
term.write("f : clignotant oui/non")
end
dessiner()
L'essayer
- Construisez le carrefour, lancez
startup listeet recopiez les noms dansROUTES: le feu et la boucle de la route principale sur la première ligne, ceux de la route secondaire sur la seconde. - Lancez
startup. La route principale est au vert, la route secondaire au rouge, et les deux boucles lisent 0. - Montez sur la boucle de la route secondaire et attendez. Au bout de
VERT_MINsecondes au plus, la route principale passe à l'orange, puis au rouge ; une seconde plus tard, la route secondaire montre rouge et orange, puis vert. - Descendez de la boucle : après
VERT_MINsecondes de vert, la route secondaire repasse par l'orange, et la route principale retrouve le vert. - Occupez les deux boucles (avec un ami, ou une vache laissée sur l'une) : le vert alterne, chaque route le gardant au plus
VERT_MAXsecondes. - Tapez
fdans le terminal : les deux feux clignotent à l'orange. Tapezfde nouveau, ou attendez la nuit (/time set night) puis le matin : le carrefour repasse au rouge des deux côtés, puis au vert sur la route principale. - Cassez la boucle de la route secondaire : l'écran signale
1 bloc(s) muet(s), et la route secondaire a toujours son tour à intervalles réguliers.
Variantes
- Un bouton pour piétons. Un bouton au bord de la route, relié à l'ordinateur : dans
etape, traitezrs.get("left") > 0comme une voiture qui attend sur la route secondaire. - Compter la circulation. Comptez les fronts montants de chaque boucle (
comptes[i]qui passe de 0 à plus) et ajoutez-les à un fichier toutes les heures : voirEnregistreur de données et graphique. - Quatre routes. Avec quatre arrivées, gardez les deux paires qui ne se croisent jamais (nord-sud, est-ouest) et donnez à chaque paire une liste de feux et de boucles : le programme ne change pas,
ROUTESa juste des listes plus longues. - Rendre les feux à la redstone. Avant de quitter le programme, appelez
traffic_light.release()sur chaque feu (peripheral.call(nom, "release")) : les feux suivent de nouveau la redstone, comme un Feu tricolore sans ordinateur. Sans cet appel, un feu garde le dernier aspect réglé par le programme, même après un redémarrage ou une fois l'ordinateur cassé. - Un passage à niveau. Remplacez la route secondaire par une voie de wagonnets : une Boucle de détection sous les rails voit chaque wagonnet, et la route passe au rouge quand l'un d'eux arrive.