Source and (Brief) Overview by GRAHAM STALKER-WILDE Java source code ©Graham Stalker-Wilde 1996 Used and Distributed on the Alan Turing website maintained by Andrew Hodges, http://www.turing.org.uk/turing/ --------------------------------------------------------------------------- TuringMachineApplet.class This Assembles the components. It creates a TuringMachine object, reads the Tape, Alphabet and State parameters from the PARAM tags, and controls the animation, stepping and resetting. It creates an animation thread, and uses a StatesPainter and TapePainter to display the state of the machine after every step. This could be rewritten to allow viewers to edit or create machines. (Tapes, symbol sets, State sets.) source: TuringMachineApplet.java ---------------------------------------------------------------------------- TuringMachine.class This mantains the internal state of the machine. It contains a vector of states, knows what the current state is (by index) and current position on the tape. It does not know the details of any of the states. When asked to "Step" all it does is pass the current state the tape position and tell it (the State) to act. source: TuringMachine.java ---------------------------------------------------------------------------- Tape.class Our friend the bidirectional infinite tape. Just a vector of Integers. The TapeIter does all the work. source: Tape.java ---------------------------------------------------------------------------- TapeIter.class This takes care of the bidirectional infinite bit. It is an iterator over a Tape. It maintains a position on the tape, knows what symbol it is at, and how to move left or right. source: TapeIter.java ---------------------------------------------------------------------------- State.class An array of M * N 3-tuples, where N is the number of characters in the symbol set (Alphabet) and M is the number of states. The first N 3-tuples represent state 0, etc. Each 3-tuples describes the symbol to write, the direction to move, and the new state (which may be STOP). The first 3-tuple of set J of N 3-tuples (yeuch) describes what state J does on reading symbol 0, etc. This is the ugliest code, I think. This class also takes care of parsing the applet state parameters. source: State.java ---------------------------------------------------------------------------- StateException.class One of these gets thrown if the parameter describing a state is screwed, or if the state set is inconsistent (this happens a lot when I try to write state sets!) source: StateException.java ---------------------------------------------------------------------------- StatesPainter.class This is a rudimentary way of depicting the state set and the current state. This is the class one would rewrite to draw pretty graphic state representations. As long as the interface was the same (and it is a subclass of Component) nothing else would be affected. source: StatesPainter.java ---------------------------------------------------------------------------- TapePainter.class A rudimentary depiction of the tape, including the current position on the tape. As with the StatesPainter, this could be rewritten without impacting the rest of the code. source: TapePainter.java ---------------------------------------------------------------------------- Alphabet.class This is used solely by the TapePainter and the StatesPainter as a mapping from the number of the symbol to the actual symbol to display. This way all states and tapes can be internally described using numerical indexes (symbol 0, 1, 2, 3, ...) but the applet can display any character set) source: Alphabet.java ---------------------------------------------------------------------------- // package TuringMachine; import java.awt.*; import java.applet.*; import java.io.*; import java.util.*; public class TuringMachineApplet extends Applet implements Runnable { private TuringMachine m_theMachine; private TapePainter m_tapePainter; private StatesPainter m_statesPainter; private Button m_btnStep; private Button m_btnAnimate; private Button m_btnReset; private Alphabet m_alphabet; private long m_lSleep; // how long to sleep for private boolean m_fAnimate; private Thread m_thread; public void init() { m_theMachine = new TuringMachine(); readAlphabet(); readTapeParameters(); readStateParameters(); readSleepParameter(); // save original state information for Restart m_theMachine.saveState(); m_fAnimate = false; m_tapePainter = new TapePainter(m_theMachine, m_alphabet); m_statesPainter = new StatesPainter(m_theMachine, m_alphabet); m_btnStep = new Button("Step"); m_btnAnimate = new Button("Run"); m_btnReset = new Button("Reset"); // Symantec's ugly ol' component layout //{{INIT_CONTROLS setLayout(null); addNotify(); resize(insets().left + insets().right + 252, insets().top + insets().bottom + 274); label1=new Label("State : Read : Write : Move : NewState"); add(label1); label1.reshape(insets().left + 7,insets().top + 38,217,15); add(m_tapePainter); m_tapePainter.reshape(insets().left + 7,insets().top + 8,256,15); m_statesPainter.setFont(new Font("Courier",Font.PLAIN,10)); add(m_statesPainter); m_statesPainter.reshape(insets().left + 12,insets().top + 67,214,103); add(m_btnAnimate); m_btnAnimate.reshape(insets().left + 133,insets().top + 180,63,23); add(m_btnStep); m_btnStep.reshape(insets().left + 42,insets().top + 180,63,23); add(m_btnReset); m_btnReset.reshape(insets().left + 42,insets().top + 210,63,23); //}} m_tapePainter.display(); m_statesPainter.display(); } public void stop() { if (m_thread != null ) { m_thread.stop(); } m_thread = null; } public void run() { while ( m_fAnimate ) { if ( ! m_theMachine.isStopped() ) { step(); try { Thread.sleep(m_lSleep); } catch ( InterruptedException e ) { // Do nothing } } else { m_fAnimate = false; m_btnAnimate.setLabel("Run"); m_btnReset.enable(); } } } public String getAppletInfo() { return "Demo of Turing Machine. rev 0.0\nGraham Stalker-Wilde "; } public String[][] getParameterInfo() { String info[][] = { {"Sleep", "int", "delay between tape moves for animated mode in milliseconds (default is 500)"}, {"Tape", "String", "Starting Tape for Turing Machine, comma delimited, eg 1,0,1,1,0,0"}, {"SymbolSet", "String", "characters to be used in the tape as single string, eg \" -12+=\" "}, {"StartPosition", "int", "Starting position in tape, from LHS"}, {"State0", "String", "0-M parameters representing states as comma delimited strings"} , {"State1", "String", "each parameter contains 3N values, where N is the number of symbols in the symbol set"} , {"State2", "String", "each triplet (from i =0 to i = N-1) represents state behaviour on reading symbol i"} , {"State3", "String", "i.e. write-on-ith-symbol,move-on-ith-symbol,newState-on-ith-symbol"}, {"State4", "String", "write-on-ith-symbol is a numeral, j, representing the jth symbol in the symbol set"}, {"State5", "String", "movements are L, R or N (for None), new state can be -1 (for Stop)"}, {"State5", "String", "Note: if a non-existent state or symbol is referenced the applet will not run."} }; return info; } public boolean action(Event evt, Object ob) { if ( evt.target == m_btnStep ) { step(); return true; } else if (evt.target == m_btnAnimate ) { m_fAnimate = !m_fAnimate; if (m_fAnimate) { m_btnAnimate.setLabel("Stop"); m_btnReset.disable(); m_thread = new Thread(this); m_thread.start(); } else { m_btnAnimate.setLabel("Run"); m_btnReset.enable(); } return true; } else if ( (evt.target == m_btnReset) && !m_fAnimate) { // Reset original state information m_theMachine.restoreState(); m_btnAnimate.enable(); m_btnStep.enable(); showStatus("State: " + m_theMachine.CurrentState()); m_tapePainter.display(); m_statesPainter.display(); return true; } return false; } private void step() { try { if ( !m_theMachine.isStopped() ) { m_theMachine.step(); m_tapePainter.display(); m_statesPainter.display(); if (!m_theMachine.isStopped()) { showStatus("State: " + m_theMachine.CurrentState()); } else { showStatus("State: Stopped"); m_btnAnimate.disable(); m_btnStep.disable(); } } else { showStatus("State: Stopped"); } } catch (StateException e) { showStatus("Error: " + e); } } private void readAlphabet() { String st = getParameter("Alphabet"); if (st != null) { m_alphabet = new Alphabet(st); } else { m_alphabet = new Alphabet("-1"); } } private void readStateParameters() { for (int i =0; true; i++) { Integer I = new Integer(i); String stState = "State"+I; try { String st = getParameter(stState); if (st == null) { break; } State state = new State(st); m_theMachine.addState(i, state); } catch (StateException se) { break; } } } private void readTapeParameters() { // ugly stuff Tape tp = new Tape(); TapeIter ti= new TapeIter(tp); try { String stZERO = "0"; String stTape = getParameter("Tape"); StringTokenizer st = new StringTokenizer(stTape, ", "); int i = 0; while (st.hasMoreTokens() ) { ti.write( new Integer (st.nextToken()) ); ti.moveRight(); i++; } int nStart = 0; String stStartingPosition = getParameter("StartPosition"); if ( stStartingPosition != null) { try { Integer startingPosition = new Integer(stStartingPosition); nStart = startingPosition.intValue(); } catch(Exception e) { // do nothing, nStart will be 0 } } for ( ; i > nStart; i--) { ti.moveLeft(); } m_theMachine.setTape(tp, ti); } catch (Exception e) { // if tape is bad then we're dead } } private void readSleepParameter() { m_lSleep = 500; // default String stSleep = getParameter("Sleep"); if ( stSleep != null) { try { m_lSleep = Long.parseLong( stSleep ); } catch(Exception e) { // do nothing, default will be 500 } } } //{{DECLARE_CONTROLS Label label1; //}} } // package TuringMachine; import java.util.*; class TuringMachine { private Tape m_Tape; private TapeIter m_CurrentPosition; // for restore private Tape m_SavedTape; private TapeIter m_SavedPosition; private Vector m_States; private Integer m_CurrentState; public TuringMachine() { m_Tape = new Tape(); m_CurrentPosition = new TapeIter(m_Tape); m_States = new Vector(); m_CurrentState = new Integer(0); } public void addState(Integer nStateNumber, Vector state) { State st = new State( state); pad(nStateNumber.intValue()); m_States.setElementAt(st, nStateNumber.intValue()); } public boolean isStopped() { return m_CurrentState.intValue() == State.STOP.intValue(); } private void pad(int n) { while (m_States.size() <= n) { m_States.addElement(new Integer(0)); } } public void setTape(Tape tp) { m_Tape = new Tape(tp); m_CurrentPosition = new TapeIter(m_Tape); } public void setTape(Tape tp, TapeIter ti) { m_Tape = new Tape(tp); m_CurrentPosition = new TapeIter(ti); } public void addState(int nStateNumber, State st) { pad(nStateNumber); m_States.setElementAt(st, nStateNumber); } public void step() throws StateException { if ( ((Integer)m_CurrentState).intValue() != State.STOP.intValue()) { State st = (State)m_States.elementAt( m_CurrentState.intValue()); m_CurrentState = st.act(m_CurrentPosition); } } public Integer CurrentState() { return m_CurrentState; } public Enumeration states() { return m_States.elements(); } public TapeIter clonePosition() { return new TapeIter(m_CurrentPosition); } // not working public void saveState() { m_SavedTape = new Tape(m_Tape); m_SavedPosition = new TapeIter(m_CurrentPosition); } // not working public void restoreState() { m_Tape = new Tape(m_SavedTape); m_CurrentPosition = new TapeIter(m_SavedPosition); m_CurrentState = new Integer(0); } } // package TuringMachine; import java.util.*; /** Tape is bidirectionally infinite It is accessed by creating iterators, any number of which might be active on the Tape at one time. Iterators are used to read and write to the tape. One will be used by the TapePainter, another will represent the Current Position of the machine on the tape and will be used for read write and movement tape is represented as a vector with (conceptually) odd numbers increasing the right, even numbers increasing to the left. i.e. ... 12 10 8 6 4 2 0 1 3 5 7 9 11 13 .... */ public class Tape { // these constants should be moved to an Alphabet structure // and accessed by ordinal (alph[0], alph[1], etc.) // this allows machines to be defined with any combination of // symbols private Vector m_tape; public Tape() { m_tape = new Vector(40); } public Tape(Tape tp) { m_tape = new Vector(); for (Enumeration e = tp.m_tape.elements() ; e.hasMoreElements() ;) { m_tape.addElement( new Integer( ( (Integer)e.nextElement() ).intValue() ) ); } } protected void writeAt(Integer i, int nIndex) { while (m_tape.size() <= nIndex) { m_tape.addElement(new Integer(0) ); } m_tape.setElementAt(i, nIndex); } protected Integer readAt(int nIndex) { try { return (Integer) m_tape.elementAt(nIndex); } catch (ArrayIndexOutOfBoundsException e) { return new Integer(0); } } } // package TuringMachine; import java.util.*; public class TapeIter { private Tape m_theTape; private int m_nIndex; public TapeIter(Tape tp) { m_theTape = tp; m_nIndex = 0; } /** references the same tape and indexes the same position Not thread safe.*/ public TapeIter(TapeIter ti) { m_theTape = new Tape(ti.m_theTape); m_nIndex = ti.m_nIndex; } /** indexes map to tape positions like this ... 12 10 8 6 4 2 0 1 3 5 7 9 11 13 .... so indexing them requires some fairly ugly arithmetic */ public void moveLeft() { // ugly bit if (m_nIndex%2 == 0) { // even index m_nIndex += 2; } else { // odd index m_nIndex -= 2; if (m_nIndex < 0 ) { m_nIndex = 0; } } } /** @see TapeIter#moveLeft() */ public void moveRight() { // ugly bit if ( m_nIndex%2 == 1) { // odd index m_nIndex += 2; } else { m_nIndex -= 2; if (m_nIndex < 0) { m_nIndex = 1; } } } /** usage iter.read(); reads at current position */ public Integer read() { return m_theTape.readAt(m_nIndex); } /** usage iter.write( new Integer(1)); */ public void write(Integer s) { m_theTape.writeAt(s, m_nIndex); } } // package TuringMachine; import java.util.*; public class State { // belongs here public static final Integer STOP = new Integer(-1); public static final Integer LEFT = new Integer(0); public static final Integer RIGHT = new Integer(1); public static final Integer NONE = new Integer(2); Vector m_state; public State(Vector state ) { setProperties( state ); } /** this will be used to createStates from PARAM strings */ public State (String st) throws StateException { StringTokenizer strtok = new StringTokenizer(st, ", "); try { Vector v = new Vector(); String stNext = strtok.nextToken(); while ( stNext != "" ) { Vector vi = new Vector(3); vi.addElement( new Integer( stNext ) ); String stTemp = strtok.nextToken(); vi.addElement( stTemp.equals("L") ? State.LEFT : stTemp.equals("R") ? State.RIGHT : State.NONE ); vi.addElement(new Integer ( strtok.nextToken() ) ); v.addElement(vi); if (strtok.hasMoreTokens()) { stNext = strtok.nextToken(); } else { stNext = ""; } } setProperties( v ); } catch (Exception e) { throw new StateException("Bad State"); } } public void setProperties(Vector state) { // performs a deep copy of state m_state = new Vector(); for (Enumeration e = state.elements(); e.hasMoreElements(); ) { Vector vi = new Vector(3); Vector v = (Vector)e.nextElement(); vi.addElement(new Integer(((Integer)v.elementAt(0)).intValue())); vi.addElement(v.elementAt(1)); // note no copy, this is an enumeration vi.addElement(new Integer(((Integer)v.elementAt(2)).intValue())); m_state.addElement(vi); } } public Integer act(TapeIter ti) throws StateException { Vector v = (Vector)m_state.elementAt( ti.read().intValue() ); ti.write((Integer)v.elementAt(0)); if ( State.LEFT == (Integer)v.elementAt(1)) { ti.moveLeft(); } else if ( (Integer)v.elementAt(1) == State.RIGHT ) { ti.moveRight(); } else { // Do nothing } return (Integer)v.elementAt(2); } /** returns a vector describing the internal representation of the states. This is designed for read only access by State painters */ public Vector describe() { return m_state; } } // package TuringMachine; public class StateException extends Exception { public StateException(String st) { super(st); } } // package TuringMachine; import java.awt.*; import java.util.*; public class StatesPainter extends List { private TuringMachine m_theMachine; private Alphabet alphabet; public StatesPainter(TuringMachine tm, Alphabet alph) { m_theMachine = tm; alphabet = alph; } public void display() { // clear() doesn't seem to work delItems(0, countItems() - 1); int i = 0; int nCurrent = m_theMachine.CurrentState().intValue(); for ( Enumeration e = m_theMachine.states(); e.hasMoreElements(); i++ ) { State state = (State)e.nextElement(); String stMarker; Vector v = state.describe(); if (i == nCurrent) { stMarker = "**"; } else { stMarker = " "; } // we loop through each entry for the state for (int j = 0; j < alphabet.size(); j++) { String stSpacer = " "; Vector vi = (Vector) v.elementAt(j); String st = ""; st = " " + i + stSpacer + alphabet.getSymbol( new Integer(j) ) + stSpacer + // read alphabet.getSymbol(vi.elementAt(0)) + stSpacer + // write ( (Integer)vi.elementAt(1) == State.LEFT ? "L" : (Integer)vi.elementAt(1) == State.RIGHT ? "R" : "-" ) + stSpacer;// move if ( ((Integer)vi.elementAt(2)).intValue() == State.STOP.intValue() )// new state { st = st + "Stop" + " " + stMarker; } else { st = st + (Integer)vi.elementAt(2) + " " + stMarker; } addItem(st); } } makeVisible(alphabet.size() * nCurrent); } } // package TuringMachine; // a more sophisticated TapePainter would be like this: // have an off-screen image buffer import java.awt.*; public class TapePainter extends Label { int nCharsShown; private TuringMachine m_theMachine; private Alphabet alphabet; public TapePainter(TuringMachine tm, Alphabet alph) { super(); m_theMachine = tm; alphabet = alph; nCharsShown = 8; // the number of characters shown will be 2* nCharsShown + 1; } public void display() { TapeIter ti = m_theMachine.clonePosition(); for (int i = 0; i < nCharsShown ; i++) { ti.moveLeft(); } String st = ""; for (int i = 0; i < nCharsShown ; i++) { st += alphabet.getSymbol(ti.read()); st += " "; ti.moveRight(); } // show current position st += "["; st += alphabet.getSymbol(ti.read()); st += "]"; ti.moveRight(); for (int i = 0; i < nCharsShown ; i++) { st += " "; st += alphabet.getSymbol(ti.read()); ti.moveRight(); } super.setText(st); } } import java.awt.*; import java.util.*; public class Alphabet { String m_st; public char getSymbol(Object obj) { return m_st.charAt(((Integer)obj).intValue() ); } public Alphabet(String st) { m_st = st; } public int size() { return m_st.length(); } }