git @ Cat's Eye Technologies yoob / master src / lang / LNUSPState.java
master

Tree @master (Download .tar.gz)

LNUSPState.java @masterraw · history · blame

/*
 * A LNUSPState implements the semantics of LNUSP.
 * The source code in this file has been placed into the public domain.
 */
package tc.catseye.yoob.lnusp;

import tc.catseye.yoob.*;
import tc.catseye.yoob.Error;

import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;

import java.awt.Graphics;
import java.awt.Color;
import java.awt.Component;


class LNUSP implements Language {
    public String getName() {
        return "LNUSP";
    }

    public int numPlayfields() {
        return 2;
    }

    public int numTapes() {
        return 0;
    }

    public boolean hasProgramText() {
        return false;
    }

    public boolean hasInput() {
        return true;
    }

    public boolean hasOutput() {
        return true;
    }

    public List<String> exampleProgramNames() {
        ArrayList<String> names = new ArrayList<String>();
        names.add("cat");
        names.add("demo of * and +");
        return names;
    }

    public LNUSPState loadExampleProgram(int index) {
        String[][] program = {
          // From http://www.esolangs.org/wiki/LNUSP
          // Author unknown, probably zzo38.  From the esowiki, thus in the public domain.
          {
              ".      .               .                .",
              " .?......!!................?            .",
              " ?.    .!  !           .    ?           .",
              " ? .   .!  !           .    ?           .",
              "  ?.!..@..!............@...?            .",
              "          !.............................@",
          },
          // Example made up by Chris Pressey to test this implementation
          // In the public domain with the rest of this file
          {
              ".",
              " .       !***?",
              "  .     !     ?",
              "   .          ?",
              "    !..***+++?",
              "        +",
          },
        };
        LNUSPState s = new LNUSPState();
        s.playfield.load(program[index]);
        return s;
    }

    public LNUSPState importFromText(String text) {
        LNUSPState s = new LNUSPState();
        s.playfield.load(text.split("\\r?\\n"));
        return s;
    }

    public List<String> getAvailableOptionNames() {
        ArrayList<String> names = new ArrayList<String>();
        return names;
    }

    private static final String[][] properties = {
        {"Author", "Aaron 'Zzo38' Black"},
        {"Implementer", "Chris Pressey"},
        {"Implementation notes",
         "This implementation does not support the 'repeat' prefix on each line. " +
	 "It also treats all characters which are not one of the five defined " +
	 "instructions as nops. It also treats exceeding the bounds to the west, " +
	 "east, or south, as halting the program; exceeding the north bound when " +
         "not in one of the defined subtroutine columns is treated as a reflection." },
    };

    public String[][] getProperties() {
        return properties;
    }
}

class LNUSPDataSpace extends BasicPlayfield<ByteElement> {
    protected BasicCursor<ByteElement> dp = null;

    public LNUSPDataSpace() {
        super(new ByteElement(0));
        clear();
    }

    public void clear() {
        super.clear();
        dp = new BasicCursor<ByteElement>(this, IntegerElement.ZERO, IntegerElement.ZERO, IntegerElement.ZERO, IntegerElement.ZERO);
    }

    public LNUSPDataSpace clone() {
        LNUSPDataSpace c = new LNUSPDataSpace();
        c.copyBackingStoreFrom(this);
        c.dp = dp.clone();
        c.dp.setPlayfield(c);
        return c;
    }

    public int numCursors() {
        return 1;
    }

    public BasicCursor<ByteElement> getCursor(int index) {
        if (index == 0)
            return dp;
        return null;
    }

    public void loadChar(int x, int y, char c) {
        set(x, y, new ByteElement(c));
    }
}

class DataSpaceView extends BasicPlayfieldView {
    public int getPreferredCellWidth() {
        return 3;
    }

    public boolean getSquareOff() {
        return false;
    }

    public float getAlignmentX() {
        return Component.RIGHT_ALIGNMENT;
    }
}

public class LNUSPState implements State {
    protected LNUSPDataSpace dataspace;
    protected CommonPlayfield playfield;
    protected BasicPlayfieldView pfView, dsView;
    protected BasicCursor<CharacterElement> savedPosition;
    protected boolean halted = false;
    protected boolean needsInput = false;
    private static final LNUSP language = new LNUSP();

