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

Tree @master (Download .tar.gz)

EtchaState.java @masterraw · history · blame

/*
 * An EtchaState implements the semantics of Etcha.
 * The source code in this file has been placed into the public domain.
 */
package tc.catseye.yoob.etcha;

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

import java.util.List;
import java.util.ArrayList;

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


class Etcha extends TextBasedLanguage<EtchaState> {
    public String getName() {
        return "Etcha";
    }

    public int numPlayfields() {
        return 1;
    }

    public int numTapes() {
        return 0;
    }

    public boolean hasInput() {
        return false;
    }

    public boolean hasOutput() {
        return false;
    }

    public List<String> exampleProgramNames() {
        ArrayList<String> names = new ArrayList<String>();
        names.add("silly little thing");
        return names;
    }

    public EtchaState loadExampleProgram(int index) {
        String[][] program = {
          // Example from the Circute docs.
          // I hereby place it into the public domain.
          {
            ">+++>+++>+++>+++>[+]>>>>+",
          }
        };
        return importFromText(program[index]);
    }

    public EtchaState importFromText(String text) {
        EtchaState s = new EtchaState();
        s.setProgramText(text);
        return s;
    }

    private static final String[][] properties = {
        {"Author", "Chris Pressey"},
        {"Implementer", "Chris Pressey"},
        {"Implementation notes",
         "This implementation uses a yoob playfield as data " +
         "store/output."},
    };

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

class EtchaPlayfield extends BasicPlayfield<BitElement> {
    protected BasicCursor<BitElement> turtle;
    public EtchaPlayfield() {
        super(BitElement.ZERO);
        this.turtle = new BasicCursor<BitElement>(this);
        turtle.setDelta(0, -1);
        clear();
    }

    public EtchaPlayfield clone() {
        EtchaPlayfield c = new EtchaPlayfield();
        c.copyBackingStoreFrom(this);
        c.turtle = turtle.clone();
        c.turtle.setPlayfield(c);
        return c;
    }

    public int numCursors() {
        return 1;
    }

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

class EtchaPlayfieldView extends BasicPlayfieldView {
    public void render(Graphics g, Element e, int x, int y, int w, int h) {
        BitElement be = (BitElement)e;
        if (be.getBoolean()) {
            g.setColor(Color.black);
        } else {
            g.setColor(Color.white);
        }
        g.fillRect(x, y, w, h);
    }

    public void render(Graphics g, Cursor c, int x, int y, int w, int h) {
        g.setColor(Color.blue);
        g.drawRoundRect(x - 1, y - 1, w + 2, h + 2, w / 4, h / 4);
        if (c instanceof BasicCursor) {
            BasicCursor bc = (BasicCursor)c;
            int cx = x + (w/2);
            int cy = y + (h/2);
            int dx = bc.getDeltaX().intValue();
            int dy = bc.getDeltaY().intValue();
            int ex = cx + (dx * w);
            int ey = cy + (dy * h);
            g.drawLine(cx, cy, ex, ey);
        }
    }
}

public class EtchaState implements State {
    protected EtchaPlayfield pf;
    protected EtchaPlayfieldView pfView;
    protected int pencounter = 0;
    protected boolean pendown = true;
    protected boolean halted = false;
    protected String program;
    protected int pc = 0;
    private static final Etcha language = new Etcha();

    public EtchaState() {
        pf = new EtchaPlayfield();
        BasicCursor<BitElement> ip = (BasicCursor<BitElement>)pf.getCursor(0);
        ip.setDelta(0, -1);
        pfView = new EtchaPlayfieldView();
    }
    
    public Language getLanguage() {
        return language;
    }

    public EtchaState clone() {
        EtchaState c = new EtchaState();
        c.pf = pf.clone();
        c.program = program;
        c.pc = pc;
        c.halted = halted;
        c.pencounter = pencounter;
        c.pendown = pendown;
        return c;
    }

    public List<Error> step(World world) {
        ArrayList<Error> errors = new ArrayList<Error>();
        BasicCursor<BitElement> ip = (BasicCursor<BitElement>)pf.getCursor(0);
        char instruction = program.charAt(pc);
        switch (instruction) {
            case '+':
                // + -- equivalent to FD 1
                if (pendown) {
                    ip.set(ip.get().invert());
                }
                ip.advance();
                break;
            case '>':
                // > -- equivalent to RT 90; toggles PU/PD every 4 executions
                ip.rotate(90);
                pencounter++;
                pencounter %= 4;
                if (pencounter == 0) {
                    pendown = !pendown;
                }
                break;
            case '[':
                // [ WHILE Begin a while loop
                if (ip.get().isZero()) {
                    // skip forwards to matching ]
                    int depth = 0;
                    for (;;) {
                        if (program.charAt(pc) == '[') {
                            depth++;
                        } else if (program.charAt(pc) == ']') {
                            depth--;
                            if (depth == 0)
                                break;
                        }
                        pc++;
                        if (pc >= program.length()) {
                            halted = true;
                            return errors;
                        }
                    }
                }
                break;
            case ']':
                // ] END End a while loop
                // skip backwards to matching ]
                int depth = 0;
                for (;;) {
                    if (program.charAt(pc) == '[') {
                        depth--;
                    } else if (program.charAt(pc) == ']') {
                        depth++;
                    }
                    pc--;
                    if (depth == 0 || pc < 0)
                        break;
                }
                break;
            default:
                // NOP
                break;
        }

        pc++;
        if (pc >= program.length()) {
            halted = true;
        }

        return errors;
    }

    public Playfield getPlayfield(int index) {
        if (index == 0)
            return pf;
        return null;
    }

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

    public String getProgramText() {
        return program;
    }

    public int getProgramPosition() {
        return pc;
    }

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

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

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

    public String exportToText() {
        return program;
    }

    public boolean hasHalted() {
        return halted;
    }

    public boolean needsInput() {
        return false;
    }

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