package org.artisanlogiciel.games; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Container; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.LinkedList; import java.util.Scanner; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSlider; import javax.swing.KeyStroke; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import org.artisanlogiciel.games.stl.Wall3d; /** * Display **/ public class Display extends JFrame { // to please eclipse, not supposed to be serialized private static final long serialVersionUID = 8500214871372184418L; MazeComponent maze; MazeControler controler; LabyModel model; JPanel controlPanel; Display(LabyModel model, MazeComponent comp) { super("Maze display"); maze = comp; this.model = model; Container con = this.getContentPane(); con.add(new JScrollPane(comp), BorderLayout.CENTER); controler = new MazeControler(); con.add(controler, BorderLayout.NORTH); } void resolve() { model.resolve(model.getWidth() - 1, model.getHeight() - 1, maze); } void goNorth() { maze.goNorth(); } void goSouth() { maze.goSouth(); } void goEast() { maze.goEast(); } void goWest() { maze.goWest(); } void setWallSize(int size) { maze.setWallSize(size); } private static class MazeParamEditor implements MazeParams { int width; int height; int maxdepth; File labdir; String name; public MazeParamEditor(File saveDir) { name = null; labdir = saveDir; } public void read(Scanner console) { width = console.nextInt(); height = console.nextInt(); maxdepth = console.nextInt(); } public int getWidth() { return width; } public int getHeight() { return height; } public int getMaxDepth() { return maxdepth; } public String getName() { if (name == null) { name = "lab" + width + "x" + height; } return name; } public File getSaveDir() { return labdir; } } private class MazeControler extends JPanel { /** * */ private static final long serialVersionUID = 1L; public MazeControler() { controlPanel = new JPanel(); JButton button = new JButton("Resolve"); button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { // System.out.println("Resolving"); resolve(); } }); @SuppressWarnings("serial") Action goNorth = new AbstractAction() { public void actionPerformed(ActionEvent evt) { // System.out.println("go North"); goNorth(); } }; JButton north = addDirection(this, "North", "UP", goNorth); @SuppressWarnings("serial") Action goEast = new AbstractAction() { public void actionPerformed(ActionEvent evt) { // System.out.println("go East"); goEast(); } }; JButton east = addDirection(this, "East", "RIGHT", goEast); @SuppressWarnings("serial") Action goWest = new AbstractAction() { public void actionPerformed(ActionEvent evt) { // System.out.println("go West"); goWest(); } }; JButton west = addDirection(this, "West", "LEFT", goWest); @SuppressWarnings("serial") Action goSouth = new AbstractAction() { public void actionPerformed(ActionEvent evt) { // System.out.println("go South"); goSouth(); } }; JButton south = addDirection(this, "South", "DOWN", goSouth); add(button, BorderLayout.CENTER); controlPanel.add(north, BorderLayout.NORTH); controlPanel.add(west, BorderLayout.WEST); controlPanel.add(east, BorderLayout.EAST); controlPanel.add(south, BorderLayout.SOUTH); add(controlPanel, BorderLayout.NORTH); final JSlider slider = new JSlider(2, 40); slider.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { setWallSize(slider.getValue()); } }); add(slider, BorderLayout.EAST); } } private JButton addDirection(final JPanel panel, final String direction, String key, Action goAction) { String actionName = "go" + direction; JButton button = new JButton(direction); button.addActionListener(goAction); KeyStroke keystroke = KeyStroke.getKeyStroke(key); panel.getInputMap().put(keystroke, actionName); panel.getActionMap().put(actionName, goAction); controlPanel.getInputMap().put(keystroke, actionName); controlPanel.getActionMap().put(actionName, goAction); return button; } private static class MazeComponent extends JComponent implements MazeCreationListener, MazeResolutionListener { private static final long serialVersionUID = 3163272907991176390L; WallsProvider map; int width = 10; int height = 10; int offsetX = 5; int offsetY = 5; Position current = null; LinkedList solvedPath = null; final Object lockChange = new Object(); // current postion of human resolution int sX = 0; int sY = 0; // goal exit int gX = -1; int gY = -1; void checkExit() { if ((sX == gX) && (sY == gY)) { System.out.println("Exit found by human !"); } } void goNorth() { resetResolution(); if ((map.getWalls(sX, sY) & Brick.UP) == 0) { sY = sY - 1; // should redraw ... invalidate(); repaint(); checkExit(); } } void goSouth() { resetResolution(); if ((map.getWalls(sX, sY) & Brick.DOWN) == 0) { sY = sY + 1; // should redraw ... invalidate(); repaint(); checkExit(); } } void goEast() { resetResolution(); if ((map.getWalls(sX, sY) & Brick.RIGHT) == 0) { sX = sX + 1; // should redraw ... invalidate(); repaint(); checkExit(); } } void goWest() { resetResolution(); if ((map.getWalls(sX, sY) & Brick.LEFT) == 0) { sX = sX - 1; // should redraw ... invalidate(); repaint(); checkExit(); } } public MazeComponent(WallsProvider map, int width, int height, int offsetX, int offsetY) { super(); this.map = map; this.width = width; this.height = height; this.offsetX = offsetX; this.offsetY = offsetY; setPreferredSize(new Dimension(1 + 2 * offsetX + map.getWidth() * width, 1 + 2 * offsetY + map.getHeight() * height)); gX = map.getWidth() - 1; gY = map.getHeight() - 1; } public void setWallSize(int size) { width = size; height = size; // should redraw ... invalidate(); repaint(); } public void changed(Position cl, Position cr, WallsProvider map) { // should redraw ... invalidate(); repaint(); Object waiter = new Object(); synchronized (waiter) { try { waiter.wait(10); } catch (InterruptedException e) { System.err.println("Interrupted !"); } } } private void drawWalls(Graphics g, int x, int y, short walls, boolean full) { if ((full) && ((walls & Brick.UP) == Brick.UP)) g.drawLine(x, y, x + width, y); if ((walls & Brick.DOWN) == Brick.DOWN) g.drawLine(x, y + height, x + width, y + height); if ((walls & Brick.RIGHT) == Brick.RIGHT) g.drawLine(x + width, y, x + width, y + height); if ((full) && ((walls & Brick.LEFT) == Brick.LEFT)) g.drawLine(x, y, x, y + height); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); int x = 0; int y = 0; short walls = 0; // try to display only visible part... // should compute pX, pY, mX, mY based on clip... // Shape s=g.getClip(); Rectangle r = g.getClipBounds(); int mX = (int) ((double) r.getWidth() / width); int mY = (int) ((double) r.getHeight() / height); int pX = (int) ((double) (r.getX() - offsetX) / width); int pY = (int) ((double) (r.getY() - offsetY) / height); if (pX < 0) pX = 0; if (pY < 0) pY = 0; if (pX >= map.getWidth()) return; if (pY >= map.getHeight()) return; mX = mX + pX; mY = mY + pY; if (mX > map.getWidth()) { mX = map.getWidth(); } if (mY > map.getHeight()) { mY = map.getHeight(); } if ((sX == gX) && (sY == gY)) { g.setColor(Color.red); } else { g.setColor(Color.blue); } drawDot(g, new Position(sX, sY), pX, pY, mX, mY); synchronized (lockChange) { g.setColor(Color.red); if (current != null) { drawDot(g, current, pX, pY, mX, mY); } if (solvedPath != null) { for (Position resolved : solvedPath) { drawDot(g, resolved, pX, pY, mX, mY); } } } g.setColor(Color.black); for (; pY < mY; pY++) { for (pX = 0; pX < mX; pX++) { x = offsetX + pX * width; y = offsetY + pY * height; walls = map.getWalls(pX, pY); drawWalls(g, x, y, walls, (pX == 0) || (pY == 0)); } } } private void drawDot(Graphics g, Position dot, int pX, int pY, int mX, int mY) { int radius = (height > width) ? width : height; int a = radius / 4; if ((dot.getX() >= pX) && (dot.getY() >= pY) && (dot.getX() < mX) && (dot.getY() < mY)) { int x = offsetX + dot.getX() * width; int y = offsetY + dot.getY() * height; g.drawOval(x + 1, y + 1, radius - a, radius - a); // g.drawLine(x+a,y+a,x+width-a,y+height-a); // g.drawLine(x+a,y+height-a,x+width-a,y+a); } else { int x = offsetX + pX * width; int y = offsetY + pY * height; g.drawLine(x + 1, y + 1, x + width - 1, y + height - 1); } } public boolean notifySearch(Position pPosition) { synchronized (lockChange) { current = pPosition; } // should redraw ... invalidate(); repaint(); return true; } public void notifySearchError(String error) { System.err.println(error); } public void notifyCompletion(LinkedList solvedPath) { LinkedList newPath = new LinkedList<>(solvedPath); System.out.println("resolution completed"); synchronized (lockChange) { this.solvedPath = newPath; } // should redraw ... invalidate(); repaint(); } public void resetResolution() { solvedPath = null; } } public static void main(String pArgs[]) { LabyModel model = null; int W = 600; int H = 400; if (pArgs.length > 0) { try { model = new LabyModel("raw", new FileInputStream(pArgs[0])); } catch (IOException io) { io.printStackTrace(System.err); } int w = W / model.getWidth(); int h = H / model.getHeight(); if (w < 5) w = 5; if (h < 5) h = 5; MazeComponent comp = new MazeComponent(model, w, h, 3, 3); Display display = new Display(model, comp); model.setMazeListener(comp); display.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); display.setBounds(W, H, W, H); display.setVisible(true); } else { System.out.println("enter width height and maxdepth"); MazeParamEditor params = new MazeParamEditor(new File("lab")); params.read(new Scanner(System.in)); model = new LabyModel(params, new java.util.Random()); int w = W / params.getWidth(); int h = H / params.getHeight(); if (w < 5) w = 5; if (h < 5) h = 5; MazeComponent comp = new MazeComponent(model, w, h, 3, 3); Display display = new Display(model, comp); model.setMazeListener(comp); display.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); display.setBounds(W, H, W, H); display.setVisible(true); model.generateWithEntry(0, 0); model.addEntryOrExit(-1, 0); model.addEntryOrExit(params.getWidth(), params.getHeight() - 1); System.out.println("Generation completed"); File outfile = new File(params.getSaveDir(), params.getName() + ".raw"); if (!outfile.exists()) { System.out.println("Saving to " + outfile + " ..."); try { FileOutputStream out = new FileOutputStream(outfile); model.streamOut("raw", out); out.flush(); out.close(); System.out.println("... Done."); } catch (IOException io) { io.printStackTrace(System.err); } } else { System.out.println("" + outfile + " already exists"); } outfile = new File(params.getSaveDir(), params.getName() + ".stl"); if (!outfile.exists()) { System.out.println("Saving to " + outfile + " ..."); try { FileOutputStream out = new FileOutputStream(outfile); Wall3d.streamWallsOut(params.getName(), (WallsProvider) model, out); out.flush(); out.close(); System.out.println("... Done."); } catch (IOException io) { io.printStackTrace(System.err); } } else { System.out.println("" + outfile + " already exists"); } } } }