    public LNUSPState() {
        dataspace = new LNUSPDataSpace();
        playfield = new CommonPlayfield();
	/* The program starts in the top left corner going southeast. */
        BasicCursor<CharacterElement> ip = playfield.getCursor(0);
	ip.setDelta(1, 1);
	savedPosition = null;
        pfView = new BasicPlayfieldView();
	dsView = new DataSpaceView();
    }
    
    public Language getLanguage() {
        return language;
    }

    public LNUSPState clone() {
        LNUSPState c = new LNUSPState();
        c.playfield = playfield.clone();
        c.dataspace = dataspace.clone();
	c.savedPosition = savedPosition;
	if (c.savedPosition != null) {
	    c.savedPosition.setPlayfield(c.playfield);
	}
        c.halted = halted;
        c.needsInput = needsInput;
        return c;
    }

    public List<Error> step(World world) {
        ArrayList<Error> errors = new ArrayList<Error>();
        BasicCursor<CharacterElement> ip = playfield.getCursor(0);
        BasicCursor<ByteElement> dp = dataspace.getCursor(0);
        ByteElement b = dp.get();
        char instruction = ip.get().getChar();

	/*
	+ Increase current memory cell by 1, mod 256
	* Move memory pointer in the direction the IP is traveling
	? Turn 45 degrees left if current memory cell is nonzero
	! Turn 45 degrees left if current memory cell is zero
	@ Save position and go north, or if there is already a
	  position saved, go back to that position and delete the
	  saved position, or if already going north, unconditionally
	  turn 45 degrees right
	. (dot) is specially guaranteed to not do anything.
	*/

        if (playfield.hasFallenOffEdge(ip)) {
	    if (ip.getY().compareTo(IntegerElement.ZERO) < 0) {
		/*
		 * Going off the north side of the program results in a
		 * subroutine being executed, followed by the instruction
		 * pointer reversing direction. Which subroutine is executed
		 * depends on how far over the column is from the left. With
		 * the leftmost column counting as 1, the columns are
		    * 8 = Input
		    * 24 = Output
		    * 41 = Stop 
		*/
		if (ip.getX().intValue() == 7) {
                    CharacterElement c = world.inputCharacter();
                    if (c == null) {
                        needsInput = true;
                        return errors;
                    }
                    dp.set(new ByteElement(c.getChar()));
		} else if (ip.getX().intValue() == 23) {
                    world.output(new CharacterElement(b.toChar()));
		} else if (ip.getX().intValue() == 40) {
		    halted = true;
                    return errors;
		}
                ip.setDeltaY(ip.getDeltaY().negate());
	    } else {
                halted = true;
                return errors;
	    }
        }

        switch (instruction) {
            case '?':
	        if (!b.isZero()) {
		    ip.rotate(-45);
	        }
                break;
            case '!':
	        if (b.isZero()) {
		    ip.rotate(-45);
	        }
                break;
            case '+':
                dp.set(b.succ());
                break;
            case '*':
                dp.move(ip.getDeltaX(), ip.getDeltaY());
                break;
            case '@':
		if (ip.isHeaded(0, -1)) {
		    ip.rotate(45);
	        } else if (savedPosition != null) {
		    ip.setX(savedPosition.getX());
                    ip.setY(savedPosition.getY());
		    ip.setDeltaX(savedPosition.getDeltaX());
                    ip.setDeltaY(savedPosition.getDeltaY());
		    savedPosition = null;
		} else {
		    savedPosition = ip.clone();
		    ip.setDelta(0, -1);
		}
                break;
            default:
                // NOP
                break;
        }

        ip.advance();
        needsInput = false;
        return errors;
    }

    public Playfield getPlayfield(int index) {
        if (index == 0)
            return playfield;
        if (index == 1)
            return dataspace;
        return null;
    }

    public Tape getTape(int index) {
        return null;
    }

    public String getProgramText() {
        return "";
    }

    public int getProgramPosition() {
        return 0;
    }

    public List<Error> setProgramText(String text) {
        ArrayList<Error> errors = new ArrayList<Error>();
        return errors;
    }

    public View getPlayfieldView(int index) {
        if (index == 0)
            return pfView;
        if (index == 1)
	    return dsView;
	return null;
    }

    public View getTapeView(int index) {
        return null;
    }

    public String exportToText() {
        return playfield.dump();
    }

    public boolean hasHalted() {
        return halted;
    }

    public boolean needsInput() {
        return needsInput;
    }

    public void setOption(String name, boolean value) {
    }
}