Porte à code
Un clavier sur le terminal qui ouvre une porte de Create quelques secondes, se bloque après des codes erronés, garde le code dans un fichier et note chaque essai.
Une porte coulissante de Create devant votre atelier, et à côté, un petit ordinateur encastré dans le mur. Qui connaît le code le tape, la porte s'ouvre cinq secondes et se referme derrière lui. Trois codes erronés de suite bloquent le clavier trente secondes, puis une minute, puis deux. Chaque essai est écrit dans un journal, avec son heure.
Le projet réunit le côté saisie de Brass : read avec un masque pour taper un secret (read()), des minuteurs pour décompter sans rien rater (os.start_timer()), des fichiers pour garder le code et le journal (fs), et les deux façons d'envoyer un signal à une contraption de Create : une sortie redstone (rs) et une fréquence de Liaison de redstone (link).
Ce qu'il vous faut
- Un ordinateur avec un clavier et un écran qui dessine : n'importe quel ordinateur à partir de l'Ordinateur à transistors (le cadenas est dessiné avec
gfx). Le Microcontrôleur est le plus adapté : assez petit pour se cacher dans un mur, avec son écran de 40 x 12, et ses fichiers vivent dans une mémoire soudée que personne ne peut emporter comme une disquette. Il est entraîné par le dessous. - Une porte : une porte coulissante de Create (Porte d'andésite, Porte de laiton, Porte de train...), une porte en fer, ou tout un portail sur un Piston mécanique. Chacune s'ouvre tant qu'elle reçoit de la redstone.
- Soit la porte contre l'ordinateur, sur son côté
leftvu en face de l'écran ; soit une Liaison de redstone en réception à côté de la porte, avec un lingot de fer et une porte en fer dans ses deux emplacements de fréquence. Le programme pilote les deux, l'un ou l'autre suffit. - Si vous voulez, un moniteur dans le mur, dehors : un clic droit dessus ouvre le terminal de l'ordinateur, et le visiteur peut taper le code sans atteindre l'ordinateur lui-même.
Vu de face :
+-------+---------+
| porte | | une porte coulissante de Create, sur deux blocs
| (haut)| |
+-------+---------+
| porte | Micro- | <- le Microcontrôleur, écran vers vous,
| | contrôl.| la porte contre son côté gauche
+-------+---------+
arbre sous le MicrocontrôleurLe programme, étape par étape
1. Les réglages
local FICHIER_CODE = "porte/code" -- le code, en texte simple
local FICHIER_JOURNAL = "porte/journal" -- une ligne par essai
local COTE_PORTE = "left" -- sortie redstone vers la porte (nil : aucune)
local LIEN_PORTE = {"minecraft:iron_ingot", "minecraft:iron_door"} -- Liaison de redstone (nil : aucune)
local SECONDES_OUVERTE = 5 -- combien de temps la porte reste ouverte
local ESSAIS_MAX = 3 -- codes erronés avant le blocage du clavier
local SECONDES_BLOCAGE = 30 -- le premier blocage ; il double à chaque fois, jusqu'à 10 minutes
local JOURNAL_MAX = 4000 -- octets ; au-delà, le journal devient porte/journal.oldLe code n'est pas écrit dans le programme : il vit dans porte/code. Vous pouvez ainsi donner le programme à des amis sans donner votre code, et changer le code sans toucher au programme.
2. La porte, le journal et le fichier du code
La porte s'ouvre et se ferme à un seul endroit. rs.set et link.set acceptent tous deux true et false aussi bien qu'une puissance de 0 à 15 :
local function regler_porte(ouverte)
if COTE_PORTE then rs.set(COTE_PORTE, ouverte) end
if LIEN_PORTE then link.set(LIEN_PORTE[1], LIEN_PORTE[2], ouverte) end
endLe journal reçoit une ligne par évènement, avec l'heure du jour. fs.append ajoute à la fin du fichier et le crée la première fois. Pour que le journal ne remplisse jamais le support, il est renommé porte/journal.old quand il dépasse JOURNAL_MAX octets (fs.move remplace un ancien porte/journal.old). fs.size renvoie nil tant que le fichier n'existe pas.
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 noter(texte)
local taille = fs.size(FICHIER_JOURNAL)
if taille ~= nil and taille > JOURNAL_MAX then fs.move(FICHIER_JOURNAL, FICHIER_JOURNAL .. ".old") end
fs.append(FICHIER_JOURNAL, heure_texte() .. " " .. texte .. "\n")
endLe fichier du code est lu une fois au démarrage. fs.read renvoie nil quand le fichier manque, et trim enlève un saut de ligne laissé par l'éditeur :
local function charger_code()
local texte = fs.read(FICHIER_CODE)
if texte ~= nil and texte:trim() ~= "" then return texte:trim() end
return nil
endLe programme n'écrit jamais un code tapé dans le journal, seulement s'il était juste. Un code erroné est souvent le bon avec une faute de frappe, et un journal que n'importe qui peut afficher avec cat ne doit pas le contenir.
3. L'écran et le cadenas
L'écran est construit pour les 40 colonnes du Microcontrôleur et s'adapte aux écrans plus larges : le cadenas est placé d'après la largeur donnée par term.get_size. Il est dessiné avec gfx : un anneau pour l'anse (gfx.circle avec une épaisseur), dont la moitié basse est effacée avec la couleur "none", deux branches, le corps et un trou de serrure. Ouvert, l'anse se soulève et sa branche droite sort du corps.
local message, couleur_message = "", "white"
local porte_ouverte = false
local function dessiner_cadenas(ouvert)
local x = term.get_size().w * 6 - 44
local y = 22
local couleur, levee, branche_droite = "red", 0, 9
if ouvert then couleur, levee, branche_droite = "lime", 7, 3 end
gfx.rect(x - 2, y - 10, 36, 56, "none", true)
gfx.circle(x + 15, y + 10 - levee, 10, couleur, 3)
gfx.rect(x + 2, y + 11 - levee, 27, 10, "none", true)
gfx.rect(x + 5, y + 10 - levee, 3, 9 + levee, couleur, true)
gfx.rect(x + 23, y + 10 - levee, 3, branche_droite, couleur, true)
gfx.rect(x, y + 18, 31, 24, couleur, true)
gfx.circle(x + 15, y + 27, 3, "black", true)
gfx.rect(x + 14, y + 29, 3, 7, "black", true)
end
local function dessiner_ecran(essais_restants)
local l = term.get_size().w
term.set_bg(term.colors.black)
term.clear()
term.set_cursor(1, 1)
term.set_bg(term.colors.blue)
term.set_fg(term.colors.white)
term.write(string.format(" %-" .. (l - 2) .. "s ", "CODE DE LA PORTE"))
term.set_bg(term.colors.black)
term.set_cursor(3, 3)
term.set_fg(term.colors.light_gray)
term.write("Tapez le code, puis Entrée.")
term.set_cursor(3, 5)
term.set_fg(term.colors.white)
term.write("Code :")
gfx.line(55, 46, 180, 46, "gray")
term.set_cursor(3, 7)
term.set_fg(term.colors.gray)
term.write("Essais restants : " .. essais_restants)
term.set_cursor(3, 9)
term.set_fg(term.colors[couleur_message])
term.write(message)
term.set_cursor(3, 11)
term.set_fg(term.colors.gray)
term.write("Chaque essai est noté.")
dessiner_cadenas(porte_ouverte)
endLa ligne grise sous le champ du code est un gfx.line du pixel 55 au pixel 180, un pixel sous la cinquième ligne de texte : une ligne de texte fait 9 pixels de haut, la ligne 5 couvre donc les pixels 37 à 45.
4. Décompter avec des minuteurs
L'ouverture de la porte et le blocage attendent tous deux un certain nombre de secondes en affichant ce qui reste. Chaque seconde est un minuteur. os.pull_event("timer") jette tous les autres évènements pendant qu'il attend : les touches qu'un visiteur martèle pendant un blocage sont perdues, et non gardées pour le prochain read. C'est exactement ce qu'il faut pour un blocage.
local function afficher_message(texte, couleur)
message, couleur_message = texte, couleur
term.set_cursor(1, 9)
term.set_bg(term.colors.black)
term.clear_line()
term.set_cursor(3, 9)
term.set_fg(term.colors[couleur])
term.write(texte)
end
local function compte_a_rebours(secondes, texte, couleur)
for reste = secondes, 1, -1 do
afficher_message(texte .. " " .. reste .. " s", couleur)
local id = os.start_timer(1)
local e = os.pull_event("timer")
while e.id ~= id do e = os.pull_event("timer") end
end
endVérifier e.id garantit que le programme réagit à son minuteur, pas à un minuteur lancé ailleurs. La page Évènements explique pourquoi les programmes préfèrent les minuteurs à sleep quand ils ont plusieurs choses à surveiller.
5. Choisir le code la première fois
Quand porte/code n'existe pas, le programme demande un code deux fois avant de l'enregistrer. read("*") affiche une étoile pour chaque caractère tapé. fs.write crée le dossier porte au passage.
local function choisir_code()
while true do
dessiner_ecran(ESSAIS_MAX)
term.set_cursor(3, 3)
term.clear_line()
term.set_cursor(3, 3)
term.set_fg(term.colors.yellow)
term.write("Aucun code : choisissez-en un.")
term.set_fg(term.colors.white)
term.set_cursor(10, 5)
local premier = read("*"):trim()
term.set_cursor(3, 7)
term.clear_line()
term.set_cursor(3, 7)
term.write("Encore :")
term.set_cursor(12, 7)
local second = read("*"):trim()
if premier ~= "" and premier == second then
fs.write(FICHIER_CODE, premier)
noter("nouveau code choisi")
return premier
end
compte_a_rebours(3, "Les codes diffèrent. Encore dans", "red")
end
end6. La boucle du clavier
La boucle principale dessine l'écran, attend une ligne avec read("*") et la compare au code. Ici, read est le bon outil : il gère les touches d'édition (Retour arrière, les flèches) et ne rend la main que quand le visiteur appuie sur Entrée.
gfx.clear()
regler_porte(false)
local code = charger_code()
if code == nil then code = choisir_code() end
local erreurs = 0
local blocage = SECONDES_BLOCAGE
while true do
dessiner_ecran(ESSAIS_MAX - erreurs)
term.set_cursor(10, 5)
term.set_fg(term.colors.white)
local tape = read("*"):trim()
if tape == code then
erreurs = 0
blocage = SECONDES_BLOCAGE
noter("porte ouverte")
regler_porte(true)
porte_ouverte = true
dessiner_ecran(ESSAIS_MAX)
compte_a_rebours(SECONDES_OUVERTE, "Bienvenue ! Fermeture dans", "lime")
regler_porte(false)
porte_ouverte = false
message = ""
elseif tape ~= "" then
erreurs = erreurs + 1
noter("code erroné " .. erreurs .. "/" .. ESSAIS_MAX)
if erreurs < ESSAIS_MAX then
message, couleur_message = "Code erroné.", "red"
else
noter("clavier bloqué " .. blocage .. " s")
dessiner_ecran(0)
compte_a_rebours(blocage, "Bloqué. Réessayez dans", "red")
blocage = math.min(blocage * 2, 600)
erreurs = 0
message = ""
end
end
endUne ligne vide (Entrée seule) ne fait rien : une touche pressée par erreur ne coûte jamais un essai. Le blocage double après chaque série de codes erronés et revient à 30 secondes après le bon code suivant.
Le programme complet
-- Porte à code : un clavier sur le terminal ouvre une porte de Create
-- quelques secondes. Les codes erronés bloquent le clavier ; chaque essai
-- est noté dans porte/journal.
local FICHIER_CODE = "porte/code" -- le code, en texte simple
local FICHIER_JOURNAL = "porte/journal" -- une ligne par essai
local COTE_PORTE = "left" -- sortie redstone vers la porte (nil : aucune)
local LIEN_PORTE = {"minecraft:iron_ingot", "minecraft:iron_door"} -- Liaison de redstone (nil : aucune)
local SECONDES_OUVERTE = 5 -- combien de temps la porte reste ouverte
local ESSAIS_MAX = 3 -- codes erronés avant le blocage du clavier
local SECONDES_BLOCAGE = 30 -- le premier blocage ; il double à chaque fois, jusqu'à 10 minutes
local JOURNAL_MAX = 4000 -- octets ; au-delà, le journal devient porte/journal.old
local message, couleur_message = "", "white"
local porte_ouverte = false
-- Porte, journal, code ---------------------------------------------------------------
local function regler_porte(ouverte)
if COTE_PORTE then rs.set(COTE_PORTE, ouverte) end
if LIEN_PORTE then link.set(LIEN_PORTE[1], LIEN_PORTE[2], ouverte) end
end
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 noter(texte)
local taille = fs.size(FICHIER_JOURNAL)
if taille ~= nil and taille > JOURNAL_MAX then fs.move(FICHIER_JOURNAL, FICHIER_JOURNAL .. ".old") end
fs.append(FICHIER_JOURNAL, heure_texte() .. " " .. texte .. "\n")
end
local function charger_code()
local texte = fs.read(FICHIER_CODE)
if texte ~= nil and texte:trim() ~= "" then return texte:trim() end
return nil
end
-- Écran -------------------------------------------------------------------------------
local function dessiner_cadenas(ouvert)
local x = term.get_size().w * 6 - 44
local y = 22
local couleur, levee, branche_droite = "red", 0, 9
if ouvert then couleur, levee, branche_droite = "lime", 7, 3 end
gfx.rect(x - 2, y - 10, 36, 56, "none", true)
gfx.circle(x + 15, y + 10 - levee, 10, couleur, 3)
gfx.rect(x + 2, y + 11 - levee, 27, 10, "none", true)
gfx.rect(x + 5, y + 10 - levee, 3, 9 + levee, couleur, true)
gfx.rect(x + 23, y + 10 - levee, 3, branche_droite, couleur, true)
gfx.rect(x, y + 18, 31, 24, couleur, true)
gfx.circle(x + 15, y + 27, 3, "black", true)
gfx.rect(x + 14, y + 29, 3, 7, "black", true)
end
local function dessiner_ecran(essais_restants)
local l = term.get_size().w
term.set_bg(term.colors.black)
term.clear()
term.set_cursor(1, 1)
term.set_bg(term.colors.blue)
term.set_fg(term.colors.white)
term.write(string.format(" %-" .. (l - 2) .. "s ", "CODE DE LA PORTE"))
term.set_bg(term.colors.black)
term.set_cursor(3, 3)
term.set_fg(term.colors.light_gray)
term.write("Tapez le code, puis Entrée.")
term.set_cursor(3, 5)
term.set_fg(term.colors.white)
term.write("Code :")
gfx.line(55, 46, 180, 46, "gray")
term.set_cursor(3, 7)
term.set_fg(term.colors.gray)
term.write("Essais restants : " .. essais_restants)
term.set_cursor(3, 9)
term.set_fg(term.colors[couleur_message])
term.write(message)
term.set_cursor(3, 11)
term.set_fg(term.colors.gray)
term.write("Chaque essai est noté.")
dessiner_cadenas(porte_ouverte)
end
local function afficher_message(texte, couleur)
message, couleur_message = texte, couleur
term.set_cursor(1, 9)
term.set_bg(term.colors.black)
term.clear_line()
term.set_cursor(3, 9)
term.set_fg(term.colors[couleur])
term.write(texte)
end
-- Attente ------------------------------------------------------------------------------
local function compte_a_rebours(secondes, texte, couleur)
for reste = secondes, 1, -1 do
afficher_message(texte .. " " .. reste .. " s", couleur)
local id = os.start_timer(1)
local e = os.pull_event("timer")
while e.id ~= id do e = os.pull_event("timer") end
end
end
local function choisir_code()
while true do
dessiner_ecran(ESSAIS_MAX)
term.set_cursor(3, 3)
term.clear_line()
term.set_cursor(3, 3)
term.set_fg(term.colors.yellow)
term.write("Aucun code : choisissez-en un.")
term.set_fg(term.colors.white)
term.set_cursor(10, 5)
local premier = read("*"):trim()
term.set_cursor(3, 7)
term.clear_line()
term.set_cursor(3, 7)
term.write("Encore :")
term.set_cursor(12, 7)
local second = read("*"):trim()
if premier ~= "" and premier == second then
fs.write(FICHIER_CODE, premier)
noter("nouveau code choisi")
return premier
end
compte_a_rebours(3, "Les codes diffèrent. Encore dans", "red")
end
end
-- Clavier ------------------------------------------------------------------------------
gfx.clear()
regler_porte(false)
local code = charger_code()
if code == nil then code = choisir_code() end
local erreurs = 0
local blocage = SECONDES_BLOCAGE
while true do
dessiner_ecran(ESSAIS_MAX - erreurs)
term.set_cursor(10, 5)
term.set_fg(term.colors.white)
local tape = read("*"):trim()
if tape == code then
erreurs = 0
blocage = SECONDES_BLOCAGE
noter("porte ouverte")
regler_porte(true)
porte_ouverte = true
dessiner_ecran(ESSAIS_MAX)
compte_a_rebours(SECONDES_OUVERTE, "Bienvenue ! Fermeture dans", "lime")
regler_porte(false)
porte_ouverte = false
message = ""
elseif tape ~= "" then
erreurs = erreurs + 1
noter("code erroné " .. erreurs .. "/" .. ESSAIS_MAX)
if erreurs < ESSAIS_MAX then
message, couleur_message = "Code erroné.", "red"
else
noter("clavier bloqué " .. blocage .. " s")
dessiner_ecran(0)
compte_a_rebours(blocage, "Bloqué. Réessayez dans", "red")
blocage = math.min(blocage * 2, 600)
erreurs = 0
message = ""
end
end
endÀ quoi ressemble l'écran
Les fonctions de dessin du programme sur un Microcontrôleur, juste après un code erroné, avec quatre étoiles tapées pour l'essai suivant :
local ESSAIS_MAX = 3
local message, couleur_message = "Code erroné.", "red"
local porte_ouverte = false
local function dessiner_cadenas(ouvert)
local x = term.get_size().w * 6 - 44
local y = 22
local couleur, levee, branche_droite = "red", 0, 9
if ouvert then couleur, levee, branche_droite = "lime", 7, 3 end
gfx.rect(x - 2, y - 10, 36, 56, "none", true)
gfx.circle(x + 15, y + 10 - levee, 10, couleur, 3)
gfx.rect(x + 2, y + 11 - levee, 27, 10, "none", true)
gfx.rect(x + 5, y + 10 - levee, 3, 9 + levee, couleur, true)
gfx.rect(x + 23, y + 10 - levee, 3, branche_droite, couleur, true)
gfx.rect(x, y + 18, 31, 24, couleur, true)
gfx.circle(x + 15, y + 27, 3, "black", true)
gfx.rect(x + 14, y + 29, 3, 7, "black", true)
end
local function dessiner_ecran(essais_restants)
local l = term.get_size().w
term.set_bg(term.colors.black)
term.clear()
term.set_cursor(1, 1)
term.set_bg(term.colors.blue)
term.set_fg(term.colors.white)
term.write(string.format(" %-" .. (l - 2) .. "s ", "CODE DE LA PORTE"))
term.set_bg(term.colors.black)
term.set_cursor(3, 3)
term.set_fg(term.colors.light_gray)
term.write("Tapez le code, puis Entrée.")
term.set_cursor(3, 5)
term.set_fg(term.colors.white)
term.write("Code :")
gfx.line(55, 46, 180, 46, "gray")
term.set_cursor(3, 7)
term.set_fg(term.colors.gray)
term.write("Essais restants : " .. essais_restants)
term.set_cursor(3, 9)
term.set_fg(term.colors[couleur_message])
term.write(message)
term.set_cursor(3, 11)
term.set_fg(term.colors.gray)
term.write("Chaque essai est noté.")
dessiner_cadenas(porte_ouverte)
end
gfx.clear()
dessiner_ecran(ESSAIS_MAX - 1)
term.set_cursor(10, 5)
term.set_fg(term.colors.white)
term.write("****")
Quand le code est juste, le même écran passe le cadenas au vert et ouvre son anse pendant que la porte est ouverte :
local message, couleur_message = "Bienvenue ! Fermeture dans 4 s", "lime"
local porte_ouverte = true
local function dessiner_cadenas(ouvert)
local x = term.get_size().w * 6 - 44
local y = 22
local couleur, levee, branche_droite = "red", 0, 9
if ouvert then couleur, levee, branche_droite = "lime", 7, 3 end
gfx.rect(x - 2, y - 10, 36, 56, "none", true)
gfx.circle(x + 15, y + 10 - levee, 10, couleur, 3)
gfx.rect(x + 2, y + 11 - levee, 27, 10, "none", true)
gfx.rect(x + 5, y + 10 - levee, 3, 9 + levee, couleur, true)
gfx.rect(x + 23, y + 10 - levee, 3, branche_droite, couleur, true)
gfx.rect(x, y + 18, 31, 24, couleur, true)
gfx.circle(x + 15, y + 27, 3, "black", true)
gfx.rect(x + 14, y + 29, 3, 7, "black", true)
end
local function dessiner_ecran(essais_restants)
local l = term.get_size().w
term.set_bg(term.colors.black)
term.clear()
term.set_cursor(1, 1)
term.set_bg(term.colors.blue)
term.set_fg(term.colors.white)
term.write(string.format(" %-" .. (l - 2) .. "s ", "CODE DE LA PORTE"))
term.set_bg(term.colors.black)
term.set_cursor(3, 3)
term.set_fg(term.colors.light_gray)
term.write("Tapez le code, puis Entrée.")
term.set_cursor(3, 5)
term.set_fg(term.colors.white)
term.write("Code :")
gfx.line(55, 46, 180, 46, "gray")
term.set_cursor(3, 7)
term.set_fg(term.colors.gray)
term.write("Essais restants : " .. essais_restants)
term.set_cursor(3, 9)
term.set_fg(term.colors[couleur_message])
term.write(message)
term.set_cursor(3, 11)
term.set_fg(term.colors.gray)
term.write("Chaque essai est noté.")
dessiner_cadenas(porte_ouverte)
end
gfx.clear()
dessiner_ecran(3)
L'essayer
- Enregistrez le programme sous
startupet lancez-le. La première fois, il demande un code deux fois : tapez4321puis Entrée, deux fois. - Tapez un code erroné :
Code erroné., et un essai de moins. Tapez le bon : le cadenas passe au vert, la porte s'ouvre, le décompte défile et la porte se referme. - Tapez trois codes erronés : le clavier se bloque 30 secondes. Tapez au clavier pendant le blocage : rien ne s'affiche, et rien n'attend une fois le blocage fini.
- Encore trois codes erronés : le blocage dure maintenant une minute.
- Arrêtez le programme avec Ctrl+T et lisez le journal :
> cat porte/journal 07:12 nouveau code choisi 07:13 porte ouverte 08:40 code erroné 1/3 08:40 code erroné 2/3 08:41 code erroné 3/3 08:41 clavier bloqué 30 s 08:42 porte ouverte
Pour changer le code, supprimez le fichier (rm porte/code) et relancez le programme : il en demande un nouveau. Ou écrivez directement le nouveau code dans le fichier avec edit porte/code.
C'est une serrure entre amis, pas une défense contre les pilleurs. Qui peut ouvrir le terminal peut aussi appuyer sur Ctrl+T, arrêter le programme et faire cat porte/code. Cachez l'ordinateur dans le mur et ne laissez qu'un moniteur dehors, en gardant en tête qu'un clic droit sur ce moniteur ouvre toujours le même terminal. Arrêter le programme pendant que la porte est ouverte la laisse ouverte, aussi : les sorties redstone et les émissions sur les Liaisons de redstone gardent leur dernière valeur jusqu'à ce que l'ordinateur redémarre ou s'éteigne.
Variantes
- Un clavier sur le moniteur. Au lieu de
read, dessinez dix boutons de chiffres sur un moniteur et recueillez les clics (os.pull_event("click"), avece.xete.y) : le code se tape alors par des clics droits sur l'écran, sans aucun terminal ouvert. Les boutons dePanneau de commande tactilesont un bon départ. - Plusieurs codes. Gardez un code par ligne dans
porte/code(fs.read(FICHIER_CODE):split("\n")), et notez lequel a ouvert la porte (sa position dans la liste, pas le code lui-même). - Une alarme. Lors d'un blocage, faites sonner une cloche par une seconde fréquence de Liaison de redstone, ou envoyez un message à un autre ordinateur avec
net.send(net) : « quelqu'un essaie des codes à l'atelier ». - Ouvrir de l'intérieur. Un bouton dans la pièce, relié à l'ordinateur, ouvre sans code : attendez avec
os.pull_event()dans une boucle qui traite à la fois les évènementscharetredstone(rs.getdit de quel côté), et construisez le code vous-même à partir des évènementschar, puisquereadne rend la main que sur Entrée. - Plusieurs portes. Un seul ordinateur peut piloter beaucoup de portes par autant de fréquences de Liaison de redstone (jusqu'à 32), chacune avec son fichier de code ; le code tapé choisit la porte.