Create: Computing AgesDoc Brass
Recettes : vrais projets

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é left vu 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ôleur

Le programme, étape par étape

1. Les réglages

Brass
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

Le 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 :

Brass
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

Le 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.

Brass
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

Le 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 :

Brass
local function charger_code()
  local texte = fs.read(FICHIER_CODE)
  if texte ~= nil and texte:trim() ~= "" then return texte:trim() end
  return nil
end

Le 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.

Brass
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)
end

La 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.

Brass
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
end

Vé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.

Brass
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

6. 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.

Brass
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

Une 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

startup
-- 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 :

Brass
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("****")
Écran
Écran

Quand le code est juste, le même écran passe le cadenas au vert et ouvre son anse pendant que la porte est ouverte :

Brass
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)
Écran
Écran

L'essayer

  1. Enregistrez le programme sous startup et lancez-le. La première fois, il demande un code deux fois : tapez 4321 puis Entrée, deux fois.
  2. 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.
  3. 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.
  4. Encore trois codes erronés : le blocage dure maintenant une minute.
  5. Arrêtez le programme avec Ctrl+T et lisez le journal :
Terminal
> 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.

Attention

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"), avec e.x et e.y) : le code se tape alors par des clics droits sur l'écran, sans aucun terminal ouvert. Les boutons de Panneau de commande tactile sont 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ènements char et redstone (rs.get dit de quel côté), et construisez le code vous-même à partir des évènements char, puisque read ne 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.