stanford.karel.KarelWorld Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of javakarel Show documentation
Show all versions of javakarel Show documentation
This the original Stanford Karel for Java, packaged for Maven. ACM Library is included. See also https://cs.stanford.edu/people/eroberts/karel-the-robot-learns-java.pdf
The newest version!
/*
* File: KarelWorld.java
* ---------------------
* This file contains the class that implements Karel's world.
*/
package stanford.karel;
import acm.util.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.text.*;
import java.util.*;
class KarelWorld extends Canvas {
public static final int NORTH = 0;
public static final int EAST = 1;
public static final int SOUTH = 2;
public static final int WEST = 3;
public static final int NORTHEAST = 10;
public static final int NORTHWEST = 11;
public static final int SOUTHEAST = 12;
public static final int SOUTHWEST = 13;
public static final int CENTER = 14;
public static final int INFINITE = 99999999;
public static final int PLUS1 = -1;
public static final int MINUS1 = -2;
public static final int BLANKB = -3;
public static final int SIMPLE = 0;
public static final int FANCY = 1;
public static final int LEFT_NUMBER_MARGIN = 16;
public static final int BOTTOM_NUMBER_MARGIN = 15;
public static final int DOUBLE_WALL_THRESHOLD = 24;
public static final int CROSS_THRESHOLD = 11;
public static final int NUMBER_THRESHOLD = 15;
public static final Font NUMBER_FONT = new Font("Times", Font.PLAIN, 9);
public static final double WALL_FRACTION = 0.30;
public static final double WALL_TOLERANCE = 0.15;
public static final int MAX_WIDTH = 50;
public static final int MAX_HEIGHT = 50;
public static final int MAX_DISPLAY_WIDTH = (6 * MAX_WIDTH) + LEFT_NUMBER_MARGIN;
public static final int MAX_DISPLAY_HEIGHT = (6 * MAX_HEIGHT) + BOTTOM_NUMBER_MARGIN;
public static final boolean TOKEN_TRACE = false;
public static final Color BEEPER_COLOR = Color.YELLOW;
public static final Color MARKED_COLOR = Color.DARK_GRAY;
public static final int BEEPER_BORDER = 1;
public static final String BEEPER_FONT_FAMILY = "Times";
public static final int MIN_FANCY = 20;
public static final int MIN_BEEPER = 4;
public static final int MIN_LABEL = 15;
public static final double BEEPER_FRACTION = 0.70;
public static final double SIMPLE_FRACTION = 0.70;
/* Constructor */
public KarelWorld() {
sizeLock = new Object();
setTitle("Karel World");
setBackground(Color.WHITE);
KarelWorldListener listener = new KarelWorldListener(this);
addMouseListener(listener);
addMouseMotionListener(listener);
addComponentListener(listener);
speedFormat = NumberFormat.getInstance();
speedFormat.setMinimumIntegerDigits(1);
speedFormat.setMaximumIntegerDigits(1);
speedFormat.setMinimumFractionDigits(2);
speedFormat.setMaximumFractionDigits(2);
forcedSize = 0;
numberSquaresFlag = true;
displayOneFlag = false;
look = FANCY;
alignment = CENTER;
karels = new ArrayList();
setDisplayFlag(true);
setRepaintFlag(true);
}
public void init(int cols, int rows) {
synchronized (sizeLock) {
this.cols = cols;
this.rows = rows;
setDisplayParameters(cols, rows);
editMode = false;
map = new Corner[cols + 2][rows + 2];
for (int x = 1; x <= cols + 1; x++) {
for (int y = 1; y <= rows + 1; y++) {
map[x][y] = new Corner();
map[x][y].wallSouth = (y == 1) || (y == rows + 1);
map[x][y].wallWest = (x == 1) || (x == cols + 1);
map[x][y].color = null;
}
}
repaint();
}
}
public void add(Karel karel) {
if (karels.indexOf(karel) == -1) {
karel.setWorld(this);
karels.add(karel);
}
repaint();
}
public void remove(Karel karel) {
karels.remove(karel);
karel.setWorld(null);
repaint();
}
public Karel getKarel() {
return getKarel(0);
}
public Karel getKarel(int k) {
if (k < 0 || k >= karels.size()) {
throw new ErrorException("Illegal Karel index");
}
return karels.get(k);
}
public int getKarelCount() {
return karels.size();
}
public Karel getKarelOnSquare(int x, int y) {
Iterator iterator = karels.iterator();
while (iterator.hasNext()) {
Karel karel = iterator.next();
Point pt = karel.getLocation();
if (pt.x == x && pt.y == y) return karel;
}
return null;
}
public void setTitle(String title) {
this.title = title;
}
public String getTitle() {
return title;
}
public void setPathname(String pathname) {
this.pathname = pathname;
}
public String getPathname() {
return pathname;
}
public void setDisplayFlag(boolean flag) {
displayFlag = flag;
}
public void setRepaintFlag(boolean flag) {
repaintFlag = flag;
}
public boolean getRepaintFlag() {
return repaintFlag;
}
public boolean getNumberSquaresFlag() {
return numberSquaresFlag;
}
public void setNumberSquaresFlag(boolean flag) {
numberSquaresFlag = flag;
}
public int getAlignment() {
return alignment;
}
public void setAlignment(int alignment) {
this.alignment = alignment;
}
public int getLook() {
return look;
}
public void setLook(int look) {
this.look = look;
}
public void setDisplayOneFlag(boolean flag) {
displayOneFlag = flag;
}
public String getPathName() {
return pathname;
}
public void setPathName(String pathname) {
this.pathname = pathname;
}
public boolean getEditMode() {
return editMode;
}
public void setEditMode(boolean flag) {
editMode = flag;
}
public void updateEditMode(boolean flag) {
if (monitor == null) throw new ErrorException("No map editor defined");
setEditMode(flag);
repaint();
}
public void forceSquareSize(int size) {
forcedSize = size;
}
public void reset() {
/* Empty */
}
public int getSquareSize() {
return sqSize;
}
public int getColumns() {
return (cols);
}
public int getRows() {
return (rows);
}
public boolean outOfBounds(Point pt) {
return outOfBounds(pt.x, pt.y);
}
public boolean outOfBounds(int x, int y) {
return (x < 1 || x > cols || y < 1 || y > rows);
}
public int getBeepersOnCorner(Point pt) {
return getBeepersOnCorner(pt.x, pt.y);
}
public int getBeepersOnCorner(int x, int y) {
return ((map[x][y]).nBeepers);
}
public void setBeepersOnCorner(Point pt, int nBeepers) {
map[pt.x][pt.y].nBeepers = nBeepers;
updateCorner(pt);
}
public void setBeepersOnCorner(int x, int y, int nBeepers) {
setBeepersOnCorner(new Point(x, y), nBeepers);
}
public static int adjustBeepers(int nBeepers, int delta) {
if (nBeepers == INFINITE) return INFINITE;
return nBeepers + delta;
}
public static int setBeepers(int nBeepers, int delta) {
if (delta == INFINITE) return INFINITE;
if (delta == PLUS1) return (nBeepers == INFINITE) ? INFINITE : nBeepers + 1;
if (delta == MINUS1) return (nBeepers == INFINITE) ? INFINITE : Math.max(0, nBeepers - 1);
return delta;
}
public Color getCornerColor(Point pt) {
return getCornerColor(pt.x, pt.y);
}
public Color getCornerColor(int x, int y) {
return (map[x][y].color);
}
public void setCornerColor(Point pt, Color color) {
map[pt.x][pt.y].color = color;
updateCorner(pt);
}
public void setCornerColor(int x, int y, Color color) {
setCornerColor(new Point(x, y), color);
}
public boolean checkWall(Point pt, int dir) {
return checkWall(pt.x, pt.y, dir);
}
public boolean checkWall(int x, int y, int dir) {
switch (dir) {
case SOUTH: return (map[x][y].wallSouth);
case WEST: return (map[x][y].wallWest);
case NORTH: return (map[x][y + 1].wallSouth);
case EAST: return (map[x + 1][y].wallWest);
}
return (false);
}
public void setWall(Point pt, int dir) {
switch (dir) {
case SOUTH: map[pt.x][pt.y].wallSouth = true; break;
case WEST: map[pt.x][pt.y].wallWest = true; break;
case NORTH: map[pt.x][pt.y + 1].wallSouth = true; break;
case EAST: map[pt.x + 1][pt.y].wallWest = true; break;
}
updateCorner(pt);
}
public void setWall(int x, int y, int dir) {
setWall(new Point(x, y), dir);
}
public void clearWall(Point pt, int dir) {
switch (dir) {
case SOUTH: map[pt.x][pt.y].wallSouth = false; break;
case WEST: map[pt.x][pt.y].wallWest = false; break;
case NORTH: map[pt.x][pt.y + 1].wallSouth = false; break;
case EAST: map[pt.x + 1][pt.y].wallWest = false; break;
}
updateCorner(pt);
if (sqSize >= DOUBLE_WALL_THRESHOLD) {
Point left = adjacentPoint(pt, leftFrom(dir));
Point right = adjacentPoint(pt, rightFrom(dir));
updateCorner(left);
updateCorner(right);
updateCorner(adjacentPoint(left, dir));
updateCorner(adjacentPoint(pt, dir));
updateCorner(adjacentPoint(right, dir));
}
}
public void clearWall(int x, int y, int dir) {
clearWall(new Point(x, y), dir);
}
public void updateCorner(int x, int y) {
updateCorner(new Point(x, y));
}
public void updateCorner(Point pt) {
Rectangle r = getCornerRect(pt);
if (repaintFlag) repaint(r.x, r.y, r.width, r.height);
}
public static String directionName(int dir) {
switch (dir) {
case SOUTH: return "SOUTH";
case WEST: return "WEST";
case NORTH: return "NORTH";
case EAST: return "EAST";
}
return null;
}
public static int leftFrom(int dir) {
switch (dir) {
case SOUTH: return EAST;
case WEST: return SOUTH;
case NORTH: return WEST;
case EAST: return NORTH;
}
return -1;
}
public static int rightFrom(int dir) {
switch (dir) {
case SOUTH: return WEST;
case WEST: return NORTH;
case NORTH: return EAST;
case EAST: return SOUTH;
}
return -1;
}
public static int oppositeDirection(int dir) {
switch (dir) {
case SOUTH: return NORTH;
case WEST: return EAST;
case NORTH: return SOUTH;
case EAST: return WEST;
}
return -1;
}
public static Point adjacentPoint(Point pt, int dir) {
return adjacentPoint(pt.x, pt.y, dir);
}
public static Point adjacentPoint(int x, int y, int dir) {
switch (dir) {
case SOUTH: return new Point(x, y - 1);
case WEST: return new Point(x - 1, y);
case NORTH: return new Point(x, y + 1);
case EAST: return new Point(x + 1, y);
}
return null;
}
public void repaint() {
if (repaintFlag) super.repaint();
}
public void update(Graphics g) {
paint(g);
}
public void paint(Graphics g) {
if (map == null || !displayFlag) return;
synchronized (sizeLock) {
if (offscreen == null) {
Dimension size = getSize();
offscreen = createImage(size.width, size.height);
}
Graphics osg = offscreen.getGraphics();
drawEmptyWorld(osg);
for (int pass = 0; pass < 2; pass++) {
for (int x = 1; x <= cols + 1; x++) {
for (int y = 1; y <= rows + 1; y++) {
boolean mustPaint = false;
if (getKarelOnSquare(x, y) != null) {
mustPaint = true;
} else if (map[x][y].color != null) {
mustPaint = true;
} else if (map[x][y].nBeepers != 0) {
mustPaint = true;
} else if (x > 1 && map[x][y].wallWest) {
mustPaint = true;
} else if (y > 1 && map[x][y].wallSouth) {
mustPaint = true;
} else if (x < cols && map[x + 1][y].wallWest) {
mustPaint = true;
} else if (y < rows && map[x][y + 1].wallSouth) {
mustPaint = true;
}
if (mustPaint) {
if (pass == 0) {
updateContents(osg, new Point(x, y));
} else {
updateWalls(osg, new Point(x, y));
}
}
}
}
}
drawWorldFrame(osg);
}
g.drawImage(offscreen, 0, 0, this);
}
public void trace() {
if (monitor != null) monitor.trace();
}
protected void setMonitor(KarelWorldMonitor monitor) {
this.monitor = monitor;
}
protected KarelWorldMonitor getMonitor() {
return monitor;
}
/* Protected hooks */
protected void componentResizedHook() {
setDisplayParameters(cols, rows);
repaint();
}
protected void mousePressedHook(MouseEvent e) {
if (editMode) {
lastClick = "";
Point pt = getClickCorner(e.getX(), e.getY());
if (pt == null) {
activeKarel = null;
checkForWallClick(e.getX(), e.getY());
} else {
activeKarel = getKarelOnSquare(pt.x, pt.y);
if (activeKarel == null) checkForCornerClick(pt);
}
}
}
protected void mouseDraggedHook(MouseEvent e) {
if (!editMode) return;
if (activeKarel != null) {
Point pt = getClickCorner(e.getX(), e.getY());
if (pt != null && !pt.equals(activeKarel.getLocation())) {
activeKarel.setLocation(pt);
repaint();
}
} else {
if (!checkForWallClick(e.getX(), e.getY())) checkForCornerClick(e.getX(), e.getY());
}
}
/* Click handling methods */
private boolean checkForWallClick(int mx, int my) {
double sx = (double) (mx - leftMargin + sqSize / 2) / sqSize;
double sy = (double) (getSize().height - my - bottomMargin - 1 + sqSize / 2) / sqSize;
int tx = (int) (sx + 0.5);
int ty = (int) (sy + 0.5);
int dir;
if (Math.abs(Math.abs(sx - tx) - 0.5) <= WALL_TOLERANCE && Math.abs(sy - ty) < WALL_FRACTION) {
if (tx > sx) tx--;
if (tx < 0 || tx > cols || ty < 1 || ty > rows) return false;
dir = EAST;
} else if (Math.abs(Math.abs(sy - ty) - 0.5) <= WALL_TOLERANCE && Math.abs(sx - tx) < WALL_FRACTION) {
if (ty > sy) ty--;
if (tx < 1 || tx > cols || ty < 0 || ty > rows) return false;
dir = NORTH;
} else {
return false;
}
String click = tx + "/" + ty + "/" + dir;
if (!click.equals(lastClick)) {
if (monitor != null) monitor.wallAction(new Point(tx, ty), dir);
lastClick = click;
}
return true;
}
private boolean checkForCornerClick(int mx, int my) {
return checkForCornerClick(getClickCorner(mx, my));
}
private boolean checkForCornerClick(Point pt) {
if (pt == null) return false;
String click = pt.x + "/" + pt.y;
if (!click.equals(lastClick)) {
if (monitor != null) monitor.cornerAction(pt);
lastClick = click;
}
return true;
}
private Point getClickCorner(int mx, int my) {
double sx = (double) (mx - leftMargin + sqSize / 2) / sqSize;
double sy = (double) (getSize().height - my - bottomMargin - 1 + sqSize / 2) / sqSize;
int tx = (int) (sx + 0.5);
int ty = (int) (sy + 0.5);
if (tx < 1 || tx > cols || ty < 1 || ty > rows) return null;
if (Math.abs(Math.abs(sx - tx) - 0.5) * sqSize <= 1) return null;
if (Math.abs(Math.abs(sy - ty) - 0.5) * sqSize <= 1) return null;
return new Point(tx, ty);
}
private static String encodeColor(Color color) {
if (color.equals(Color.BLACK)) return "BLACK";
if (color.equals(Color.BLUE)) return "BLUE";
if (color.equals(Color.CYAN)) return "CYAN";
if (color.equals(Color.DARK_GRAY)) return "DARK_GRAY";
if (color.equals(Color.GRAY)) return "GRAY";
if (color.equals(Color.GREEN)) return "GREEN";
if (color.equals(Color.LIGHT_GRAY)) return "LIGHT_GRAY";
if (color.equals(Color.MAGENTA)) return "MAGENTA";
if (color.equals(Color.ORANGE)) return "ORANGE";
if (color.equals(Color.PINK)) return "PINK";
if (color.equals(Color.RED)) return "RED";
if (color.equals(Color.WHITE)) return "WHITE";
if (color.equals(Color.YELLOW)) return "YELLOW";
return "0x" + Integer.toString(color.getRGB() & 0xFFFFFF).toUpperCase();
}
private static Color decodeColor(String name) {
if (name.equalsIgnoreCase("black")) return Color.BLACK;
if (name.equalsIgnoreCase("blue")) return Color.BLUE;
if (name.equalsIgnoreCase("cyan")) return Color.CYAN;
if (name.equalsIgnoreCase("darkGray") || name.equalsIgnoreCase("DARK_GRAY")) return Color.DARK_GRAY;
if (name.equalsIgnoreCase("gray")) return Color.GRAY;
if (name.equalsIgnoreCase("green")) return Color.GREEN;
if (name.equalsIgnoreCase("lightGray") || name.equalsIgnoreCase("LIGHT_GRAY")) return Color.LIGHT_GRAY;
if (name.equalsIgnoreCase("magenta")) return Color.MAGENTA;
if (name.equalsIgnoreCase("orange")) return Color.ORANGE;
if (name.equalsIgnoreCase("pink")) return Color.PINK;
if (name.equalsIgnoreCase("red")) return Color.RED;
if (name.equalsIgnoreCase("white")) return Color.WHITE;
if (name.equalsIgnoreCase("yellow")) return Color.YELLOW;
return Color.decode(name);
}
private void setDisplayParameters(int cols, int rows) {
offscreen = null;
int usableWidth = getSize().width - ((numberSquaresFlag) ? LEFT_NUMBER_MARGIN : 2);
int usableHeight = getSize().height - ((numberSquaresFlag) ? BOTTOM_NUMBER_MARGIN : 0) - 2;
this.width = cols;
this.height = rows;
if (forcedSize == 0) {
sqSize = Math.min((int) (usableWidth / cols), (int) (usableHeight / rows));
} else {
sqSize = forcedSize;
}
width = cols * sqSize;
height = rows * sqSize;
switch (alignment) {
case NORTHWEST: case WEST: case SOUTHWEST:
leftMargin = (numberSquaresFlag) ? LEFT_NUMBER_MARGIN : 2;
break;
case NORTH: case CENTER: case SOUTH:
leftMargin = ((numberSquaresFlag) ? LEFT_NUMBER_MARGIN : 2) + (usableWidth - width) / 2;
break;
case NORTHEAST: case EAST: case SOUTHEAST:
leftMargin = getSize().width - width - 1;
break;
}
switch (alignment) {
case NORTHWEST: case NORTH: case NORTHEAST:
bottomMargin = getSize().height - height - 2;
break;
case WEST: case CENTER: case EAST:
bottomMargin = ((numberSquaresFlag) ? BOTTOM_NUMBER_MARGIN : 0) + (usableHeight - height) / 2;
break;
case SOUTHWEST: case SOUTH: case SOUTHEAST:
bottomMargin = (numberSquaresFlag) ? BOTTOM_NUMBER_MARGIN : 0;
break;
}
}
private void drawEmptyWorld(Graphics g) {
if (g == null) return;
Dimension size = getSize();
g.setColor(Color.WHITE);
g.fillRect(0, 0, size.width, size.height);
g.setColor(Color.BLACK);
int x = leftMargin + sqSize / 2;
for (int ix = 1; ix <= cols; ix++) {
int y = getSize().height - bottomMargin - (sqSize + 1) / 2 - 1;
for (int iy = 1; iy <= rows; iy++) {
drawCornerMarker(g, x, y);
y -= sqSize;
}
x += sqSize;
}
}
private void drawWorldFrame(Graphics g) {
if (g == null) return;
g.setColor(Color.BLACK);
int x = leftMargin;
int y = getSize().height - bottomMargin - 1 - height;
if (sqSize >= DOUBLE_WALL_THRESHOLD) {
g.drawRect(x, y, width - 1, height - 1);
g.drawRect(x - 1, y - 1, width + 1, height + 1);
} else {
g.drawRect(x - 1, y - 1, width, height);
}
if (sqSize > NUMBER_THRESHOLD && numberSquaresFlag) {
g.setFont(NUMBER_FONT);
FontMetrics fm = g.getFontMetrics();
x = leftMargin + sqSize / 2;
y = getSize().height - bottomMargin + 10;
for (int ix = 1; ix <= cols; ix++) {
String label = "" + ix;
g.drawString(label, x - fm.stringWidth(label) / 2, y);
x += sqSize;
}
x = leftMargin - 3;
y = getSize().height - bottomMargin - sqSize / 2 + 2;
for (int iy = 1; iy <= rows; iy++) {
g.drawString("" + iy, x - g.getFontMetrics().stringWidth("" + iy), y);
y -= sqSize;
}
}
}
public void updateContents(Graphics g, Point pt) {
if (g == null) return;
if (outOfBounds(pt)) return;
int x = leftMargin + (pt.x - 1) * sqSize;
int y = getSize().height - bottomMargin - 1 - pt.y * sqSize;
drawCorner(g, x, y, pt);
}
public void drawCorner(Graphics g, int x, int y, Point pt) {
if (g == null) return;
int sqSize = getSquareSize();
Color color = getCornerColor(pt);
g.setColor((color == null) ? Color.WHITE : color);
g.fillRect(x, y, sqSize, sqSize);
int cx = x + sqSize / 2;
int cy = y + sqSize / 2;
int nBeepers = getBeepersOnCorner(pt);
if (nBeepers > 0) {
if (nBeepers == 1 && !displayOneFlag) nBeepers = BLANKB;
drawBeeperForStyle(g, cx, cy, sqSize, nBeepers, 1);
}
Karel karel = getKarelOnSquare(pt.x, pt.y);
if (karel != null) {
drawKarel(g, cx, cy, karel.getDirection(), sqSize);
} else {
if (color == null && nBeepers == 0) drawCornerMarker(g, cx, cy);
}
}
public static void drawMarkedCorner(Graphics g, int x, int y, int size) {
if (g == null) return;
int inset = Math.max(2, size / 5);
g.setColor(Color.WHITE);
g.fillRect(x, y, size, size);
g.setColor(MARKED_COLOR);
g.fillRect(x + inset, y + inset, size - 2 * inset, size - 2 * inset);
}
public void drawKarel(Graphics g, int x, int y, int dir, int size) {
if (g == null) return;
if (size < MIN_FANCY || getLook() == SIMPLE) {
drawSimpleKarel(g, x, y, dir, size);
} else {
drawFancyKarel(g, x, y, dir, size);
}
}
public void drawSimpleKarel(Graphics g, int x, int y, int dir, int size) {
if (g == null) return;
size = (int) Math.round(size * SIMPLE_FRACTION);
if (size % 2 == 0) size--;
int half = (size + 1) / 2;
for (int pass = 1; pass <= 2; pass++) {
KarelRegion r = new KarelRegion();
r.setOrigin(x, y, -half, -half, dir);
r.addVector(half, 0, dir);
r.addVector(half, half, dir);
r.addVector(-half, half, dir);
r.addVector(-half, 0, dir);
r.addVector(0, -size, dir);
if (pass == 1) {
g.setColor(Color.WHITE);
g.fillPolygon(r.getPolygon());
} else {
g.setColor(Color.BLACK);
g.drawPolygon(r.getPolygon());
}
}
}
public void drawFancyKarel(Graphics g, int x, int y, int dir, int size) {
drawFancyKarel(g, x, y, dir, size - KAREL_INSET, Color.WHITE);
}
public static void drawFancyKarel(Graphics g, int x, int y, int dir, int size, Color color) {
if (g == null) return;
for (int pass = 1; pass <= 2; pass++) {
KarelRegion r = new KarelRegion();
r.setOrigin(x, y, BODY_OFFSET_X * size, BODY_OFFSET_Y * size + LOWER_NOTCH * size, dir);
int sx = r.getCurrentX();
int sy = r.getCurrentY();
g.setColor((pass == 1) ? color : Color.BLACK);
r.addVector(0, BODY_HEIGHT * size - LOWER_NOTCH * size, dir);
r.addVector(BODY_WIDTH * size - UPPER_NOTCH * size, 0, dir);
r.addVector(UPPER_NOTCH * size, -UPPER_NOTCH * size, dir);
r.addVector(0, -(BODY_HEIGHT * size - UPPER_NOTCH * size), dir);
r.addVector(-(BODY_WIDTH * size - LOWER_NOTCH * size), 0, dir);
r.addVector(-LOWER_NOTCH * size, LOWER_NOTCH * size, dir);
if (pass == 1) {
r.getPolygon().addPoint(sx, sy);
r.addVector((SCREEN_OFFSET_X - BODY_OFFSET_X) * size,
(SCREEN_OFFSET_Y - BODY_OFFSET_Y - LOWER_NOTCH) * size, dir);
} else {
g.drawPolygon(r.getPolygon());
r = new KarelRegion();
r.setOrigin(sx, sy, (SCREEN_OFFSET_X - BODY_OFFSET_X) * size,
(SCREEN_OFFSET_Y - BODY_OFFSET_Y - LOWER_NOTCH) * size, dir);
}
r.addVector(SCREEN_WIDTH * size, 0, dir);
r.addVector(0, SCREEN_HEIGHT * size, dir);
r.addVector(-SCREEN_WIDTH * size, 0, dir);
r.addVector(0, -SCREEN_HEIGHT * size, dir);
if (pass == 1) {
r.getPolygon().addPoint(sx, sy);
g.fillPolygon(r.getPolygon());
r = new KarelRegion();
r.setOrigin(sx, sy, (SCREEN_OFFSET_X - BODY_OFFSET_X) * size - 1,
(SCREEN_OFFSET_Y - BODY_OFFSET_Y - LOWER_NOTCH) * size - 1, dir);
r.addVector(SCREEN_WIDTH * size + 2, 0, dir);
r.addVector(0, SCREEN_HEIGHT * size + 2, dir);
r.addVector(-(SCREEN_WIDTH * size + 2), 0, dir);
r.addVector(0, -(SCREEN_HEIGHT * size + 2), dir);
g.drawPolygon(r.getPolygon());
} else {
g.drawPolygon(r.getPolygon());
}
}
g.setColor(Color.BLACK);
KarelRegion r = new KarelRegion();
r.setOrigin(x, y, SCREEN_OFFSET_X * size + SCREEN_WIDTH * size,
(SCREEN_OFFSET_Y * size + BODY_OFFSET_Y * size) / 2, dir);
r.addVector(-SLOT_WIDTH * size, 0, dir);
g.drawPolygon(r.getPolygon());
r = new KarelRegion();
r.setOrigin(x, y, BODY_OFFSET_X * size, SCREEN_OFFSET_Y * size, dir);
r.addVector(-(UPPER_ANKLE * size + FOOT_WIDTH * size), 0, dir);
r.addVector(0, -FOOT_LENGTH * size, dir);
r.addVector(FOOT_WIDTH * size, 0, dir);
r.addVector(0, FOOT_LENGTH * size - FOOT_WIDTH * size, dir);
r.addVector(UPPER_ANKLE * size, 0, dir);
r.addVector(0, FOOT_WIDTH * size, dir);
g.fillPolygon(r.getPolygon());
g.drawPolygon(r.getPolygon());
r = new KarelRegion();
r.setOrigin(x, y, SCREEN_OFFSET_X * size + SCREEN_WIDTH * size - SLOT_WIDTH * size,
BODY_OFFSET_Y * size, dir);
r.addVector(0, -(LOWER_ANKLE * size + FOOT_WIDTH * size), dir);
r.addVector(FOOT_LENGTH * size, 0, dir);
r.addVector(0, FOOT_WIDTH * size, dir);
r.addVector(-(FOOT_LENGTH * size - FOOT_WIDTH * size), 0, dir);
r.addVector(0, LOWER_ANKLE * size, dir);
r.addVector(-FOOT_WIDTH * size, 0, dir);
g.fillPolygon(r.getPolygon());
g.drawPolygon(r.getPolygon());
}
public void drawBeeperForStyle(Graphics g, int x, int y, int size, int n, int border) {
String label = "";
if (size > MIN_LABEL && (displayOneFlag || n != 1)) {
label = beeperLabel(n);
}
drawBeeper(g, x, y, size, label, border, this);
}
public static void drawBeeper(Graphics g, int x, int y, int size, int n, int border, Component comp) {
if (g == null) return;
int beeperSize = (int) Math.round(size * BEEPER_FRACTION);
if (beeperSize % 2 == 0) beeperSize--;
int half = (beeperSize + 1) / 2;
KarelRegion r = new KarelRegion();
r.setOrigin(x, y, 0, -half, EAST);
r.addVector(half, half, EAST);
r.addVector(-half, half, EAST);
r.addVector(-half, -half, EAST);
r.addVector(half, -half, EAST);
g.setColor(BEEPER_COLOR);
g.fillPolygon(r.getPolygon());
g.drawPolygon(r.getPolygon());
g.setColor(Color.BLACK);
for (int i = 0; i < border; i++) {
int delta = half + i;
g.drawLine(x - delta, y, x, y + delta);
g.drawLine(x, y + delta, x + delta, y);
g.drawLine(x + delta, y, x, y - delta);
g.drawLine(x, y - delta, x - delta, y);
}
if (size > MIN_LABEL && n != 1) {
labelBeeper(g, x, y, size, beeperLabel(n), comp);
}
}
public static void drawBeeper(Graphics g, int x, int y, int size, String label, int border, Component comp) {
if (g == null) return;
int beeperSize = (int) Math.round(size * BEEPER_FRACTION);
if (beeperSize % 2 == 0) beeperSize--;
int half = (beeperSize + 1) / 2;
KarelRegion r = new KarelRegion();
r.setOrigin(x, y, 0, -half, EAST);
r.addVector(half, half, EAST);
r.addVector(-half, half, EAST);
r.addVector(-half, -half, EAST);
r.addVector(half, -half, EAST);
g.setColor(BEEPER_COLOR);
g.fillPolygon(r.getPolygon());
g.drawPolygon(r.getPolygon());
g.setColor(Color.BLACK);
for (int i = 0; i < border; i++) {
int delta = half + i;
g.drawLine(x - delta, y, x, y + delta);
g.drawLine(x, y + delta, x + delta, y);
g.drawLine(x + delta, y, x, y - delta);
g.drawLine(x, y - delta, x - delta, y);
}
labelBeeper(g, x, y, size, label, comp);
}
public static void labelBeeper(Graphics g, int x, int y, int size, String label, Component comp) {
if (label.equals("%")) {
if (infinityImage == null) {
infinityImage = MediaTools.createImage(INFINITY);
}
g.drawImage(infinityImage, x - 4, y - 2, comp);
} else {
int psz = 7;
switch (label.length()) {
case 1: case 2: psz = 10; break;
default: psz = 7; break;
}
Font font = new Font(BEEPER_FONT_FAMILY, Font.PLAIN, psz);
g.setFont(font);
FontMetrics fm = g.getFontMetrics();
g.drawString(label, x - fm.stringWidth(label) / 2, y + fm.getAscent() / 2);
}
}
public static String beeperLabel(int n) {
switch (n) {
case INFINITE: return "%";
case PLUS1: return "+1";
case MINUS1: return "-1";
case BLANKB: return "";
default: return "" + n;
}
}
public void updateWalls(Graphics g, Point pt) {
if (g == null) return;
if (outOfBounds(pt)) return;
int x = leftMargin + (pt.x - 1) * sqSize;
int y = getSize().height - bottomMargin - 1 - pt.y * sqSize;
g.setColor(Color.BLACK);
for (int dir = NORTH; dir <= WEST; dir++) {
if (checkWall(pt, dir)) drawWall(g, x, y, dir);
if (sqSize < DOUBLE_WALL_THRESHOLD) fixCornerPoint(g, pt, dir);
}
}
private void drawWall(Graphics g, int x, int y, int dir) {
if (g == null) return;
int x0, y0, x1, y1;
switch (dir) {
case NORTH:
x0 = x;
y0 = y;
x1 = x0 + sqSize;
y1 = y0;
break;
case EAST:
x0 = x + sqSize;
y0 = y;
x1 = x0;
y1 = y0 + sqSize;
break;
case SOUTH:
x0 = x;
y0 = y + sqSize;
x1 = x0 + sqSize;
y1 = y0;
break;
case WEST:
x0 = x;
y0 = y;
x1 = x0;
y1 = y0 + sqSize;
break;
default:
x0 = y0 = x1 = y1 = 0;
break;
}
if (sqSize < DOUBLE_WALL_THRESHOLD) {
g.drawLine(x0 - 1, y0 - 1, x1 - 1, y1 - 1);
} else {
if (x0 == x1) {
g.drawLine(x0 - 1, y0 - 1, x1 - 1, y1);
g.drawLine(x0, y0 - 1, x1, y1);
} else {
g.drawLine(x0 - 1, y0 - 1, x1, y1 - 1);
g.drawLine(x0 - 1, y0, x1, y1);
}
}
}
public void drawCornerMarker(Graphics g, int x, int y) {
if (g == null) return;
g.setColor(Color.BLACK);
if (sqSize < CROSS_THRESHOLD) {
g.drawLine(x, y, x, y);
} else {
g.drawLine(x - 1, y, x + 1, y);
g.drawLine(x, y - 1, x, y + 1);
}
}
private void fixCornerPoint(Graphics g, Point pt, int dir) {
int left = leftFrom(dir);
Point pUp = adjacentPoint(pt, dir);
Point pLeft = adjacentPoint(pt, left);
if (!outOfBounds(pUp) && checkWall(pUp, left)) {
int x = leftMargin + (pUp.x - 1) * sqSize;
int y = getSize().height - bottomMargin - 1 - pUp.y * sqSize;
drawWall(g, x, y, left);
} else if (!outOfBounds(pLeft) && checkWall(pLeft, dir)) {
int x = leftMargin + (pLeft.x - 1) * sqSize;
int y = getSize().height - bottomMargin - 1 - pLeft.y * sqSize;
drawWall(g, x, y, dir);
}
}
private Rectangle getCornerRect(Point pt) {
int x = leftMargin + (pt.x - 1) * sqSize;
int y = getSize().height - bottomMargin - 1 - pt.y * sqSize;
return new Rectangle(x - 1, y - 1, sqSize + 2, sqSize + 2);
}
/* File saving */
public void save() {
if (pathname == null) return;
Point pt = new Point(0, 0);
try {
PrintWriter wr = new PrintWriter(new FileWriter(pathname));
wr.println("Dimension: (" + cols + ", " + rows + ")");
for (pt.x = 1; pt.x <= cols; pt.x++) {
for (pt.y = 1; pt.y <= rows; pt.y++) {
if (pt.x > 1 && checkWall(pt, WEST)) {
wr.println("Wall: (" + pt.x + ", " + pt.y + ") west");
}
if (pt.y > 1 && checkWall(pt, SOUTH)) {
wr.println("Wall: (" + pt.x + ", " + pt.y + ") south");
}
}
}
for (pt.x = 1; pt.x <= cols; pt.x++) {
for (pt.y = 1; pt.y <= rows; pt.y++) {
Color color = getCornerColor(pt);
if (color != null) {
wr.println("Color: (" + pt.x + ", " + pt.y + ") " + encodeColor(color));
}
int nBeepers = getBeepersOnCorner(pt);
if (nBeepers != 0) {
String str = (nBeepers == INFINITE) ? "INFINITE" : "" + nBeepers;
wr.println("Beeper: (" + pt.x + ", " + pt.y + ") " + str);
}
}
}
Iterator iterator = karels.iterator();
while (iterator.hasNext()) {
Karel karel = iterator.next();
String dirName = "Error";
switch (karel.getDirection()) {
case NORTH: dirName = "north"; break;
case EAST: dirName = "east"; break;
case SOUTH: dirName = "south"; break;
case WEST: dirName = "west"; break;
}
Point loc = karel.getLocation();
wr.print("Karel: (" + loc.x + ", " + loc.y + ") " + dirName);
int nBeepers = karel.getBeepersInBag();
if (nBeepers != 0) {
String str = (nBeepers == INFINITE) ? "INFINITE" : "" + nBeepers;
if (getKarelCount() == 1) {
wr.println();
wr.println("BeeperBag: " + str);
} else {
wr.println(" " + str);
}
}
}
if (monitor != null) {
wr.println("Speed: " + speedFormat.format(monitor.getSpeed()));
}
wr.close();
} catch (IOException ex) {
throw new ErrorException("" + ex);
}
Platform.setFileTypeAndCreator(pathname, "TEXT", "CWIE");
}
/* File loading */
public void load(String lines[]) {
String program = "";
for (int i = 0; i < lines.length; i++) {
program += lines[i] + '\n';
}
load(new StringReader(program));
}
public void load(File file) {
try {
pathname = file.getPath();
Reader rd = new FileReader(file);
if (rd == null) throw new ErrorException("Can't open " + pathname);
load(rd);
rd.close();
} catch (IOException ex) {
throw new ErrorException("I/O error reading map file");
}
}
public void load(String pathname) {
try {
this.pathname = pathname;
Reader rd = new FileReader(pathname);
if (rd == null) throw new ErrorException("Can't open " + pathname);
load(rd);
rd.close();
} catch (IOException ex) {
throw new ErrorException("I/O error reading map file");
}
}
public void load(Reader rd) {
try {
setRepaintFlag(false);
lastBeeperCount = INFINITE;
tokenizer = new StreamTokenizer(rd);
tokenizer.eolIsSignificant(true);
tokenizer.lowerCaseMode(true);
tokenizer.resetSyntax();
tokenizer.wordChars('A', 'Z');
tokenizer.wordChars('a', 'z');
tokenizer.wordChars('0', '9');
tokenizer.wordChars('.', '.');
tokenizer.wordChars('_', '_');
tokenizer.whitespaceChars(' ', ' ');
tokenizer.whitespaceChars('\t', '\t');
tokenizer.whitespaceChars('\r', '\r');
while (readMapLine()) {
/* Empty */
}
rd.close();
setRepaintFlag(true);
repaint();
} catch (IOException ex) {
setRepaintFlag(true);
throw new ErrorException("I/O error reading map file");
}
}
private boolean readMapLine() {
int token = nextToken();
switch (token) {
case StreamTokenizer.TT_EOF: return false;
case StreamTokenizer.TT_EOL: return true;
case StreamTokenizer.TT_WORD:
String cmd = tokenizer.sval;
if (nextToken() != ':') {
throw new ErrorException("Missing colon after " + cmd);
}
if (cmd.equals("dimension")) {
dimensionCommand();
} else if (cmd.equals("karel") || cmd.equals("turtle")) {
karelCommand();
} else if (cmd.equals("wall")) {
wallCommand();
} else if (cmd.equals("mark") || cmd.equals("color")) {
setColorCommand();
} else if (cmd.equals("speed")) {
speedCommand();
} else if (cmd.equals("beeper")) {
beeperCommand();
} else if (cmd.equals("beeperbag")) {
beeperBagCommand();
} else {
throw new ErrorException("Illegal command: " + cmd);
}
break;
default:
throw new ErrorException("Illegal character '" + (char) token + "'");
}
return (true);
}
private void dimensionCommand() {
verifyToken('(');
int cols = scanInt();
verifyToken(',');
int rows = scanInt();
verifyToken(')');
verifyToken(StreamTokenizer.TT_EOL);
init(cols, rows);
}
private void karelCommand() {
Point pt = new Point(0, 0);
int dir = EAST;
int nBeepers = lastBeeperCount;
verifyToken('(');
pt.x = scanInt();
verifyToken(',');
pt.y = scanInt();
verifyToken(')');
if (nextToken() != StreamTokenizer.TT_WORD) {
throw new ErrorException("Illegal direction");
}
if ("north".startsWith(tokenizer.sval)) {
dir = NORTH;
} else if ("east".startsWith(tokenizer.sval)) {
dir = EAST;
} else if ("south".startsWith(tokenizer.sval)) {
dir = SOUTH;
} else if ("west".startsWith(tokenizer.sval)) {
dir = WEST;
} else {
throw new ErrorException("Illegal direction " + tokenizer.sval);
}
int token = nextToken();
if (token == StreamTokenizer.TT_WORD) {
if ("infinite".startsWith(tokenizer.sval) || "infinity".startsWith(tokenizer.sval)) {
nBeepers = INFINITE;
} else {
try {
nBeepers = Integer.parseInt(tokenizer.sval);
} catch (NumberFormatException ex) {
throw new ErrorException("Illegal beeper bag value");
}
}
token = nextToken();
}
if (token != StreamTokenizer.TT_EOL) {
throw new ErrorException("Unexpected tokens at end of line");
}
lastKarel = getKarel();
if (lastKarel != null) {
lastKarel.setLocation(pt.x, pt.y);
lastKarel.setDirection(dir);
lastKarel.setBeepersInBag(nBeepers);
}
}
private void wallCommand() {
Point pt = new Point(0, 0);
int dir = EAST;
verifyToken('(');
pt.x = scanInt();
verifyToken(',');
pt.y = scanInt();
verifyToken(')');
if (nextToken() != StreamTokenizer.TT_WORD) {
throw new ErrorException("Illegal direction");
}
if ("north".startsWith(tokenizer.sval)) {
dir = NORTH;
} else if ("east".startsWith(tokenizer.sval)) {
dir = EAST;
} else if ("south".startsWith(tokenizer.sval)) {
dir = SOUTH;
} else if ("west".startsWith(tokenizer.sval)) {
dir = WEST;
} else {
throw new ErrorException("Illegal direction " + tokenizer.sval);
}
verifyToken(StreamTokenizer.TT_EOL);
setWall(pt, dir);
}
private void setColorCommand() {
Point pt = new Point(0, 0);
String colorName = null;
verifyToken('(');
pt.x = scanInt();
verifyToken(',');
pt.y = scanInt();
verifyToken(')');
int tt = nextToken();
if (tt != StreamTokenizer.TT_EOL) {
if (tt != StreamTokenizer.TT_WORD) throw new ErrorException("Missing color name");
colorName = tokenizer.sval.toLowerCase();
verifyToken(StreamTokenizer.TT_EOL);
}
setCornerColor(pt, decodeColor(colorName));
}
private void speedCommand() {
if (nextToken() != StreamTokenizer.TT_WORD) {
throw new ErrorException("I expected a number");
}
double speed = new Double(tokenizer.sval).doubleValue();
verifyToken(StreamTokenizer.TT_EOL);
if (monitor != null) monitor.setSpeed(speed);
}
protected void beeperBagCommand() {
if (nextToken() != StreamTokenizer.TT_WORD) {
throw new ErrorException("Illegal beeper count");
}
int nBeepers = 0;
if ("infinite".startsWith(tokenizer.sval) || "infinity".startsWith(tokenizer.sval)) {
nBeepers = INFINITE;
} else {
tokenizer.pushBack();
nBeepers = scanInt();
}
verifyToken(StreamTokenizer.TT_EOL);
if (lastKarel == null) {
lastBeeperCount = nBeepers;
} else {
lastKarel.setBeepersInBag(nBeepers);
}
}
protected void beeperCommand() {
Point pt = new Point(0, 0);
verifyToken('(');
pt.x = scanInt();
verifyToken(',');
pt.y = scanInt();
verifyToken(')');
int nBeepers = 1;
int token = nextToken();
if (token != StreamTokenizer.TT_EOL) {
if (token != StreamTokenizer.TT_WORD) {
throw new ErrorException("Illegal beeper count");
}
if ("infinite".startsWith(tokenizer.sval) || "infinity".startsWith(tokenizer.sval)) {
nBeepers = INFINITE;
} else {
tokenizer.pushBack();
nBeepers = scanInt();
}
verifyToken(StreamTokenizer.TT_EOL);
}
setBeepersOnCorner(pt, nBeepers);
}
protected void ignoreCommand() {
while (nextToken() != StreamTokenizer.TT_EOL) {
/* Empty */
}
}
private void verifyToken(int token) {
if (nextToken() != token) {
if (token == StreamTokenizer.TT_EOL) {
throw new ErrorException("Unexpected tokens at end of line");
}
throw new ErrorException("I expected a '" + (char) token + "'");
}
}
private int scanInt() {
if (nextToken() != StreamTokenizer.TT_WORD) {
throw new ErrorException("I expected an integer");
}
try {
return Integer.parseInt(tokenizer.sval);
} catch (NumberFormatException ex) {
throw new ErrorException("Illegal integer");
}
}
private int nextToken() {
int token = 0;
try {
token = tokenizer.nextToken();
} catch (IOException ex) {
throw new ErrorException("Exception: " + ex);
}
if (TOKEN_TRACE) {
switch (token) {
case StreamTokenizer.TT_EOF: System.out.println(""); break;
case StreamTokenizer.TT_EOL: System.out.println(); break;
case StreamTokenizer.TT_WORD:
System.out.print(tokenizer.sval + " ");
break;
default:
System.out.print((char) token + " ");
break;
}
}
return ((token == '\n') ? StreamTokenizer.TT_EOL : token);
}
/* Private constants */
private static final String INFINITY[] = {
"47494638396109000600F70000FFFFFF980098339999989800111111222222000054CBFFCB003298",
"0033660033CC0033FE00323266330066660000659800989800CC9900FE99329800659800CC0099FE",
"0098659898999999CC9900FE98009800329800659900CC9800FE3399CB3399FF9999339898659832",
"0098650099339998659833CB9833FF9999CC0099FE00336699656698CC9898FF9999323200336600",
"32003233006632009833339965009866339900663300983200666600986500CC3300FE3200CC6600",
"FE65CCCC98CCFF99FFCC99FFFF993300CC3200FE6600CC6500FECC0033CC0066FE0032FE00653399",
"33339966669933669865CC00CCCB00FEFE00CBFE00FE6699CC6598FF9898CC9999FFCB9833CC9966",
"FF9933FF9865333333326532323265326565660033653232660066653265CC3300CC6600FE3200FE",
"65000066CC0099CC0066FE0098FE00CCCC00FECB00CCFE00FEFE33CC0033FE0066CC0066FE00CB33",
"98CC6699FF3399FF659866CC9965FF9898CC9899FF99CCCC00CCFE00FECB00FEFE00993333996633",
"9933669865659833CB9966CC9933FF9865FF33CBCB33FFCC33CCFF33FFFF99CB3399FF3399CC6698",
"FF65CC98CCCCCCCCCC99FFCBCBFFFF99CCFFCBCBFF99FFFFCBFF3333CB3366CB3333FF3366FF6533",
"CB6666CC6633FF6565FFCB3333CB6533CB3365CC6666FF3333FF6633FF3366FF656533CB3333FF33",
"33CB6633FF6666CB3366FF3366CC6665FF65CB33CBCC66CCCC33FFCC65FFFF33CCFF65CCFF33FFFF",
"65FF66CCCC65FFCC65CCFF65FFFF98CCCC99FFCC99CCFF99FFFFCBCB33CCFF33CCCC66CCFF65FFCC",
"33FFFF33FFCC65FFFF65444444656532DDDDDDCBFFFFFFFFCBEEEEEE100000980000001000660000",
"000098000066777777888888AAAAAABBBBBB5555556666660000100000224400005400000000CC00",
"00DC0000EE0000FE00003200004400880000980000AA0000BA0000CC0000DC0000EE0000FE00CC00",
"00DC0000EE0000FE0000004400005400006600007600220000320000AA0000BA0000002200003200",
"7600008800000000AA0000BA00007600008800000021F90401000090002C0000000009000600C7FF",
"FFFF980098339999989800111111222222000054CBFFCB0032980033660033CC0033FE0032326633",
"0066660000659800989800CC9900FE99329800659800CC0099FE0098659898999999CC9900FE9800",
"9800329800659900CC9800FE3399CB3399FF99993398986598320098650099339998659833CB9833",
"FF9999CC0099FE00336699656698CC9898FF99993232003366003200323300663200983333996500",
"9866339900663300983200666600986500CC3300FE3200CC6600FE65CCCC98CCFF99FFCC99FFFF99",
"3300CC3200FE6600CC6500FECC0033CC0066FE0032FE0065339933339966669933669865CC00CCCB",
"00FEFE00CBFE00FE6699CC6598FF9898CC9999FFCB9833CC9966FF9933FF98653333333265323232",
"65326565660033653232660066653265CC3300CC6600FE3200FE65000066CC0099CC0066FE0098FE",
"00CCCC00FECB00CCFE00FEFE33CC0033FE0066CC0066FE00CB3398CC6699FF3399FF659866CC9965",
"FF9898CC9899FF99CCCC00CCFE00FECB00FEFE009933339966339933669865659833CB9966CC9933",
"FF9865FF33CBCB33FFCC33CCFF33FFFF99CB3399FF3399CC6698FF65CC98CCCCCCCCCC99FFCBCBFF",
"FF99CCFFCBCBFF99FFFFCBFF3333CB3366CB3333FF3366FF6533CB6666CC6633FF6565FFCB3333CB",
"6533CB3365CC6666FF3333FF6633FF3366FF656533CB3333FF3333CB6633FF6666CB3366FF3366CC",
"6665FF65CB33CBCC66CCCC33FFCC65FFFF33CCFF65CCFF33FFFF65FF66CCCC65FFCC65CCFF65FFFF",
"98CCCC99FFCC99CCFF99FFFFCBCB33CCFF33CCCC66CCFF65FFCC33FFFF33FFCC65FFFF6544444465",
"6532DDDDDDCBFFFFFFFFCBEEEEEE100000980000001000660000000098000066777777888888AAAA",
"AABBBBBB5555556666660000100000224400005400000000CC0000DC0000EE0000FE000032000044",
"00880000980000AA0000BA0000CC0000DC0000EE0000FE00CC0000DC0000EE0000FE000000440000",
"5400006600007600220000320000AA0000BA00000022000032007600008800000000AA0000BA0000",
"7600008800000008190021091C4810D2BF7F06110A54C870E0C1840E174A2C4830200021FF0B4D41",
"4347436F6E2004031039000000015772697474656E20627920474946436F6E76657274657220322E",
"342E33206F66204D6F6E6461792C204D61792032352C2031393938003B"
};
private static final int KAREL_INSET = 6;
private static final double BODY_OFFSET_X = -0.20;
private static final double BODY_OFFSET_Y = -0.33;
private static final double BODY_WIDTH = 0.60;
private static final double BODY_HEIGHT = 0.80;
private static final double UPPER_NOTCH = 0.15;
private static final double LOWER_NOTCH = 0.10;
private static final double SCREEN_OFFSET_X = -0.07;
private static final double SCREEN_OFFSET_Y = -0.05;
private static final double SCREEN_WIDTH = 0.30;
private static final double SCREEN_HEIGHT = 0.40;
private static final double SLOT_WIDTH = 0.15;
private static final double FOOT_WIDTH = 0.08;
private static final double FOOT_LENGTH = 0.20;
private static final double UPPER_ANKLE = 0.08;
private static final double LOWER_ANKLE = 0.08;
private static Image infinityImage;
private StreamTokenizer tokenizer;
private KarelWorldMonitor monitor;
private Karel activeKarel, lastKarel;
private boolean repaintFlag, displayFlag, editMode, numberSquaresFlag, displayOneFlag;
private int cols, rows, sqSize, forcedSize, alignment;
private int width, height, leftMargin, bottomMargin;
private String lastClick;
private String pathname, title;
private Corner map[][];
private int look;
private int lastBeeperCount;
private NumberFormat speedFormat;
private ArrayList karels;
private Object sizeLock;
private Image offscreen;
}
class Corner {
public Color color;
public boolean wallSouth;
public boolean wallWest;
public int nBeepers;
}
class KarelWorldListener implements MouseListener, MouseMotionListener, ComponentListener {
public KarelWorldListener(KarelWorld world) {
this.world = world;
}
public void mousePressed(MouseEvent e) { world.mousePressedHook(e); }
public void mouseClicked(MouseEvent e) { }
public void mouseReleased(MouseEvent e) { }
public void mouseEntered(MouseEvent e) { }
public void mouseExited(MouseEvent e) { }
public void mouseDragged(MouseEvent e) { world.mouseDraggedHook(e); }
public void mouseMoved(MouseEvent e) { }
public void componentResized(ComponentEvent e) { world.componentResizedHook(); }
public void componentMoved(ComponentEvent e) { }
public void componentShown(ComponentEvent e) { }
public void componentHidden(ComponentEvent e) { }
private KarelWorld world;
}
class KarelRegion {
public static final double EPSILON = 0.00000000001;
public KarelRegion() {
p = new Polygon();
x = y = 0;
}
public Polygon getPolygon() {
return p;
}
public void setOrigin(int x, int y, double dx, double dy, int dir) {
if (p.npoints != 0) {
throw new ErrorException("setOrigin called on nonempty region");
}
this.x = x;
this.y = y;
addVector(dx, dy, dir);
}
public void addVector(double dx, double dy, int dir) {
switch (dir) {
case KarelWorld.EAST: x += dx; y -= dy; break;
case KarelWorld.NORTH: x -= dy; y -= dx; break;
case KarelWorld.WEST: x -= dx; y += dy; break;
case KarelWorld.SOUTH: x += dy; y += dx; break;
}
p.addPoint((int) Math.round(x + EPSILON), (int) Math.round(y + EPSILON));
}
public int getCurrentX() {
return p.xpoints[p.npoints - 1];
}
public int getCurrentY() {
return p.ypoints[p.npoints - 1];
}
/* Private state */
private Polygon p;
private double x, y;
}
interface KarelWorldMonitor {
/*
* Method: startWorldEdit
* Usage: startWorldEdit();
* ------------------------
* This action is invoked at the beginning of an editing session.
*/
public void startWorldEdit();
/* Method: endWorldEdit */
/**
* This action is invoked at the end of an editing session.
*/
public void endWorldEdit();
/* Method: wallAction */
/**
* This action is invoked when the mouse is clicked on a wall, which
* is the wall in the indicated direction from the Karel coordinates
* given by pt.
*/
public void wallAction(Point pt, int dir);
/* Method: cornerAction */
/**
* This action is invoked when the mouse is clicked on a corner, which
* is the wall in the indicated direction from the given point.
*/
public void cornerAction(Point pt);
/* Method: trace */
/**
* This action is invoked when karel executes an instruction.
*/
public void trace();
/* Method: setSpeed */
/**
* This method is invoked when a world map file needs to set the simulation
* speed.
*/
public void setSpeed(double speed);
/* Method: getSpeed */
/**
* This method is invoked when the KarelWorld class needs to get the simulation
* speed.
*/
public double getSpeed();
}