import java.awt.*; import java.awt.event.*; import java.applet.*; import java.util.*; import java.io.*; public class Turing extends Applet { TextArea source; Machine machine; Toile toile; // pour dessiner Pilote pilote; // boutons de contrôle String programmeInitial () { String p = "0 . 1 . =\n"; // castor record avec 5 états p = p + "1 . 2 x Left\n"; p = p + "2 . 3 x Right\n"; p = p + "3 . 1 x L\n"; p = p + "4 . 1 x L\n"; p = p + "5 . 0 x =\n"; p = p + "1 x 1 - L\n"; p = p + "2 x 2 - R\n"; p = p + "3 x 4 - R\n"; p = p + "4 x 5 - R\n"; p = p + "5 x 3 . R\n"; return p; } public void init() { source = new TextArea (programmeInitial (), 64, 20); machine = new Machine (); toile = new Toile (); pilote = new Pilote (); source.setFont (new Font ("Courier", Font.PLAIN, 12) ); source.setBackground (new Color (192, 192, 240) ); pilote.setBackground (new Color (192, 192, 192) ); toile.setBackground (new Color (224, 240, 128) ); setLayout (new BorderLayout ()); add ("East", source); add ("South", pilote); add ("Center", toile); } public String[][] getParameterInfo() { String[][] info = { // Parameter Name Kind of Value Description {"Bande", "int", "Longueur de la bande (bidirectionnelle)"}, {"Origine", "int", "Position de l'origine sur la bande"} }; return info; } class Machine implements Runnable { Hashtable prog = new Hashtable (); final int longueur_bande0 = 10000; int longueur_bande; char bande []; final char blanc = '.'; final int butee = 5; final char butoir = '!'; final Character any = new Character ('-'); final Integer etatInitial = new Integer (0); Integer etat; // on écrit plutôt à droite qu'à gauche int origine; int curseur; int etape = 0; // une exécution en mode automatique // peut être stoppée en modifiant le booléen suivant boolean stop = false; int delai; final int periode = 1000000; Machine () { initBande (); } void initBande () { String parametre; int i; longueur_bande = longueur_bande0; parametre = getParameter ("BANDE"); if (parametre != null) { try { longueur_bande = Integer.parseInt (parametre); if (longueur_bande < 100) longueur_bande = 100; } catch (NumberFormatException e) {} } origine = longueur_bande / 10; parametre = getParameter ("ORIGINE"); if (parametre != null) { try { i = Integer.parseInt (parametre); if (i > butee && i < longueur_bande - butee) origine = i; } catch (NumberFormatException e) {} } bande = new char [longueur_bande]; i = 0; while (i < butee) bande [i++] = butoir; while (i < longueur_bande - butee) bande [i++] = blanc; while (i < longueur_bande) bande [i++] = butoir; etat = etatInitial; curseur = origine; } // Lire les règles (une par ligne). // Chaque ligne est de la forme q c q' c' d // q = état, c = caractère, d = déplacement public void lire () { String ligne = null; String token; Vector gauche, droite; BufferedReader lecteur = new BufferedReader ( new StringReader ( source.getText () )); StringTokenizer lex; char c; int d; int nl = 0; // numéro de ligne try { d = Integer.parseInt (pilote.etapeTexte.getText ()); etape = d; } catch (NumberFormatException e) {} prog.clear (); while (true) { try { ligne = lecteur.readLine(); } catch (IOException e) {} nl++; if (ligne == null) break; if ( ligne.length () == 0 || // ligne.startsWith ("Err") || ligne.startsWith ("//") ) continue; lex = new StringTokenizer (ligne); gauche = new Vector (2); droite = new Vector (3); try { token = lex.nextToken (); gauche.add (new Integer (token)); token = lex.nextToken (); c = token.charAt (0); gauche.add (new Character (c)); token = lex.nextToken (); droite.add (new Integer (token)); token = lex.nextToken (); c = token.charAt (0); droite.add (new Character (c)); token = lex.nextToken (); c = token.charAt (0); switch (c) { case 'g': case 'G': case 'l': case 'L': case '<': case '-': d = -1; break; case 's': case 'S': case '=': d = 0; break; default: d = 1; break; } droite.add (new Integer (d)); } catch (NumberFormatException e) { source.append ("// Ligne " + nl + ": " + e.toString () + "\n"); break; } catch (NoSuchElementException e) { source.append ("// Ligne " + nl + " trop courte\n"); break; } prog.put (gauche, droite); } // source.append (prog.toString () + "\n"); } // executer () retourne "true" si l'exécution a réussi // "false" sinon, c'est-à-dire : // pas de règle applicable // OU // état négatif (signal conventionnel d'arrêt) // OU // fin de bande (butoir) boolean executer () { char c = bande [curseur]; Character caractere = new Character (c); Integer d; int n = etat.intValue (); Vector gauche = new Vector (2); Vector droite; Object t; if (n < 0) { etat = new Integer (-n); return false; } if (c == butoir) return false; gauche.add (etat); gauche.add (caractere); t = prog.get (gauche); if (t == null) { // chercher une règle par défaut gauche.set (1, any); t = prog.get (gauche); } if (t == null) return false; droite = (Vector) t; etat = (Integer) droite.get (0); caractere = (Character) droite.get (1); if (! caractere.equals (any) ) { bande [curseur] = caractere.charValue (); } d = (Integer) droite.get (2); curseur = curseur + d.intValue (); etape++; return true; } // ce code est exécuté lorsque la machine // fonctionne en mode automatique, // dans un thread créé à cet effet public void run () { while ( ! stop && machine.executer () ) { if ( (delai > 0)) { try { Thread.sleep (delai); } catch (InterruptedException e) { } toile.repaint (); } else if (etape % periode == 0) toile.repaint (); } toile.repaint (); } } class Pilote extends Panel implements ActionListener { TextField delaiTexte, etapeTexte; Thread exec = null; Pilote () { Button stop = new Button ("Stop"); Button auto = new Button ("Automatique"); Button pas = new Button ("Pas à pas"); Button nettoyer = new Button ("Nettoyer la bande"); delaiTexte = new TextField ("100",4); etapeTexte = new TextField (8); stop.addActionListener (this); auto.addActionListener (this); pas.addActionListener (this); nettoyer.addActionListener (this); add (stop); add (auto); add (delaiTexte); add (pas); add (etapeTexte); add (nettoyer); } public void actionPerformed (ActionEvent e) { String bouton = e.getActionCommand (); if (bouton.startsWith ("Auto")) { // éviter de créér un nouveau thread si la machine // fonctionne déjà en mode automatique // (appui répété sur le bouton) if ( exec == null || ! exec.isAlive () ) { int d; try {d = Integer.parseInt (delaiTexte.getText ()); } catch (NumberFormatException exc) {d = 0;} machine.delai = d; machine.stop = false; machine.lire (); exec = new Thread (machine); exec.start (); } } else if (bouton.startsWith ("Net")) { machine.initBande (); machine.etape = 0; } else if (bouton.startsWith ("Pas")) { machine.stop = true; machine.lire (); machine.executer (); } else if (bouton.startsWith ("Stop")) { machine.stop = true; } toile.repaint (); // pour que le clavier agisse sur la bande // sans qu'il soit nécessaire de cliquer dessus toile.requestFocus (); } } class Toile extends Canvas implements KeyListener { // distance horizontale entre la bande et le bord final int marge = 32; // dimensions d'une case de la bande final int largeur_case = 12; final int hauteur_case = 18; // position verticale du bas de la banderole final int marge_haut = 30; // pour afficher la banderole "Machine de Turing" Font TMfont = new Font ("Courier", Font.BOLD, 18); Toile () { addKeyListener (this); } public void keyTyped (KeyEvent e) { // pas moyen de récupérer les "KeyCode" ici } public void keyPressed(KeyEvent e) { int code = e.getKeyCode (); char c = e.getKeyChar (); // source.append ("T " + code + "SP " + KeyEvent.VK_SPACE + "\n"); if (c != KeyEvent.CHAR_UNDEFINED && code != KeyEvent.VK_BACK_SPACE) { if (c == ' ') c = machine.blanc; machine.bande [machine.curseur++] = c; } else switch (code) { case KeyEvent.VK_LEFT: case KeyEvent.VK_KP_LEFT: machine.curseur--; break; case KeyEvent.VK_RIGHT: case KeyEvent.VK_KP_RIGHT: machine.curseur++; break; case KeyEvent.VK_BACK_SPACE: machine.bande [--machine.curseur] = machine.blanc; break; default: break; } repaint (); } public void keyReleased(KeyEvent e) { } void ombre (String s, int x, int y, Graphics g){ Color temp = g.getColor(); g.setColor (Color.black); g.drawString (s, x+1, y+1); g.setColor (temp); g.drawString(s, x, y); } void afficherBande (int x, int y, int nc, Graphics g){ // police des caractères sur la bande Font Bfont = new Font ("Courier", Font.BOLD, 14); // FontMetrics fm = getFontMetrics (Bfont); // int h = fm.getHeight (); // int w = fm.charsWidth (machine.bande, machine.curseur, nc); int cellule; // case est un mot réservé ! int x0 = x; g.setColor (Color.white); for (int i = 0; i < nc; i++) { g.fillRect (x, y, largeur_case, hauteur_case); x = x + largeur_case; } g.setColor (Color.red); x = x0; for (int i = 0; i < nc; i++) { g.drawRect (x, y, largeur_case, hauteur_case); x = x + largeur_case; } // signaler la position du curseur g.setColor (Color.green); x = x0 + (nc / 2) * largeur_case; g.fillRect (x, y, largeur_case, hauteur_case); // écrire les caractères g.setFont (Bfont); g.setColor (Color.black); y = y + hauteur_case - 4; x = x0 + 2; cellule = machine.curseur - nc / 2; for (int i = 0; i < nc; i++) { if (cellule >= 0 && cellule < machine.longueur_bande) g.drawChars (machine.bande, cellule, 1, x, y); x = x + largeur_case; cellule++; } } public void paint (Graphics g){ Dimension r = getSize(); int milieu = r.width / 2; // position verticale du haut de la bande int yb = (r.height + marge_haut) / 2; // nombre de cases int nc = (r.width - 2 * marge) / largeur_case; int curseurUtilisateur; g.setFont(TMfont); g.setColor(Color.red); ombre ("Machine de Turing", milieu - 96, marge_haut, g); afficherBande (marge, yb, nc, g); g.drawString ("Etat : " + machine.etat, milieu - 60, yb - hauteur_case / 2); curseurUtilisateur = machine.curseur - machine.origine; g.drawString ("Curseur : " + curseurUtilisateur, milieu - 84, yb + 2 * hauteur_case); pilote.etapeTexte.setText ("" + machine.etape); // if (pilote.exec != null) // g.drawString (pilote.exec.toString (), // marge, yb - 2 * hauteur_case); } } }