diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/laby.iml b/.idea/laby.iml new file mode 100644 index 0000000..216b054 --- /dev/null +++ b/.idea/laby.iml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/artgaphics_0_1_0.xml b/.idea/libraries/artgaphics_0_1_0.xml new file mode 100644 index 0000000..f00ac9e --- /dev/null +++ b/.idea/libraries/artgaphics_0_1_0.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..bcd4766 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..b142ba3 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/LISEZMOI b/LISEZMOI index ea87c6e..1f87cb0 100644 --- a/LISEZMOI +++ b/LISEZMOI @@ -5,6 +5,14 @@ Il s'agit d'un générateur de labyrinthes écrit en langage java. Pour le lancer vous devez avoir installé un environnement java 8 ( avec javafx (*) ) +racourçi : + +sous linux ./laby.sh + +sous windows lancez le laby.bat + +il s'gait d'un porgramme java qui peut aussi être lancé avec l'interpréteur java ainsi : + java -jar artloglaby-1.0.jar mail pl@artisanlogiciel.net @@ -14,5 +22,6 @@ vous pouvez le distribuer ou bien même le vendre ses sources sont sur https://github.com/artlog/labystl ( et utilise aussi https://github.com/artlog/sharedrawweb ) +Les labyrinthes que vous sauvegardez le sont dans le répertoire lab. diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..e413b6a --- /dev/null +++ b/Makefile.am @@ -0,0 +1,2 @@ +SUBDIRS = c +dist_doc_DATA = README diff --git a/README b/README index c1423d6..b7e1822 100644 --- a/README +++ b/README @@ -3,7 +3,8 @@ This is a personal project to generate a 2D maze using in depth path generation. Download the 'latest' laby.zip containing java jar from my blog http://blog.artisanlogiciel.net/public/tech/laby.zip -this can generate a stl file, overall goal was to print it with a 3D printer, i didn't test it yet ( even if i actualy have a 3D printer ). +this can generate a stl file, overall goal was to print it with a 3D printer. +After years i finaly printed one maze, find it at https://www.thingiverse.com/thing:2814655 This is the very first usable part. It was developped under debian 7/8 but since it is java based it might be very easily recompiled or even just copied on other platform. @@ -25,6 +26,8 @@ cd labystl ./init.sh # will create build scripts from artlog_toolbox # ./doit.sh +# depends on sharedrawweb for stl export +./fetch_dependencies.sh ant dist source ./specificdoit.sh; create_zip_package unzip laby.zip; cd artloglaby ; ./laby.sh diff --git a/bootstrapbuild.sh b/bootstrapbuild.sh new file mode 100755 index 0000000..c7957c4 --- /dev/null +++ b/bootstrapbuild.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +if [[ ! -f configure.ac ]] +then + echo "[ERROR] Missing configure.ac . Please create one " >&2 + exit 1 +fi + +if which autoreconf +then + + echo "bootstrap using automake tools" + echo "from configure.ac will generate configure" + + autoreconf --install + + echo "lauch ./configure && make" + +else + + echo "[ERROR] Missing autoreconf script from autotools. Please install autotools ( package autotools-dev and autoreconf" >&2 + +fi diff --git a/build.xml b/build.xml index 973e799..d99e9ad 100644 --- a/build.xml +++ b/build.xml @@ -10,10 +10,10 @@ - + - - + + @@ -26,6 +26,7 @@ + @@ -37,8 +38,8 @@ - - + + @@ -50,5 +51,4 @@ - diff --git a/c/Makefile.am b/c/Makefile.am new file mode 100644 index 0000000..fe8ba1c --- /dev/null +++ b/c/Makefile.am @@ -0,0 +1,12 @@ +# Makefile.am is REALLY where changes should be done +# Makefile.in and Makefile are generated from this. + +bin_PROGRAMS = laby + +laby_SOURCES = allaby_reader.c allaby_main.c + +laby_CPPFLAGS = $(ARTLOG_CPPFLAGS) $(ALEXPANDER_CPPFLAGS) + +# in LDADD and not in LDFLAGS to be linked AFTER +laby_LDADD = $(ARTLOG_LDFLAGS) $(ALEXPANDER_LDFLAGS) + diff --git a/c/allaby_main.c b/c/allaby_main.c new file mode 100644 index 0000000..6284847 --- /dev/null +++ b/c/allaby_main.c @@ -0,0 +1,17 @@ +#include "al_options.h" + +// NOT YET IMPLEMENTED +int main(int argc, char ** argv) +{ + int exitcode = 1; + struct al_options * options = al_options_create(argc,argv); + al_options_set_debug(options,0); + struct alhash_datablock * debugdata = al_option_get(options,"debug"); + if ( debugdata != NULL ) + { + exitcode = 0; + } + al_options_release(options); + + return exitcode; +} diff --git a/c/allaby_reader.c b/c/allaby_reader.c new file mode 100644 index 0000000..feefc82 --- /dev/null +++ b/c/allaby_reader.c @@ -0,0 +1,2 @@ +#include "allaby_reader.h" + diff --git a/c/allaby_reader.h b/c/allaby_reader.h new file mode 100644 index 0000000..92f85e0 --- /dev/null +++ b/c/allaby_reader.h @@ -0,0 +1,9 @@ +#ifndef __ALLABY_READER_H__ +#define __ALLABY_READER_H__ + +/** goal : read a labyrinth/maze created +by java laby project +**/ + + +#endif // __ALLABY_READER_H__ diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..01758a0 --- /dev/null +++ b/configure.ac @@ -0,0 +1,37 @@ +# written manually, this is root entry files for automake together with Makefile.am +# inspired from +# http://www.gnu.org/software/automake/manual/html_node/Creating-amhello.html#Creating-amhello + +AC_INIT([allaby], [0.1], [pl@artisanlogiciel.net]) + +# set ARTLOG_TOOLBOX where artlog_toolbox is to use it. +[ARTLOG_TOOLBOX=`pwd`/`./locate_artlog_toolbox.sh`] + +# non need to propagate it in .h ... +# AC_DEFINE_UNQUOTED([ARTLOG_TOOLBOX],["$ARTLOG_TOOLBOX"],[where artog toolbox is]) + +ARTLOG_CPPFLAGS="-I${ARTLOG_TOOLBOX}/build/include" +ARTLOG_LDFLAGS="-Wl,-Bstatic -L${ARTLOG_TOOLBOX}/build/lib -lalsave -laltest -laljson -lalstack -lalhash -laldev -Wl,-Bdynamic" + +AC_SUBST(ARTLOG_CPPFLAGS) +AC_SUBST(ARTLOG_LDFLAGS) + +[if [ "$ALEXPANDER_DIR" = "" ]] +[then] +[ALEXPANDER_DIR=`pwd`/../sharedrawweb] +[fi] +ALEXPANDER_CPPFLAGS="-I${ALEXPANDER_DIR}/build/include -D CHARSPERRECORD=80" +ALEXPANDER_LDFLAGS="-Wl,-Bstatic -L${ALEXPANDER_DIR}/build/lib -lalima -lalcommon -Wl,-Bdynamic" + +AC_SUBST(ALEXPANDER_CPPFLAGS) +AC_SUBST(ALEXPANDER_LDFLAGS) + +AM_INIT_AUTOMAKE([-Wall -Werror foreign]) +AC_PROG_CC +AC_CONFIG_HEADERS([config.h]) +AC_DEFINE([MAX_FLAT_VIEWS],[20],[maximum number of view]) +AC_CONFIG_FILES([ + Makefile + c/Makefile +]) +AC_OUTPUT diff --git a/fetch_dependencies.sh b/fetch_dependencies.sh new file mode 100755 index 0000000..0eeff37 --- /dev/null +++ b/fetch_dependencies.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +if [[ ! -d ../sharedrawweb ]] +then + echo "[ERROR] expected a sharedrawweb project parent ( for exports )" >&2 +fi + +if [[ ! -d libs ]] +then + mkdir libs +fi + +if [[ ! -e libs/artgaphics-0.1.0.jar ]] +then + ln -s ../../sharedrawweb/dist/lib/artgaphics-0.1.0.jar libs/ +fi diff --git a/java/Makefile b/java/Makefile index 90904b5..1b58e23 100644 --- a/java/Makefile +++ b/java/Makefile @@ -11,8 +11,8 @@ $(OUT): # mkdir -p $(OUT) clean: - @find $(PACKAGE_DIR) -name "*.class" -type f -print0|xargs -0 rm 2>/dev/null && echo "cleaned classes in source" - @find $(OUT) -name "*.class" -type f -print0|xargs -0 rm 2>/dev/null || echo "nothing to clean" + @find $(PACKAGE_DIR) -name "*.class" -type f -print0|xargs -0 --no-run-if-empty rm 2>/dev/null + @find $(OUT) -name "*.class" -type f -print0|xargs -0 --no-run-if-empty rm 2>/dev/null test: echo "$(pwd)/$(PACKAGE_DIR)" diff --git a/java/org/artisanlogiciel/games/DirectionPosition.java b/java/org/artisanlogiciel/games/DirectionPosition.java index 96567ac..f11f88a 100644 --- a/java/org/artisanlogiciel/games/DirectionPosition.java +++ b/java/org/artisanlogiciel/games/DirectionPosition.java @@ -1,77 +1,60 @@ package org.artisanlogiciel.games; /** - DirectionPosition - -record direction and position ( direction as defined in LabyModel ). + * DirectionPosition + *

+ * record direction and position ( direction as defined in LabyModel ). **/ -public class DirectionPosition -{ +public class DirectionPosition { private short direction; private Position position; // (direction,position) - DirectionPosition(short d, Position p) - { - direction=d; - position=p; + public DirectionPosition(short d, Position p) { + direction = d; + position = p; } - short getDirection() - { - return direction; + public short getDirection() { + return direction; } - void setDirection(short d) - { - direction = d; + public void setDirection(short d) { + direction = d; } - Position getPosition() - { - return position; + public Position getPosition() { + return position; } - public String toString() - { - if (position != null) - { - return position.toString(); - } - return "?"; + public String toString() { + if (position != null) { + return position.toString(); + } + return "?"; } // create a new DirectionPosition from this using one possible direction. - DirectionPosition moveToAdjacentDirection() - { - short pointingdirection = 0; - Position p = null; - if (LabyModel.isFlagSet(direction,LabyModel.RIGHT)) - { - p = new Position(position.getX() + 1, position.getY()); - pointingdirection |= LabyModel.LEFT; - } - else if (LabyModel.isFlagSet(direction,LabyModel.LEFT)) - { - p = new Position(position.getX() -1 ,position.getY()); - pointingdirection |= LabyModel.RIGHT; - } - else if (LabyModel.isFlagSet(direction,LabyModel.UP)) - { - p = new Position(position.getX(),position.getY() -1); - pointingdirection |= LabyModel.DOWN; - } - else if (LabyModel.isFlagSet(direction,LabyModel.DOWN)) - { - p = new Position(position.getX(),position.getY() + 1); - pointingdirection |= LabyModel.UP; - } - else - { - p = position; - } + public DirectionPosition moveToAdjacentDirection() { + short pointingdirection = 0; + Position p = null; + if (LabyModel.isFlagSet(direction, LabyModel.RIGHT)) { + p = new Position(position.getX() + 1, position.getY()); + pointingdirection |= LabyModel.LEFT; + } else if (LabyModel.isFlagSet(direction, LabyModel.LEFT)) { + p = new Position(position.getX() - 1, position.getY()); + pointingdirection |= LabyModel.RIGHT; + } else if (LabyModel.isFlagSet(direction, LabyModel.UP)) { + p = new Position(position.getX(), position.getY() - 1); + pointingdirection |= LabyModel.DOWN; + } else if (LabyModel.isFlagSet(direction, LabyModel.DOWN)) { + p = new Position(position.getX(), position.getY() + 1); + pointingdirection |= LabyModel.UP; + } else { + p = position; + } - return new DirectionPosition((short) pointingdirection, p); + return new DirectionPosition((short) pointingdirection, p); } } diff --git a/java/org/artisanlogiciel/games/Display.java b/java/org/artisanlogiciel/games/Display.java deleted file mode 100644 index b83903d..0000000 --- a/java/org/artisanlogiciel/games/Display.java +++ /dev/null @@ -1,1375 +0,0 @@ -package org.artisanlogiciel.games; - -import java.awt.BorderLayout; -import java.awt.image.BufferedImage; -import java.awt.Color; -import java.awt.Container; -import java.awt.Dimension; -import java.awt.FlowLayout; -import java.awt.Graphics; -import java.awt.Point; -import java.awt.Rectangle; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.ComponentAdapter; -import java.awt.event.ComponentEvent; -import java.awt.event.InputEvent; -import java.awt.event.MouseEvent; -import java.awt.event.MouseMotionListener; -import java.io.DataOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.LinkedList; -import java.util.Locale; -import java.util.Scanner; -import java.util.ResourceBundle; - -import javax.imageio.ImageIO; - -import javax.swing.AbstractAction; -import javax.swing.Action; -import javax.swing.BoxLayout; -import javax.swing.JButton; -import javax.swing.JComponent; -import javax.swing.JCheckBox; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JSlider; -import javax.swing.JTextField; -import javax.swing.KeyStroke; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; - -import org.artisanlogiciel.games.stl.Wall3d; -import org.artisanlogiciel.util.UTF8Control; - -import org.artisanlogiciel.graphics.Drawing; -import org.artisanlogiciel.graphics.DrawingLine; -import org.artisanlogiciel.graphics.SvgWriter; - -/** - * Display is Main JFrame for this tool - **/ -public class Display extends JFrame -{ - // to please eclipse, not supposed to be serialized - private static final long serialVersionUID = 8500214871372184418L; - - final static ResourceBundle labels = ResourceBundle.getBundle("LabelsBundle", Locale.getDefault(), Display.class.getClassLoader(), new UTF8Control()); - - MazeComponent maze; - MazeControler controler; - LabyModel model; - JPanel controlPanel; - boolean autoSize; - - MazeParams params = null; - - Drawing drawing = new Drawing(); - - Display(LabyModel model,int W, int H, MazeParams params) - { - super(labels.getString("title")); - if (params != null) - { - // fixedParams = new MazeParamsFixed(params.getSaveDir(),params.getWidth(),params.getHeight(),params.getMaxDepth()); - this.params = params; - } - this.model = model; - maze = createMazeComponent(model,W,H); - - Container con = this.getContentPane(); - JScrollPane scrollableMaze = new JScrollPane(maze); - scrollableMaze.addMouseMotionListener(maze); - con.add(scrollableMaze, BorderLayout.CENTER); - controler = new MazeControler(params); - con.add(controler.getMoveControl(), BorderLayout.NORTH); - con.add(controler.getGenerationControl(), BorderLayout.SOUTH); - - addComponentListener(new ComponentAdapter() { - @Override - public void componentResized(ComponentEvent e) { - if ( autoSize ) - { - maze.getAutoSize(); - } - } - }); - - model.setMazeListener(maze); - setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - setBounds(W, H, W, H); - setVisible(true); - } - - private static MazeComponent createMazeComponent(LabyModel model, int W, int H) - { - MazeCellParameters cp = new MazeCellParameters(model.getWidth(),model.getHeight(), W, H, 3, 3); - MazeComponent comp = new MazeComponent(model, cp); - return comp; - } - - void recreateModel() - { - // recreate labyrinth - if (params != null) - { - params = controler.getSettings().getParams(); - // keep current model... - // model = new LabyModel(params, new java.util.Random()); - maze.resetWallsProvider(model); - model.setMazeListener(maze); - - model.reset(); - - // we are in GUI event thread, need to release it and do it outside. - new Thread() { - public void run() { - model.generateWithEntry(0, 0); - model.addEntryOrExit(-1, 0); - model.addEntryOrExit(params.getWidth(), params.getHeight() - 1); - } - }.start(); - } - } - - void resetModel() - { - // recreate labyrinth - if (params != null) - { - params = controler.getSettings().getParams(); - model = new LabyModel(params, new java.util.Random()); - maze.resetWallsProvider(model); - model.setMazeListener(maze); - - - // we are in GUI event thread, need to release it and do it outside. - new Thread() { - public void run() { - maze.changed(null,null,model); - } - }.start(); - - - } - } - - void resolve() - { - model.reset(); - 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); - } - - void setAutoSize(boolean autoSize) - { - this.autoSize = autoSize; - if ( autoSize ) - { - maze.getAutoSize(); - } - } - - void addWallInDrawing(int pX, int pY, Drawing d) - { - short walls = model.getWalls(pX,pY); - short wdrawn = 0; - // todo - int w = 2; - int h = 2; - int ox=0; - int oy=0; - - int x = ox + (int) (pX * w); - int y = oy + (int) (pY * h); - DrawingLine dl = new DrawingLine(); - // order matters since all points are linked - if ( (pY == 0) && LabyModel.isFlagSet(walls,Brick.UP) ) - { - dl.addPoint(new Point(x, y)); - dl.addPoint(new Point(x + (int) w, y)); - wdrawn |= Brick.UP; - } - if (LabyModel.isFlagSet(walls,Brick.RIGHT)) - { - if ( ! LabyModel.isFlagSet(wdrawn,Brick.UP) ) - { - dl.addPoint(new Point(x + (int) w, y)); - } - dl.addPoint(new Point(x + (int) w, y + (int) h)); - wdrawn |= Brick.RIGHT; - } - if (LabyModel.isFlagSet(walls,Brick.DOWN)) - { - if ( ! LabyModel.isFlagSet(wdrawn,Brick.RIGHT) ) - { - if (wdrawn != 0) - { - d.addLine(dl); - dl = new DrawingLine(); - } - dl.addPoint(new Point(x + (int) w, y + (int) h)); - } - dl.addPoint(new Point(x, y + (int) h)); - wdrawn |= Brick.DOWN; - } - if ( (pX == 0 ) && LabyModel.isFlagSet(walls,Brick.LEFT)) - { - if ( ! LabyModel.isFlagSet(wdrawn,Brick.DOWN) ) - { - if (wdrawn != 0) - { - d.addLine(dl); - dl = new DrawingLine(); - } - dl.addPoint(new Point(x, y + (int) h)); - } - dl.addPoint(new Point(x, y)); - wdrawn |= Brick.LEFT; - } - if (wdrawn != 0) - { - d.addLine(dl); - } - } - - Drawing createDrawing() - { - Drawing d = new Drawing(); - { - short walls; - - // draw all walls within clip bounds horiz first then lines - for (int y = 0; y < model.getHeight(); y++) - { - for (int x = 0; x < model.getWidth(); x ++) - { - addWallInDrawing(x,y,d); - } - } - } - return d; - } - - // to allow to log / write somewher into screen... - void writeSentence(String pSentence) - { - // TODO - System.out.println( pSentence); - } - - void writeError(String pError) - { - System.err.println(pError); - } - - void saveImc() - { - Drawing d = createDrawing(); - - if ( d != null ) - { - File outfile = new File(params.getSaveDir(), params.getName() + ".imc"); - writeSentence("Saving to " + outfile + " ..."); - try - { - DataOutputStream out = new DataOutputStream(new FileOutputStream(outfile)); - d.saveLinesKompressed(out); - out.flush(); - out.close(); - writeSentence("... Done."); - } - catch (IOException io) - { - io.printStackTrace(System.err); - } - } - } - - void saveSvg() - { - Drawing d = createDrawing(); - - if ( d != null ) - { - File outfile = new File(params.getSaveDir(), params.getName() + ".svg"); - writeSentence("Saving to " + outfile + " ..."); - try - { - DataOutputStream out = new DataOutputStream(new FileOutputStream(outfile)); - SvgWriter writer = new SvgWriter(d.getInternLines()); - writer.writeTo(out); - out.flush(); - out.close(); - writeSentence("... Done."); - } - catch (IOException io) - { - io.printStackTrace(System.err); - } - } - else - { - writeError("drawing creation failed"); - } - } - - - - void savePng() - { - File file = new File("snapshot.png"); - // BufferedImage bi = new BufferedImage(this.getSize().width, this.getSize().height, BufferedImage.TYPE_INT_ARGB); - BufferedImage bi = new BufferedImage(maze.getSize().width, maze.getSize().height, BufferedImage.TYPE_INT_ARGB); - Graphics g = bi.createGraphics(); - // this.paint(g); - maze.paint(g); - g.dispose(); - try{ - ImageIO.write(bi,"png",file); - }catch (Exception e) { - e.printStackTrace(); - } - } - - private class MazeSettings extends JPanel - { - MazeParams params; - - JTextField textWidth = null; - JTextField textHeight = null; - JTextField textDepth = null; - - // TODO set width and height and depth of maze with gui - public MazeSettings(MazeParams params) - { - super(); - this.params = params; - createSettingsGui(); - } - - void createSettingsGui() - { - if ( params != null ) - { - JButton buttonCreate = new JButton(labels.getString("create")); - buttonCreate.addActionListener(new ActionListener() - { - public void actionPerformed(ActionEvent evt) - { - recreateModel(); - } - }); - add(buttonCreate); - - JButton buttonReset = new JButton("reset");//labels.getString("reset")); - buttonReset.addActionListener(new ActionListener() - { - public void actionPerformed(ActionEvent evt) - { - resetModel(); - } - }); - add(buttonReset); - - } - if ( params != null ) - { - JLabel widthLabel = new JLabel(labels.getString("width")); - textWidth = new JTextField("0" + params.getWidth()); - add(widthLabel); - add(textWidth); - JLabel heightLabel = new JLabel(labels.getString("height")); - textHeight = new JTextField("0" + params.getHeight()); - add(heightLabel); - add(textHeight); - JLabel depthLabel = new JLabel(labels.getString("depth")); - textDepth = new JTextField("0" + params.getMaxDepth()); - add(depthLabel); - add(textDepth); - } - } - - public MazeParams getParams() - { - return new MazeParamsFixed(params.getSaveDir(), - Integer.parseInt(textWidth.getText()), - Integer.parseInt(textHeight.getText()), - Integer.parseInt(textDepth.getText())); - } - - } - - private static class MazeParamsFixed implements MazeParams - { - int width; - int height; - int maxdepth; - File labdir; - String name; - - public MazeParamsFixed() - { - } - - public MazeParamsFixed(MazeParams params) - { - setParams(params.getSaveDir(),params.getWidth(),params.getHeight(),params.getMaxDepth()); - } - - public void setParams(File saveDir, int W, int H, int MD) - { - labdir = saveDir; - width=W; - height=H; - maxdepth=MD; - } - - public MazeParamsFixed(File saveDir, int W, int H, int MD) - { - name = null; - setParams(saveDir,W,H,MD); - } - - public int getWidth() - { - return width; - } - - public int getHeight() - { - return height; - } - - public int getMaxDepth() - { - return maxdepth; - } - - public void setName(String n) - { - name = n; - } - - 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; - - private MazeSettings settings = null; - - private JPanel resizecontrol = null; - - private void setMazeName(String pName) - { - MazeParamsFixed p = (MazeParamsFixed) params; - p.setName(pName); - } - - private JPanel createSavingBar() - { - final JTextField saveName = new JTextField("newlaby "); - final JButton savePngButton = new JButton(labels.getString("save") +" png"); - Action savePngAction = new AbstractAction() { - public void actionPerformed(ActionEvent evt) - { - // - System.out.println("save png"); - setMazeName(saveName.getText()); - savePng(); - } - }; - savePngButton.addActionListener(savePngAction); - final JButton saveSvgButton = new JButton(labels.getString("save") +" svg"); - Action saveSvgAction = new AbstractAction() { - public void actionPerformed(ActionEvent evt) - { - writeSentence("save png"); - setMazeName(saveName.getText()); - saveSvg(); - } - }; - saveSvgButton.addActionListener(saveSvgAction); - final JButton saveButton = new JButton(labels.getString("save") +" raw"); - Action saveAction = new AbstractAction() { - public void actionPerformed(ActionEvent evt) - { - // - System.out.println("save"); - setMazeName(saveName.getText()); - MazeParamsFixed p = (MazeParamsFixed) params; - save(p,model); - } - }; - saveButton.addActionListener(saveAction); - final JButton saveImcButton = new JButton(labels.getString("save") +" imc"); - Action saveImcAction = new AbstractAction() { - public void actionPerformed(ActionEvent evt) - { - // - System.out.println("save imc"); - setMazeName(saveName.getText()); - saveImc(); - } - }; - saveImcButton.addActionListener(saveImcAction); - - JPanel savePanel = new JPanel(new FlowLayout()); - savePanel.add(saveName); - savePanel.add(saveSvgButton); - savePanel.add(savePngButton); - savePanel.add(saveButton); - savePanel.add(saveImcButton); - - return savePanel; - - } - - private JPanel createResolveQuitBar() - { - JPanel resolveQuitBar = new JPanel(new FlowLayout()); - JButton resolveButton = new JButton(labels.getString("resolve")); - resolveButton.addActionListener(new ActionListener() - { - public void actionPerformed(ActionEvent evt) - { - // - System.out.println("Resolving"); - resolve(); - } - }); - - resolveQuitBar.add(resolveButton); - - final JButton quitButton = new JButton(labels.getString("quit")); - Action quitAction = new AbstractAction() { - public void actionPerformed(ActionEvent evt) - { - // - System.out.println("quit"); - System.exit(0); - } - }; - quitButton.addActionListener(quitAction); - - resolveQuitBar.add(quitButton); - - return resolveQuitBar; - - } - - public MazeControler(MazeParams params) - { - // super(new BorderLayout()); - super(); - BoxLayout layout = new BoxLayout(this,BoxLayout.Y_AXIS); - setLayout(layout); - - add(createSavingBar()); - - add(createResolveQuitBar()); - - controlPanel = new JPanel(new BorderLayout()); - settings = new MazeSettings(params); - @SuppressWarnings("serial") - Action goNorth = new AbstractAction() - { - public void actionPerformed(ActionEvent evt) - { - // - System.out.println("go North"); - goNorth(); - } - }; - JButton north = addDirection(this, labels.getString("north"), "UP", goNorth); - - @SuppressWarnings("serial") - Action goEast = new AbstractAction() - { - public void actionPerformed(ActionEvent evt) - { - // - System.out.println("go East"); - goEast(); - } - }; - - JButton east = addDirection(this, labels.getString("east"), "RIGHT", goEast); - - @SuppressWarnings("serial") - Action goWest = new AbstractAction() - { - public void actionPerformed(ActionEvent evt) - { - // - System.out.println("go West"); - goWest(); - } - }; - JButton west = addDirection(this, labels.getString("west"), "LEFT", goWest); - - @SuppressWarnings("serial") - Action goSouth = new AbstractAction() - { - public void actionPerformed(ActionEvent evt) - { - // - System.out.println("go South"); - goSouth(); - } - }; - JButton south = addDirection(this, labels.getString("south"), "DOWN", goSouth); - - controlPanel.add(north, BorderLayout.NORTH); - controlPanel.add(west, BorderLayout.WEST); - controlPanel.add(east, BorderLayout.EAST); - controlPanel.add(south, BorderLayout.SOUTH); - add(controlPanel, BorderLayout.NORTH); - - // control display panel contains controls for display - // zoom , autozoom. - final JPanel controlDisplayPanel = new JPanel(new BorderLayout()); - final JCheckBox showAll = new JCheckBox("show all"); - showAll.addChangeListener(new ChangeListener() - { - public void stateChanged(ChangeEvent e) - { - maze.setShowAll( showAll.isSelected()); - } - }); - - final JSlider slider = new JSlider(2, 40); - slider.addChangeListener(new ChangeListener() - { - public void stateChanged(ChangeEvent e) - { - setWallSize(slider.getValue()); - } - }); - final JCheckBox autoSlide = new JCheckBox("autoslide"); - autoSlide.addChangeListener(new ChangeListener() - { - public void stateChanged(ChangeEvent e) - { - setAutoSize( autoSlide.isSelected()); - } - }); - - resizecontrol = new JPanel(new FlowLayout()); - resizecontrol.add(showAll); - resizecontrol.add(slider); - resizecontrol.add(autoSlide); - - add(controlDisplayPanel, BorderLayout.SOUTH); - add(resizecontrol,BorderLayout.WEST); - add(settings,BorderLayout.EAST); - - } - - private JPanel getMoveControl() - { - return this; - } - - private JPanel getResizeControl() - { - return resizecontrol; - } - - private JPanel getGenerationControl() - { - return settings; - } - - public MazeSettings getSettings() - { - return settings; - } - - } - - 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); - // attach keys to maze - maze.getInputMap().put(keystroke, actionName); - maze.getActionMap().put(actionName, goAction); - return button; - } - - private static class MazeCellParameters - { - double width = 10; // width of one cell - double height = 10; // height of one cell - int offsetX = 5; // x offset of upper corner left - int offsetY = 5; // y offset of upper corner left - - int mapWidth = 0; - int mapHeight = 0; - - public MazeCellParameters(int mapw, int maph, int W, int H, int x, int y) - { - double w = ( W - x )/ mapw; - double h = ( H - y )/ maph; - mapWidth = mapw; - mapHeight = maph; - if (w < 5) - w = 5; - if (h < 5) - h = 5; - setCellSize(w,h); - offsetX = x; - offsetY = y; - } - - public void resetMazeWidthHeight(int mapw, int maph) - { - mapWidth = mapw; - mapHeight = maph; - } - - public void adaptTo(double W, double H) - { - double w = ( W - offsetX ) / mapWidth; - double h = ( H - offsetY ) / mapHeight; - mapWidth = mapWidth; - mapHeight = mapHeight; - if (w < 5) - w = 5; - if (h < 5) - h = 5; - setCellSize(w,h); - } - - public void setCellSize(double w, double h) - { - width = w; - height = h; - } - - public double getWidth() - { - return width; - } - - public double getHeight() - { - return height; - } - - public int getOffsetX() - { - return offsetX; - } - - public int getOffsetY() - { - return offsetY; - } - - public Dimension getDimension() - { - return new Dimension(offsetX + (int) (mapWidth * width), - offsetY + (int) (mapHeight * height)); - } - - public void drawWalls(Graphics g, int pX, int pY, short walls) - { - int x = offsetX + (int) (pX * width); - int y = offsetY + (int) (pY * height); - if ( (pY == 0) && ((walls & Brick.UP) == Brick.UP)) - g.drawLine(x, y, x + (int) width, y); - if ((walls & Brick.DOWN) == Brick.DOWN) - g.drawLine(x, y + (int) height, x + (int) width, y + (int) height); - if ((walls & Brick.RIGHT) == Brick.RIGHT) - g.drawLine(x + (int) width, y, x + (int) width, y + (int) height); - if ( (pX == 0 ) && ((walls & Brick.LEFT) == Brick.LEFT)) - g.drawLine(x, y, x, y + (int) height); - } - - public void drawPath(Graphics g, DirectionPosition dp, int pX, int pY, int mX, int mY) - { - if ( dp != null ) - { - Position dot = dp.getPosition(); - if ((dot.getX() >= pX) && (dot.getY() >= pY) && (dot.getX() < mX) && (dot.getY() < mY)) - { - int x = offsetX + (int) (dot.getX() * width); - int y = offsetY + (int) (dot.getY() * height); - short path = dp.getDirection(); - int xm = x+ (int) (width / 2); - int ym = y+ (int) (height / 2); - if ( (path & LabyModel.HORIZONTAL) == LabyModel.HORIZONTAL) - { - if ( (path & LabyModel.POSITIVE) == LabyModel.POSITIVE ) - { - g.drawLine(xm, ym, x + (int) width, ym); - g.drawLine(xm, ym + (int) (height / 4), x + (int) width, ym); - } - // LEFT /_ - if ( (path & LabyModel.NEGATIVE) == LabyModel.NEGATIVE ) - { - g.drawLine(x, ym, xm, ym); - g.drawLine(x, ym, xm, ym - (int) (height / 4)); - } - } - if ((path & LabyModel.VERTICAL) == LabyModel.VERTICAL) - { - if ( (path & LabyModel.POSITIVE) == LabyModel.POSITIVE ) - { - g.drawLine(xm, ym, xm , y + (int) height); - g.drawLine(xm - (int) (width / 4), ym , xm, y + (int) height); - } - // UP |\ - if ( (path & LabyModel.NEGATIVE) == LabyModel.NEGATIVE ) - { - g.drawLine(xm, ym, xm , y); - g.drawLine(xm + (int) (width /4), ym , xm , y); - } - } - } - } - } - - public void drawDot(Graphics g, Position dot, int pX, int pY, int mX, int mY) - { - double radius = (height > width) ? width : height; - int a = (int) ( radius / 4 ); - if ((dot.getX() >= pX) && (dot.getY() >= pY) && (dot.getX() < mX) && (dot.getY() < mY)) - { - int x = offsetX + (int) (dot.getX() * width); - int y = offsetY + (int) (dot.getY() * height); - int r2 = (int) ( (radius * 3)/4 ); - g.drawOval(x + 1, y + 1, r2, r2); - // 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 + (int) (pX * width); - int y = offsetY + (int) (pY * height); - g.drawLine(x + 1, y + 1, x + (int) width - 1, y + (int) height - 1); - } - } - - } - - private static class MazeComponent - extends JComponent - implements MazeCreationListener, - MazeResolutionListener, - MouseMotionListener - { - private static final long serialVersionUID = 3163272907991176390L; - - // WallsProvider map; - LabyModel map; - final MazeCellParameters cp; - Position current = null; - LinkedList solvedPath = null; - LinkedList drawingPath = null; - final Object lockChange = new Object(); - // current postion of human resolving - int sX = 0; - int sY = 0; - // goal exit - int gX = -1; - int gY = -1; - - // not set by default for debug, will show any path - boolean showAll = false; - - // for a given (x,y) pixel return cell position. - Position getPosition(int x, int y) - { - int pX = (int) ((double) (x - cp.getOffsetX()) / cp.getWidth()); - int pY = (int) ((double) (y - cp.getOffsetY()) / cp.getHeight()); - return new Position(pX,pY); - } - - @Override - public void mouseDragged(MouseEvent e) { - // should find the cell ... - DirectionPosition last = null; - short path = 0; - Position newPosition = getPosition(e.getX(),e.getY()); - if (drawingPath == null ) - { - drawingPath = new LinkedList<>(); - last = new DirectionPosition((short) 0,newPosition); - System.out.println("Mouse dragged Cell " + newPosition + " Button " + e.getModifiersEx() + " " + InputEvent.BUTTON1_DOWN_MASK); - drawingPath.addLast(last); - } - else - { - // setShowAll(true); - DirectionPosition first = drawingPath.getLast(); - last = first; - // construct as many move form last to new position as needed. - while ( ! last.getPosition().equals(newPosition)) - { - path=LabyModel.getDirection(last.getPosition(),newPosition); - last.setDirection(path); - // button 1 : add direction; button 2 : replace with direction. - if ( ( e.getModifiersEx() & InputEvent.BUTTON1_DOWN_MASK ) != 0 ) - { - map.addDirection(last.getPosition().getX(),last.getPosition().getY(),path); - } - else - { - map.setDirection(last.getPosition().getX(),last.getPosition().getY(),path); - } - last = last.moveToAdjacentDirection(); - drawingPath.addLast(last); - } - System.out.println("Mouse dragged from Cell " + first.getPosition() + "To" + newPosition + " Button " + e.getModifiersEx() + " " + InputEvent.BUTTON1_DOWN_MASK); - } - changed(null,null,map); - } - - @Override - public void mouseMoved(MouseEvent e) { - // System.out.println("Mouse moved (" + e.getX() + ',' + e.getY() + ')'); - Position newPosition = getPosition(e.getX(),e.getY()); - if ( ( newPosition.getX() >= 0 ) && ( newPosition.getY() >= 0 ) ) - { - requestFocus(); - } - } - - public void setShowAll(boolean showall ) - { - this.showAll = showall; - System.out.println(showAll ? "show all" : "X"); - } - - 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(); - } - - } - - /** - width, height of one cell, - offsetX, offsetY of upper left corner - **/ - // public MazeComponent(WallsProvider map, MazeCellParameters cp) - public MazeComponent(LabyModel map, MazeCellParameters cp) - { - super(); - this.cp = cp; - this.map = map; - setPreferredSize(cp.getDimension()); - gX = map.getWidth() - 1; - gY = map.getHeight() - 1; - } - - // public void resetWallsProvider(WallsProvider map) - public void resetWallsProvider(LabyModel map) - { - this.map = map; - solvedPath=null; - // could be kept - drawingPath=null; - sX=0; - sY=0; - cp.resetMazeWidthHeight(map.getWidth(),map.getHeight()); - setPreferredSize(cp.getDimension()); - } - - public void setWallSize(int size) - { - cp.setCellSize((double) size,(double) size); - // should redraw ... - invalidate(); - repaint(); - } - - public int getAutoSize() - { - Rectangle r = getBounds(); - cp.adaptTo((int) r.getWidth(),(int) r.getHeight()); - // should redraw ... - invalidate(); - repaint(); - return 50; - } - - 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 !"); - } - } - } - - @Override - protected void paintComponent(Graphics g) - { - super.paintComponent(g); - int x = 0; - int y = 0; - short walls = 0; - short path = 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() / cp.getWidth()); - int mY = (int) ((double) r.getHeight() / cp.getHeight()); - int pX = (int) ((double) (r.getX() - cp.getOffsetX()) / cp.getWidth()); - int pY = (int) ((double) (r.getY() - cp.getOffsetY()) / cp.getHeight()); - - 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.green); - cp.drawDot(g, new Position(gX, gY), pX, pY, mX, mY); - g.setColor(Color.blue); - } - cp.drawDot(g, new Position(sX, sY), pX, pY, mX, mY); - - synchronized (lockChange) - { - g.setColor(Color.red); - if (current != null) - { - cp.drawDot(g, current, pX, pY, mX, mY); - } - if (solvedPath != null) - { - for (DirectionPosition resolved : solvedPath) - { - // cp.drawDot(g, resolved.getPosition(), pX, pY, mX, mY); - cp.drawPath(g,resolved, pX, pY, mX, mY); - } - } - } - - int aX = pX; - int aY = pY; - - g.setColor(Color.black); - - // draw all walls within clip bounds horiz first then lines - for (; pY < mY; pY++) - { - for (pX = 0; pX < mX; pX++) - { - walls = map.getWalls(pX, pY); - cp.drawWalls(g, pX, pY, walls); - } - } - - if (this.showAll) - { - System.out.println("*"); - pX = aX; - pY = aY; - - // draw all path within clip bounds horiz first then lines - for (; pY < mY; pY++) - { - for (pX = 0; pX < mX; pX++) - { - path = map.getPath(pX,pY); - if ( ( path & LabyModel.SOLVED ) == LabyModel.SOLVED ) - { - g.setColor(Color.green); - } - else - { - g.setColor(Color.blue); - } - if ( path != 0 ) - { - DirectionPosition dp = new DirectionPosition(path,new Position(pX,pY)); - cp.drawPath(g,dp,pX,pY,pX+1,pY+1); - } - } - } - } - } - - - public boolean notifySearch(DirectionPosition pPosition) - { - synchronized (lockChange) - { - current = pPosition.getPosition(); - } - // 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; - } - } - - private static void setupDisplay(LabyModel model, int W, int H, MazeParams params) - { - Display display = new Display(model, W,H,params); - } - - public static void save(MazeParamsFixed params,LabyModel model) - { - 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(), model, out); - out.flush(); - out.close(); - System.out.println("... Done."); - } - catch (IOException io) - { - io.printStackTrace(System.err); - } - } - else - { - System.out.println("" + outfile + " already exists"); - } - - } - - public static void main(String pArgs[]) - { - LabyModel model = null; - int W = 600; - int H = 400; - - System.out.println("Default Locale " + Locale.getDefault()); - - if ( (pArgs.length > 0) && (pArgs[0].length() > 0)) - { - try - { - model = new LabyModel("raw", new FileInputStream(pArgs[0])); - } - catch (IOException io) - { - io.printStackTrace(System.err); - System.exit(1); - } - - setupDisplay(model, W,H,null); - } - else - { - MazeParamsFixed params = new MazeParamsFixed(new File("lab"),20,20,12); - model = new LabyModel(params, new java.util.Random()); - - setupDisplay(model,W,H,params); - - /* - model.generateWithEntry(0, 0); - - model.addEntryOrExit(-1, 0); - model.addEntryOrExit(params.getWidth(), params.getHeight() - 1); - - System.out.println("Generation completed"); - */ - /* - */ - } - - } -} diff --git a/java/org/artisanlogiciel/games/LabyModel.java b/java/org/artisanlogiciel/games/LabyModel.java index 9e2da37..56e22b9 100644 --- a/java/org/artisanlogiciel/games/LabyModel.java +++ b/java/org/artisanlogiciel/games/LabyModel.java @@ -1,16 +1,10 @@ package org.artisanlogiciel.games; +import java.io.*; import java.util.ArrayList; import java.util.LinkedList; -import java.util.Collections; import java.util.Random; -import java.io.OutputStream; -import java.io.InputStream; -import java.io.IOException; -import java.io.DataOutputStream; -import java.io.DataInputStream; - /** * Model of labyrinth storing only paths not walls. wall are regenerated later * on based on adjacent paths. each position (x,y) stores what move can be done @@ -18,8 +12,7 @@ import java.io.DataInputStream; * openList if all moves from its position have not been resolved a node is * tagged CLOSED when fully processed **/ -public class LabyModel implements WallsProvider -{ +public class LabyModel implements WallsProvider { /** * WARNING don't change those values, they are used as it is for @@ -27,42 +20,42 @@ public class LabyModel implements WallsProvider */ private final static short FLAGLENGTH = 7; private final static short CLEAR = 0; // mandatory 0 since array creation is - // initialized with 0. + // initialized with 0. public final static short HORIZONTAL = 1; public final static short VERTICAL = 2; public final static short DIRECTION = 4; // could we get rid of that to - // free one bit for other purpose - // ? + // free one bit for other purpose + // ? + // can be both POSITIVE and NEGATIVE, it means that you can move in positive direction and in negative direction. public final static short POSITIVE = 8; public final static short NEGATIVE = 16; - + + // can it be both OPEN and CLOSED ? private final static short OPEN = 32; // can be reused once generation is - // completed + // completed private final static short CLOSED = 64; // can be reused once generation is - // completed + // completed public final static short LEFT = Brick.LEFT << FLAGLENGTH | DIRECTION | HORIZONTAL | NEGATIVE; public final static short DOWN = Brick.DOWN << FLAGLENGTH | DIRECTION | VERTICAL | POSITIVE; public final static short RIGHT = Brick.RIGHT << FLAGLENGTH | DIRECTION | HORIZONTAL | POSITIVE; public final static short UP = Brick.UP << FLAGLENGTH | DIRECTION | VERTICAL | NEGATIVE; - public final static short ENTRY = Brick.ENTRY << FLAGLENGTH; // flag when a - // wall should - // be open to - // access - // this. - private final static short GOAL = Brick.GOAL << FLAGLENGTH; // flag when a - // wall should - // be open to - // access this. - public final static short SOLVED = 64 << FLAGLENGTH; // flag when solution - // is on this path. - private final static short FREE = 128 << FLAGLENGTH; // free flag + + // flag when a wall should be open to access this for entry + public final static short ENTRY = Brick.ENTRY << FLAGLENGTH; + // flag when a wall should be open to access this for exit + private final static short GOAL = Brick.GOAL << FLAGLENGTH; + // flag when solution is on this path. + public final static short SOLVED = 64 << FLAGLENGTH; + // free flag + private final static short FREE = 128 << FLAGLENGTH; // remains 2 free bits ( keep one out for sign ) - private static final short[] AllDirections = { LEFT, DOWN, RIGHT, UP }; + // orders matters see getReverseDirection + private static final short[] AllDirections = {LEFT, DOWN, RIGHT, UP}; private int width; private int height; - // wall flags + status flags for each position (x,y) + // wall flags Brick.(LEFT,DOWN,RIGHT,UP,ENTRY,GOAL) + status flags for each position (x,y) private short[][] t; private int depth = 0; /** @@ -71,45 +64,27 @@ public class LabyModel implements WallsProvider * that to shorter maxdepth is the harder the labyrinth is ... **/ private int maxdepth = 0; - private int deepest = 0; // longest path found + // longest path found + private int deepest = 0; + // each move is a linearwork step private int linearwork = 0; + private Position deepestEnd = null; boolean maxreached = false; java.util.Random random; + // list of positions not fully walked private final LinkedList openList = new LinkedList(); // list of entries and exits. private final LinkedList entryExits = new LinkedList(); private final Object coherentLock = new Object(); // before getting the lock - // and after lock release - // all is coherent ( ie - // check() is ok ), + // and after lock release + // all is coherent ( ie + // check() is ok ), MazeCreationListener listener = null; - /** -TODO - private static MyLinkedList

- { - MyLinkedList

next; - P content - - MyLinkedList

( P object) - { - content=object; - next=null; - } - - MyLinkedList

addLast(P object) - { - next = new MyLinkedList(object); - return next; - } - } - */ - - public LabyModel(int width, int height, int maxdepth, Random random) - { + private LabyModel(int width, int height, int maxdepth, Random random) { this.width = width; this.height = height; this.maxdepth = maxdepth; @@ -118,218 +93,183 @@ TODO t = new short[width][height]; } - public LabyModel(MazeParams params, Random random) - { - this.width = params.getWidth(); - this.height = params.getHeight(); - this.maxdepth = params.getMaxDepth(); - this.random = random; - // CLEAR == 0 and array is initialized with 0s - t = new short[width][height]; + public LabyModel(MazeParams params) { + this(params.getWidth(), params.getHeight(), params.getMaxDepth(), new java.util.Random(params.getSeed())); } - public LabyModel(String pFormat, InputStream pIn) throws IOException - { + /** + * construct LabyModel from an InputStream, yet only "raw" is supported + **/ + public LabyModel(String pFormat, InputStream pIn) throws IOException { parseInputStream(pFormat, pIn); } - public void setMazeListener(MazeCreationListener listener) - { + public void setMazeListener(MazeCreationListener listener) { this.listener = listener; } // FIXME FULLY BREAK RESOLVING... - public void noWalls(int x, int y) - { - if ((x >= 0) && (x < width) && (y >= 0) && (y < height)) - { - // t[x][y] |= DIRECTION | VERTICAL | POSITIVE | HORIZONTAL | NEGATIVE | LEFT | RIGHT | UP | DOWN; - t[x][y] |= DIRECTION | VERTICAL | POSITIVE | HORIZONTAL | NEGATIVE; - t[x][y] |= LEFT | RIGHT | UP | DOWN; - } + public void noWalls(int x, int y) { + if ((x >= 0) && (x < width) && (y >= 0) && (y < height)) { + // t[x][y] |= DIRECTION | VERTICAL | POSITIVE | HORIZONTAL | NEGATIVE | LEFT | RIGHT | UP | DOWN; + t[x][y] |= LEFT | RIGHT | UP | DOWN; + } } /* set direction existing onee are lost */ // FIXME FULLY BREAK RESOLVING... - public void setDirection(int x, int y, short path) - { - if ((x >= 0) && (x < width) && (y >= 0) && (y < height)) - { - t[x][y]= (short) (path | OPEN); - } + public void setDirection(int x, int y, short path) { + if ((x >= 0) && (x < width) && (y >= 0) && (y < height)) { + t[x][y] = (short) (path | OPEN); + } } - /** add a new direction, exiting ones are kept */ + /** + * add a new direction, exiting ones are kept + */ // FIXME FULLY BREAK RESOLVING... - public void addDirection(int x, int y, short path) - { - if ((x >= 0) && (x < width) && (y >= 0) && (y < height)) - { - t[x][y]|= (short) (path | OPEN); - } + public void addDirection(int x, int y, short path) { + if ((x >= 0) && (x < width) && (y >= 0) && (y < height)) { + t[x][y] |= (short) (path | OPEN); + } } // entry and exit can be outside the model boundaries a one x or one y out. - public boolean addEntryOrExit(int x, int y) - { + public boolean addEntryOrExit(int x, int y) { entryExits.add(new Position(x, y)); - if ((x > 0) && (x < width) && (y > 0) && (y < height)) - { + if ((x > 0) && (x < width) && (y > 0) && (y < height)) { t[x][y] |= ENTRY; } return true; } - public void debugOut() - { - for (int y = 0; y < height; y++) - { - for (int x = 0; x < width; x++) - { + public void debugOut() { + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { out4XY(x, y); } System.out.print('\n'); } } - public void out4XY(int x, int y) - { + public void out4XY(int x, int y) { int direction = t[x][y]; - if ((direction & OPEN) == OPEN) - { + if ((direction & OPEN) == OPEN) { System.out.print("?"); - } - else if ((direction & CLOSED) == CLOSED) - { + } else if ((direction & CLOSED) == CLOSED) { System.out.print("."); - } - else - { + } else { System.out.print(" "); } // don't display information about short. direction &= ~OPEN; direction &= ~GOAL; direction &= ~CLOSED; - switch (direction) - { - case LEFT: // left - System.out.print("<-"); - break; - case DOWN: // down - System.out.print("vv"); - break; - case RIGHT: // right - System.out.print("->"); - break; - case UP: // up - System.out.print("^^"); - break; - case HORIZONTAL: // - - System.out.print("--"); - break; - case VERTICAL: // | - System.out.print("||"); - break; - case CLEAR: - System.out.print(" "); - break; - case CLOSED: - System.out.print("00"); - break; - case RIGHT | LEFT: - System.out.print("--"); - break; - case UP | DOWN: - System.out.print("||"); - break; - case RIGHT | UP: - System.out.print("^>"); - break; - case RIGHT | DOWN: - System.out.print("v>"); - break; - case LEFT | DOWN: - System.out.print(""); - break; - case LEFT | RIGHT | UP | DOWN: - System.out.print("++"); - break; - case OPEN: - System.out.print("??"); - break; - case GOAL: - System.out.print("**"); - break; - default: - System.out.print(".."); + switch (direction) { + case LEFT: // left + System.out.print("<-"); + break; + case DOWN: // down + System.out.print("vv"); + break; + case RIGHT: // right + System.out.print("->"); + break; + case UP: // up + System.out.print("^^"); + break; + case HORIZONTAL: // - + System.out.print("--"); + break; + case VERTICAL: // | + System.out.print("||"); + break; + case CLEAR: + System.out.print(" "); + break; + case CLOSED: + System.out.print("00"); + break; + case RIGHT | LEFT: + System.out.print("--"); + break; + case UP | DOWN: + System.out.print("||"); + break; + case RIGHT | UP: + System.out.print("^>"); + break; + case RIGHT | DOWN: + System.out.print("v>"); + break; + case LEFT | DOWN: + System.out.print(""); + break; + case LEFT | RIGHT | UP | DOWN: + System.out.print("++"); + break; + case OPEN: + System.out.print("??"); + break; + case GOAL: + System.out.print("**"); + break; + default: + System.out.print(".."); } - if (((t[x][y] & RIGHT) == RIGHT) || ((x + 1 < width) && ((t[x + 1][y] & LEFT) == LEFT))) - { + if (((t[x][y] & RIGHT) == RIGHT) || ((x + 1 < width) && ((t[x + 1][y] & LEFT) == LEFT))) { System.out.print("-"); - } - else - { + } else { System.out.print("H"); } } - public String outHorizWall2XY(int x, int y) - { + public String outHorizWall2XY(int x, int y) { String freeway = " "; - if ((t[x][y] & SOLVED) == SOLVED) - { + if ((t[x][y] & SOLVED) == SOLVED) { freeway = "*"; } - if ((x < 0) || (x > width)) - { + if ((x < 0) || (x > width)) { return " H"; } - if ((y < 0) || (y >= height)) - { + if ((y < 0) || (y >= height)) { return "HH"; } - if (((t[x][y] & DOWN) == DOWN) || ((y + 1 < height) && ((t[x][y + 1] & UP) == UP))) - { + if (((t[x][y] & DOWN) == DOWN) || ((y + 1 < height) && ((t[x][y + 1] & UP) == UP))) { return freeway + "H"; - } - else - { + } else { return "HH"; } } - public String out2XY(int x, int y) - { + public String out2XY(int x, int y) { // can check for entry exits. - if ((y < 0) || (y >= height) || (x < 0) || (x > width)) - { + if ((y < 0) || (y >= height) || (x < 0) || (x > width)) { return " H"; } String low = ""; int direction = t[x][y]; String freeway = " "; - if ((t[x][y] & SOLVED) == SOLVED) - { + if ((t[x][y] & SOLVED) == SOLVED) { freeway = "*"; } // don't display information about short. @@ -337,136 +277,115 @@ TODO direction &= ~GOAL; direction &= ~CLOSED; direction &= ~SOLVED; - switch (direction) - { - case LEFT: - case DOWN: - case RIGHT: - case UP: - low = freeway; - break; - case HORIZONTAL: // - - low = " "; - break; - case VERTICAL: // | - low = " "; - break; - case CLEAR: - low = " "; - break; - case CLOSED: - low = "0"; - break; - case RIGHT | LEFT: - case UP | DOWN: - case RIGHT | UP: - case RIGHT | DOWN: - case LEFT | DOWN: - case LEFT | UP: - case LEFT | RIGHT | UP: - case LEFT | RIGHT | DOWN: - case LEFT | DOWN | UP: - case RIGHT | DOWN | UP: - case LEFT | RIGHT | UP | DOWN: - low = "."; - break; - case OPEN: - low = "?"; - break; - case GOAL: - low = "*"; - break; - default: - low = "."; + switch (direction) { + case LEFT: + case DOWN: + case RIGHT: + case UP: + low = freeway; + break; + case HORIZONTAL: // - + low = " "; + break; + case VERTICAL: // | + low = " "; + break; + case CLEAR: + low = " "; + break; + case CLOSED: + low = "0"; + break; + case RIGHT | LEFT: + case UP | DOWN: + case RIGHT | UP: + case RIGHT | DOWN: + case LEFT | DOWN: + case LEFT | UP: + case LEFT | RIGHT | UP: + case LEFT | RIGHT | DOWN: + case LEFT | DOWN | UP: + case RIGHT | DOWN | UP: + case LEFT | RIGHT | UP | DOWN: + low = "."; + break; + case OPEN: + low = "?"; + break; + case GOAL: + low = "*"; + break; + default: + low = "."; } - for (Position exit : entryExits) - { - if ((exit.getX() == x + 1) && (exit.getY() == y)) - { + for (Position exit : entryExits) { + if ((exit.getX() == x + 1) && (exit.getY() == y)) { low = low + ">"; return low; } } - if (((t[x][y] & RIGHT) == RIGHT) || ((x + 1 < width) && ((t[x + 1][y] & LEFT) == LEFT))) - { + if (((t[x][y] & RIGHT) == RIGHT) || ((x + 1 < width) && ((t[x + 1][y] & LEFT) == LEFT))) { low = low + freeway; - } - else - { + } else { low = low + "H"; } return low; } - public LabyMap toLabyMap() - { - synchronized (coherentLock) - { + public LabyMap toLabyMap() { + synchronized (coherentLock) { Brick[][] brickMap = new Brick[width][height]; - for (int y = 0; y < height; y++) - { - for (int x = 0; x < width; x++) - { + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { brickMap[x][y] = new Brick(out2XY(x, y), outHorizWall2XY(x, y), getWalls(x, y)); } } - return new LabyMap(brickMap, (LinkedList) entryExits.clone()); + return new LabyMap(brickMap, new LinkedList(entryExits)); } } - public int getWidth() - { + public int getWidth() { return this.width; } - public int getHeight() - { + public int getHeight() { return this.height; } - public LinkedList getOpenList() - { + public LinkedList getOpenList() { return openList; } - public void reset() - { + public void reset() { depth = 0; computeOpenList(); - resetResolving(); + resetResolving(); } - public void resetResolving() - { - // don't keep solved path - for (int y = 0; y < height; y++) - { - for (int x = 0; x < width; x++) - { - t[x][y] &= ~(SOLVED); + public void resetResolving() { + // don't keep solved path + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + t[x][y] &= ~(SOLVED); } } } - public void fullReset() - { + public void fullReset() { depth = 0; openList.clear(); maxreached = false; deepest = 0; deepestEnd = null; // clear open - for (int y = 0; y < height; y++) - { - for (int x = 0; x < width; x++) - { - // resetCell(x,y); - t[x][y] &= ~( OPEN | SOLVED); - + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + // resetCell(x,y); + t[x][y] &= ~(OPEN | SOLVED); } } @@ -475,26 +394,21 @@ TODO /** * check model coherency and fix if model needed a fix it means that * algorithm has a problem - * + * * @return true if model is ok and no fix was applied. **/ - public boolean check() - { + public boolean check() { boolean check = true; - synchronized (coherentLock) - { + synchronized (coherentLock) { // node in OPEN should be tagged OPEN and not CLOSED. - for (Position p : openList) - { + for (Position p : openList) { int x = p.getX(); int y = p.getY(); - if (isFlagSet(t[x][y],OPEN)) - { + if (isFlagSet(t[x][y], OPEN)) { check = false; t[x][y] |= OPEN; } - if (isFlagSet(t[x][y],CLOSED)) - { + if (isFlagSet(t[x][y], CLOSED)) { check = false; t[x][y] &= ~CLOSED; } @@ -506,47 +420,43 @@ TODO return check; } - public void computeOpenList() - { + // coherency : if CLOSED then not OPEN + public void computeOpenList() { openList.clear(); - for (int y = 0; y < height; y++) - { - for (int x = 0; x < width; x++) - { - if (isFlagSet(t[x][y],CLOSED)) - { + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + if (isFlagSet(t[x][y], CLOSED)) { t[x][y] &= ~OPEN; } - if (isFlagSet(t[x][y],OPEN) || (t[x][y] == CLEAR)) - { + if (isFlagSet(t[x][y], OPEN) || (t[x][y] == CLEAR)) { openList.add(new Position(x, y)); } } } } - public void generateWithEntry(int x, int y) - { + /** + * This is core of generator. + *

+ * generate a maze with an entry set at (x,y) with a maxdepth + **/ + public void generateWithEntry(int x, int y) { openList.add(new Position(x, y)); - while (!openList.isEmpty()) - { - step(0); // fixme depth not set + while (!openList.isEmpty()) { + // this is where magic happens + step(); - synchronized (coherentLock) - { + synchronized (coherentLock) { linearwork++; - if (linearwork % maxdepth == 0) - { + if (linearwork % maxdepth == 0) { coherentLock.notifyAll(); - if (listener != null) - { + if (listener != null) { listener.changed(null, null, this); } // should not continue in depth first... - if (!openList.isEmpty()) - { + if (!openList.isEmpty()) { // insert head as next position to pick up. Position current = openList.removeFirst(); openList.add(current); @@ -556,43 +466,35 @@ TODO } - if (deepestEnd != null) - { + if (deepestEnd != null) { t[deepestEnd.getX()][deepestEnd.getY()] |= GOAL; } } - public int getDepth() - { + public int getDepth() { return depth; } - public Position getDeepestEnd() - { + public Position getDeepestEnd() { return deepestEnd; } - public int getDeepestPath() - { + public int getDeepestPath() { return deepest; } - public boolean maxReached() - { + public boolean maxReached() { return maxreached; } /** - * @param p - * Position + * @param p Position * @return true if newly added , false if already open. **/ - private boolean open(boolean first, Position p) - { + private boolean open(boolean first, Position p) { int x = p.getX(); int y = p.getY(); - if ((t[x][y] & OPEN) != OPEN) - { + if ((t[x][y] & OPEN) != OPEN) { t[x][y] |= OPEN; openList.addLast(p); return true; @@ -600,318 +502,257 @@ TODO return false; } - public final static boolean isFlagSet(short check, short flag) - { - return ((check & flag) == flag); + public final static boolean isFlagSet(short check, short flag) { + return ((check & flag) == flag); } - public final short getCell(Position p) - { - return t[p.getX()][p.getY()]; + public final short getCell(Position p) { + return t[p.getX()][p.getY()]; } - public final void updateCell(Position p, short flags) - { - t[p.getX()][p.getY()] |= flags; + public final void updateCell(Position p, short flags) { + t[p.getX()][p.getY()] |= flags; } - public final static short getReverseDirection(int index) - { - return AllDirections[(index + 2) % 4]; + public final static short getReverseDirection(int index) { + return AllDirections[(index + 2) % 4]; } - /** return direction to use to be closer to 'to' from 'from' */ - public final static short getDirection(Position from, Position to) - { - short pointingdirection = DIRECTION; - if ( from.equals(to) ) - { - return pointingdirection; - } - if ( from.getX() < to.getX() ) - { - pointingdirection |= RIGHT; - } - else - if ( from.getX() > to.getX() ) - { - pointingdirection |= LEFT; - } - if ( from.getY() < to.getY() ) - { - pointingdirection |= DOWN; - } - else - if ( from.getY() > to.getY() ) - { - pointingdirection |= UP; - } - return pointingdirection; + /** + * return direction to use to be closer to 'to' from 'from' + */ + public final static short getDirection(Position from, Position to) { + short pointingdirection = DIRECTION; + if (from.equals(to)) { + return pointingdirection; + } + if (from.getX() < to.getX()) { + pointingdirection |= RIGHT; + } else if (from.getX() > to.getX()) { + pointingdirection |= LEFT; + } + if (from.getY() < to.getY()) { + pointingdirection |= DOWN; + } else if (from.getY() > to.getY()) { + pointingdirection |= UP; + } + return pointingdirection; } - + /** * resolve this labrynth using internal representation * initial (x,y) is exit, will return list of positions from start (0,0) to end (x,y) **/ - public LinkedList resolve(int x, int y, MazeResolutionListener rlistener) - { - long safeguard = width * height; + public LinkedList resolve(int x, int y, MazeResolutionListener rlistener) { + long safeguard = width * height; int newx = 0; int newy = 0; - resetResolving(); + resetResolving(); - // list of alternate paths + // list of alternate paths LinkedList> altpath = new LinkedList<>(); - // list of positions from start to end + // list of positions from start to end LinkedList backpath = new LinkedList(); - // position that point to (x,y). - DirectionPosition found = new DirectionPosition((short) 0,new Position(x, y)); - // entry - Position entry = new Position(0, 0); + // position that point to (x,y). + DirectionPosition found = new DirectionPosition((short) 0, new Position(x, y)); + // entry + Position entry = new Position(0, 0); - while (! found.getPosition().equals(entry) ) - { - Position last = found.getPosition(); + while (!found.getPosition().equals(entry)) { + Position last = found.getPosition(); backpath.addFirst(found); - found = null; + found = null; // should find from all adjacent cells (directions) one that point to this. - { - // didx is index of four cell adjacent to this (x,y) - for (int didx = 0; didx < 4; didx++) - { - int delta = 0; - short direction = AllDirections[didx]; - short reversedirection = getReverseDirection(didx); - short pointingdirection = DIRECTION; + { + // didx is index of four cell adjacent to this (x,y) + for (int didx = 0; didx < 4; didx++) { + int delta = 0; + short direction = AllDirections[didx]; + short reversedirection = getReverseDirection(didx); + short pointingdirection = DIRECTION; - if (isFlagSet(direction,POSITIVE)) - { - delta = 1; - pointingdirection |= NEGATIVE; - } - else - { - delta = -1; - pointingdirection |= POSITIVE; - } + if (isFlagSet(direction, POSITIVE)) { + delta = 1; + pointingdirection |= NEGATIVE; + } else { + delta = -1; + pointingdirection |= POSITIVE; + } - if (isFlagSet(direction,HORIZONTAL)) - { - newx = last.getX() + delta; - newy = last.getY(); - pointingdirection |= HORIZONTAL; - } - else - { - newy = last.getY() + delta; - newx = last.getX(); - pointingdirection |= VERTICAL; - } + if (isFlagSet(direction, HORIZONTAL)) { + newx = last.getX() + delta; + newy = last.getY(); + pointingdirection |= HORIZONTAL; + } else { + newy = last.getY() + delta; + newx = last.getX(); + pointingdirection |= VERTICAL; + } - // internal GUARD. - if (! isFlagSet(reversedirection,pointingdirection)) - { - System.out.println("Internal ERROR. Please check AllDirections order " - + (reversedirection & pointingdirection) + " " + pointingdirection); - return backpath; - } + // internal GUARD. + if (!isFlagSet(reversedirection, pointingdirection)) { + System.out.println("[FATAL] Internal ERROR. Please check AllDirections order " + + (reversedirection & pointingdirection) + " " + pointingdirection); + return backpath; + } - Position p = new Position(newx,newy); - if ((newx >= 0) && (newy >= 0) && (newx < width) && (newy < height)) - { - if (isFlagSet(getCell(p),reversedirection)) - { - if (found != null) - { - // there is already a potential solution in adjacent cell of last. - System.out.println("alternate " + p + " from " + last + "/" + safeguard); - // could be a unique parent of two paths... - // but search from other entry/exits than generated - // from - // ie entry(0,0) exit(width-1,height-1) not yet - // implemented. - { - if (! isFlagSet(getCell(p), SOLVED )) - { - LinkedList cp = (LinkedList) backpath.clone(); - DirectionPosition altfound = new DirectionPosition(reversedirection,p); - cp.addFirst(altfound); - altpath.addLast(cp); - rlistener.notifySearchError("record alternate path " + p.toString()); - } - else - { - // this was already solved, might be a loop. - if (rlistener != null) - { - rlistener.notifySearchError("Loop " + last.toString() + " has two parents " + found.toString() + " " - + p.toString()); - } - // continue; - } - } - } - else - { - if (! isFlagSet(getCell(p), SOLVED )) - { - // this is first potential solution in adjacent cell of last. - System.out.println("check " + p + " from " + last + "/" + safeguard); - - found = new DirectionPosition(reversedirection,p); - if (rlistener != null) - { - rlistener.notifySearch(found); - } - } - else - { - // was already solved. - System.out.println("already solved " + p + " from " + last + "/" + safeguard); - } - } - // support multiple pathes - } - else - { - System.out.println("not reachable " + p + " from " + last + "/" + safeguard); - } - } - else - { - System.out.println("p outofbounds " + p + "/" + safeguard); - } - } - if (found == null) - { - if ( ! altpath.isEmpty() ) - { - // new possible backpath - backpath = altpath.removeFirst(); - found = backpath.removeFirst(); - if (rlistener != null) - { - rlistener.notifySearchError("try alternate path " + found.toString()); - rlistener.notifySearch(found); - } - } - } - } - if (found == null) - { - if ( ! altpath.isEmpty() ) - { - rlistener.notifySearchError("No path found BUT ALTPATH !" ); - } - rlistener.notifySearchError("No path found !"); - break; - } + Position p = new Position(newx, newy); + if ((newx >= 0) && (newy >= 0) && (newx < width) && (newy < height)) { + if (isFlagSet(getCell(p), reversedirection)) { + if (found != null) { + // there is already a potential solution in adjacent cell of last. + System.out.println("alternate " + p + " from " + last + "/" + safeguard); + // could be a unique parent of two paths... + // but search from other entry/exits than generated + // from + // ie entry(0,0) exit(width-1,height-1) not yet + // implemented. + { + if (!isFlagSet(getCell(p), SOLVED)) { + LinkedList cp = new LinkedList(backpath); + DirectionPosition altfound = new DirectionPosition(reversedirection, p); + cp.addFirst(altfound); + altpath.addLast(cp); + rlistener.notifySearchError("record alternate path " + p.toString()); + } else { + // this was already solved, might be a loop. + if (rlistener != null) { + rlistener.notifySearchError("Loop " + last.toString() + " has two parents " + found.toString() + " " + + p.toString()); + } + // continue; + } + } + } else { + if (!isFlagSet(getCell(p), SOLVED)) { + // this is first potential solution in adjacent cell of last. + System.out.println("check " + p + " from " + last + "/" + safeguard); + + found = new DirectionPosition(reversedirection, p); + if (rlistener != null) { + rlistener.notifySearch(found); + } + } else { + // was already solved. + System.out.println("already solved " + p + " from " + last + "/" + safeguard); + } + } + // support multiple pathes + } else { + System.out.println("not reachable " + p + " from " + last + "/" + safeguard); + } + } else { + System.out.println("p outofbounds " + p + "/" + safeguard); + } + } + if (found == null) { + if (!altpath.isEmpty()) { + // new possible backpath + backpath = altpath.removeFirst(); + found = backpath.removeFirst(); + if (rlistener != null) { + rlistener.notifySearchError("try alternate path " + found.toString()); + rlistener.notifySearch(found); + } + } + } + } + if (found == null) { + if (!altpath.isEmpty()) { + rlistener.notifySearchError("No path found BUT ALTPATH !"); + } + rlistener.notifySearchError("No path found !"); + break; + } // System.out.println(found); - if (isFlagSet(getCell(found.getPosition()), SOLVED )) - { - System.out.println("[INFO] position already solved" + found.toString() + " *length:" + backpath.size()); - } - else - { - updateCell(found.getPosition(),SOLVED); - } + if (isFlagSet(getCell(found.getPosition()), SOLVED)) { + System.out.println("[INFO] position already solved" + found.toString() + " *length:" + backpath.size()); + } else { + updateCell(found.getPosition(), SOLVED); + } - - safeguard --; - if ( safeguard < 0 ) - { - rlistener.notifySearchError("Path too long ( or long overflow ) for width*height:" + (width*height) + " length:" + backpath.size()); - break; - } + + safeguard--; + if (safeguard < 0) { + rlistener.notifySearchError("Path too long ( or long overflow ) for width*height:" + (width * height) + " length:" + backpath.size()); + break; + } } - if (rlistener != null) - { + if (rlistener != null) { rlistener.notifyCompletion(backpath); } return backpath; } + private final void closePosition(int x, int y) { + t[x][y] &= ~OPEN; + t[x][y] |= CLOSED; + } + /** - * @returns wheter process closed current node. + * One step in Maze generation process + * get last element in open list and explore one random direction with it. + * + * @returns whether process closed current node. **/ - public boolean step(int depth) - { + public boolean step() { boolean complete = false; Position current = null; - synchronized (coherentLock) - { + synchronized (coherentLock) { - if (!openList.isEmpty()) - { + if (!openList.isEmpty()) { // last : in depth before. current = openList.getLast(); - } - else - { + } else { return true; } - if (current != null) - { + if (current != null) { int x = current.getX(); int y = current.getY(); // should find all free positions... ArrayList freeDirection = new ArrayList(); - for (short direction : AllDirections) - { + for (short direction : AllDirections) { int delta = 0; int newx = -1; int newy = -1; - if ((direction & POSITIVE) == POSITIVE) - { + if ((direction & POSITIVE) == POSITIVE) { delta = 1; - } - else - { + } else { delta = -1; } - if ((direction & HORIZONTAL) == HORIZONTAL) - { + if ((direction & HORIZONTAL) == HORIZONTAL) { newx = x + delta; newy = y; - } - else - { + } else { newy = y + delta; newx = x; } - if ((newx >= 0) && (newy >= 0) && (newx < width) && (newy < height)) - { - if ((t[newx][newy]) == CLEAR) - { - freeDirection.add(new Short(direction)); + if ((newx >= 0) && (newy >= 0) && (newx < width) && (newy < height)) { + if ((t[newx][newy]) == CLEAR) { + freeDirection.add(Short.valueOf(direction)); } } } - if (!freeDirection.isEmpty()) - { + if (!freeDirection.isEmpty()) { // control random using our own pseudorandom short direction = 0; - if (freeDirection.size() > 1) - { + if (freeDirection.size() > 1) { direction = freeDirection.get(random.nextInt(freeDirection.size())); - } - else - { + } else { direction = freeDirection.get(0); Position last = openList.removeLast(); - if (last != current) - { + if (last != current) { // GUARD, should not happen ( or multi-thread access // to model error ) System.err.println("INTERNAL ERROR 3"); @@ -923,38 +764,28 @@ TODO int newx = -1; int newy = -1; - if ((direction & POSITIVE) == POSITIVE) - { + if ((direction & POSITIVE) == POSITIVE) { delta = 1; - } - else - { + } else { delta = -1; } - if ((direction & HORIZONTAL) == HORIZONTAL) - { + if ((direction & HORIZONTAL) == HORIZONTAL) { newx = x + delta; newy = y; - } - else - { + } else { newy = y + delta; newx = x; } Position target = new Position(newx, newy, current.getDepth() + 1); open(false, target); - if ((t[x][y] & DIRECTION) == DIRECTION) - { + if ((t[x][y] & DIRECTION) == DIRECTION) { t[x][y] |= direction; - } - else - { + } else { t[x][y] = direction; } // not a 'direction' ... is it necessary to check ? - if (freeDirection.size() > 1) - { + if (freeDirection.size() > 1) { // keep it open at the very same place, previous open // did add another node to inspect. return false; @@ -964,24 +795,21 @@ TODO // last ) // can proceed to close - } - else - { + } else { // no free direction remaining => closing Position last = openList.removeLast(); - if (last != current) - { + if (last != current) { // GUARD, should not happen. System.err.println("INTERNAL ERROR 3"); return false; } } complete = true; - t[x][y] &= ~OPEN; - t[x][y] |= CLOSED; - if (current.getDepth() > deepest) - { - deepest = depth; + closePosition(x, y); + + final int currentdepth = current.getDepth(); + if (currentdepth > deepest) { + deepest = currentdepth; deepestEnd = current; } } // current != null; @@ -989,34 +817,28 @@ TODO return complete; } - public short getWalls(int x, int y) - { + public short getWalls(int x, int y) { short walls = 0; - for (short direction : AllDirections) - { - if (hasWallInDirection(x, y, direction)) - { + for (short direction : AllDirections) { + if (hasWallInDirection(x, y, direction)) { walls |= (direction >> FLAGLENGTH); } } return walls; } - public short getPath(int x, int y) - { + public short getPath(int x, int y) { short path = 0; - if (( x < width ) && ( y < height )) - { - path = t[x][y]; - } - return path; + if ((x < width) && (y < height)) { + path = t[x][y]; + } + return path; } /** * is there a wall in that direction ? **/ - public boolean hasWallInDirection(int x, int y, short direction) - { + public boolean hasWallInDirection(int x, int y, short direction) { int newx = 0; int newy = 0; int delta = 0; @@ -1024,106 +846,87 @@ TODO int reversedirection = 0; // is this direction on the path ? yes => no wall - if ((t[x][y] & direction) == direction) - { + if ((t[x][y] & direction) == direction) { return false; } // is adjacent tile in direction pointing in reverse direction ? yes => // no wall - if ((direction & POSITIVE) == POSITIVE) - { + if ((direction & POSITIVE) == POSITIVE) { delta = 1; - } - else - { + } else { delta = -1; } - if ((direction & HORIZONTAL) == HORIZONTAL) - { + if ((direction & HORIZONTAL) == HORIZONTAL) { newx = x + delta; newy = y; reversedirection = (direction == RIGHT) ? LEFT : RIGHT; - } - else - { + } else { newy = y + delta; newx = x; reversedirection = (direction == UP) ? DOWN : UP; } - if ((newx >= 0) && (newy >= 0) && (newx < width) && (newy < height)) - { - return !isFlagSet(t[newx][newy],(short)reversedirection); - } - else - { + if ((newx >= 0) && (newy >= 0) && (newx < width) && (newy < height)) { + return !isFlagSet(t[newx][newy], (short) reversedirection); + } else { // outside boundaries. // TODO CHECK exits. return true; } } - public void streamOut(String pFormat, OutputStream pOut) throws IOException - { - if ((pFormat == null) || (pFormat.equals("raw"))) - { + public void streamOut(String pFormat, OutputStream pOut) throws IOException { + if ((pFormat == null) || (pFormat.equals("raw"))) { // first raw format, not smart. DataOutputStream dataOut = new DataOutputStream(pOut); // should dump this to stream. - dataOut.write(new byte[] { (byte) 'L', (byte) 'A', (byte) 'B', (byte) '0' }); + dataOut.write(new byte[]{(byte) 'L', (byte) 'A', (byte) 'B', (byte) '0'}); dataOut.writeInt(getWidth()); dataOut.writeInt(getHeight()); dataOut.flush(); - for (int y = 0; y < getHeight(); y++) - { - for (int x = 0; x < getWidth(); x++) - { + for (int y = 0; y < getHeight(); y++) { + for (int x = 0; x < getWidth(); x++) { dataOut.writeShort(t[x][y]); } } dataOut.flush(); - } - else - { + } else { throw new IOException("Format " + pFormat + " Not yet implemented."); } } - private void streamIn(String pFormat, InputStream pIn) throws IOException - { + private void streamIn(String pFormat, InputStream pIn) throws IOException { throw new IOException("Use correct constructor."); } - private void parseInputStream(String pFormat, InputStream pIn) throws IOException - { - if ((pFormat == null) || (pFormat.equals("raw"))) - { + private void parseInputStream(String pFormat, InputStream pIn) throws IOException { + if ((pFormat == null) || (pFormat.equals("raw"))) { + // maxdepth is unset then unmodified byte[] header = new byte[4]; DataInputStream in = new DataInputStream(pIn); in.read(header); int rwidth = in.readInt(); int rheight = in.readInt(); - width = rwidth; - height = rheight; - maxdepth = maxdepth; - random = null; - // SHOULD CHECK max width and max height... - // CLEAR == 0 and array is initialized with 0s - t = new short[width][height]; - for (int y = 0; y < height; y++) - { - for (int x = 0; x < width; x++) - { - t[x][y] = in.readShort(); + if ((rwidth > 0) && (rheight > 0)) { + width = rwidth; + height = rheight; + random = null; + // SHOULD CHECK max width and max height... + // CLEAR == 0 and array is initialized with 0s + t = new short[width][height]; + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + t[x][y] = in.readShort(); + } } + } else { + throw new IOException("Invalid header for width and height"); } // should be at end of stream ? Not necessary can stream multiple // labs ( or tiling ). - } - else - { + } else { throw new IOException("Format " + pFormat + " Not yet implemented."); } diff --git a/java/org/artisanlogiciel/games/Main.java b/java/org/artisanlogiciel/games/Main.java index 0aa7afd..4b19244 100644 --- a/java/org/artisanlogiciel/games/Main.java +++ b/java/org/artisanlogiciel/games/Main.java @@ -14,25 +14,14 @@ public class Main return editor; } - public WallsProvider generate(int width, int height, int maxdepth, MazeResolutionListener listener) + public LabyMap generate2(MazeParamEditor params) { - LabyModel model = new LabyModel(width, height, maxdepth, new java.util.Random()); + params.setSeed(1024L); + LabyModel model = new LabyModel(params); model.generateWithEntry(0, 0); - LinkedList exits = new LinkedList(); - model.addEntryOrExit(-1, 0); - model.addEntryOrExit(width, height - 1); - if (!model.check()) - { - System.out.println("Check failed"); - } - model.resolve(width - 1, height - 1, listener); - return model; - } - public LabyMap generate2(int width, int height, int maxdepth) - { - LabyModel model = new LabyModel(width, height, maxdepth, new java.util.Random(1024L)); - model.generateWithEntry(0, 0); + final int width = params.getWidth(); + final int height = params.getHeight(); LinkedList exits = new LinkedList(); model.addEntryOrExit(-1, 0); model.addEntryOrExit(width, height - 1); @@ -51,7 +40,7 @@ public class Main { Main m = new Main(); MazeParamEditor editor = m.editor(); - LabyMap map = m.generate2(editor.width, editor.height, editor.maxdepth); + LabyMap map = m.generate2(editor); System.out.println(map.toShortString()); System.out.println(map.toString()); System.out.println(Brick.getDirLine()); diff --git a/java/org/artisanlogiciel/games/MazeCreationListener.java b/java/org/artisanlogiciel/games/MazeCreationListener.java index d425a70..633ecac 100644 --- a/java/org/artisanlogiciel/games/MazeCreationListener.java +++ b/java/org/artisanlogiciel/games/MazeCreationListener.java @@ -3,7 +3,7 @@ package org.artisanlogiciel.games; /** * MazeCreationListener **/ -interface MazeCreationListener +public interface MazeCreationListener { void changed(Position cornerleft, Position cornerright, WallsProvider provider); } diff --git a/java/org/artisanlogiciel/games/MazeParamEditor.java b/java/org/artisanlogiciel/games/MazeParamEditor.java index 664e1b2..003723b 100644 --- a/java/org/artisanlogiciel/games/MazeParamEditor.java +++ b/java/org/artisanlogiciel/games/MazeParamEditor.java @@ -8,6 +8,7 @@ import java.util.Scanner; **/ class MazeParamEditor implements MazeParams { + long seed; int width; int height; int maxdepth; @@ -27,6 +28,11 @@ class MazeParamEditor implements MazeParams maxdepth = console.nextInt(); } + public long getSeed() + { + return seed; + } + public int getWidth() { return width; @@ -51,6 +57,11 @@ class MazeParamEditor implements MazeParams return name; } + public void setSeed(long seed) + { + this.seed = seed; + } + public File getSaveDir() { return labdir; diff --git a/java/org/artisanlogiciel/games/MazeParams.java b/java/org/artisanlogiciel/games/MazeParams.java index 77f5753..e2020a8 100644 --- a/java/org/artisanlogiciel/games/MazeParams.java +++ b/java/org/artisanlogiciel/games/MazeParams.java @@ -7,6 +7,9 @@ import java.io.File; **/ public interface MazeParams { + /** currently seed of java.util.random **/ + public long getSeed(); + public int getWidth(); public int getHeight(); diff --git a/java/org/artisanlogiciel/games/MazeParamsFixed.java b/java/org/artisanlogiciel/games/MazeParamsFixed.java new file mode 100644 index 0000000..6f9e505 --- /dev/null +++ b/java/org/artisanlogiciel/games/MazeParamsFixed.java @@ -0,0 +1,76 @@ +package org.artisanlogiciel.games; + +import java.io.File; + +public class MazeParamsFixed implements MazeParams +{ + long seed; + int width; + int height; + int maxdepth; + File labdir; + String name; + + public MazeParamsFixed() + { + } + + public MazeParamsFixed(MazeParams params) + { + setParams(params.getSaveDir(),params.getWidth(),params.getHeight(),params.getMaxDepth()); + } + + public void setParams(File saveDir, int W, int H, int MD) + { + labdir = saveDir; + width=W; + height=H; + maxdepth=MD; + } + + public MazeParamsFixed(File saveDir, int W, int H, int MD, long seed) + { + name = null; + setParams(saveDir,W,H,MD); + this.seed=seed; + } + + public long getSeed() + { + return seed; + } + + public int getWidth() + { + return width; + } + + public int getHeight() + { + return height; + } + + public int getMaxDepth() + { + return maxdepth; + } + + public void setName(String n) + { + name = n; + } + + public String getName() + { + if (name == null) + { + name = "lab" + width + "x" + height; + } + return name; + } + + public File getSaveDir() + { + return labdir; + } +} diff --git a/java/org/artisanlogiciel/games/WallsProvider.java b/java/org/artisanlogiciel/games/WallsProvider.java index 6e61b4a..6c96c10 100644 --- a/java/org/artisanlogiciel/games/WallsProvider.java +++ b/java/org/artisanlogiciel/games/WallsProvider.java @@ -13,7 +13,12 @@ public interface WallsProvider /** * See Brick * - * Will set bits : 3 2 1 0 (8)(4)(2)(1) ^ > v < U R D L p i o e g w f h n t + * Will set bits : + * 3 2 1 0 + * (8)(4)(2)(1) + * ^ > v < + * U R D L + * p i o e g w f h n t * t **/ public short getWalls(int x, int y); diff --git a/java/org/artisanlogiciel/games/maze/gui/Display.java b/java/org/artisanlogiciel/games/maze/gui/Display.java new file mode 100644 index 0000000..79893e5 --- /dev/null +++ b/java/org/artisanlogiciel/games/maze/gui/Display.java @@ -0,0 +1,1164 @@ +package org.artisanlogiciel.games.maze.gui; + +import java.awt.BorderLayout; +import java.awt.image.BufferedImage; +import java.awt.Color; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Graphics; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; +import java.awt.event.InputEvent; +import java.awt.event.MouseEvent; +import java.awt.event.MouseMotionListener; +import java.io.*; +import java.util.LinkedList; +import java.util.Locale; +import java.util.ResourceBundle; + +import javax.imageio.ImageIO; + +import javax.swing.*; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import org.artisanlogiciel.games.*; +import org.artisanlogiciel.games.stl.Maze3dParams; +import org.artisanlogiciel.games.stl.Wall3d; +import org.artisanlogiciel.games.stl.Wall3dStream; +import org.artisanlogiciel.util.UTF8Control; + +import org.artisanlogiciel.graphics.Drawing; +import org.artisanlogiciel.graphics.DrawingLine; +import org.artisanlogiciel.graphics.SvgWriter; + +/** + * Display is Main JFrame for this tool + **/ +public class Display extends JFrame { + // to please eclipse, not supposed to be serialized + private static final long serialVersionUID = 8500214871372184418L; + + public final static ResourceBundle labels = ResourceBundle.getBundle("LabelsBundle", Locale.getDefault(), Display.class.getClassLoader(), new UTF8Control()); + + MazeComponent maze; + MazeControler controler; + LabyModel model; + boolean autoSize; + + MazeParams params = null; + JTextField statusField = null; + + Maze3dSettings stlsettings; + + Display(LabyModel model, int W, int H, MazeParams params) { + super(labels.getString("title")); + if (params != null) { + // fixedParams = new MazeParamsFixed(params.getSaveDir(),params.getWidth(),params.getHeight(),params.getMaxDepth()); + this.params = params; + } + this.model = model; + maze = createMazeComponent(model, W, H); + + Container con = this.getContentPane(); + JScrollPane scrollableMaze = new JScrollPane(maze); + scrollableMaze.addMouseMotionListener(maze); + con.add(scrollableMaze, BorderLayout.CENTER); + controler = new MazeControler(params); + con.add(controler.getMoveControl(), BorderLayout.NORTH); + con.add(controler.getGenerationControl(), BorderLayout.SOUTH); + + addComponentListener(new ComponentAdapter() { + @Override + public void componentResized(ComponentEvent e) { + if (autoSize) { + maze.getAutoSize(); + } + } + }); + + model.setMazeListener(maze); + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + setBounds(W, H, W, H); + setVisible(true); + } + + private MazeComponent createMazeComponent(LabyModel model, int W, int H) { + MazeCellParameters cp = new MazeCellParameters(model.getWidth(), model.getHeight(), W, H, 3, 3); + MazeComponent comp = new MazeComponent(model, cp); + return comp; + } + + void recreateModel() { + // recreate labyrinth + if (params != null) { + + // Keep current model to be able to complete a manualy edited one + maze.resetWallsProvider(model); + model.setMazeListener(maze); + + model.reset(); + + // we are in GUI event thread, need to release it and do it outside. + new Thread() { + public void run() { + model.generateWithEntry(0, 0); + model.addEntryOrExit(-1, 0); + model.addEntryOrExit(params.getWidth(), params.getHeight() - 1); + } + }.start(); + } + } + + void resetModel() { + // recreate labyrinth + if (params != null) { + params = controler.getSettings().resetParams(); + model = new LabyModel(params); + + refresh(); + } + } + + void refresh() + { + // reinit labyrinth view + if (params != null) { + maze.resetWallsProvider(model); + model.setMazeListener(maze); + + // we are in GUI event thread, need to release it and do it outside. + new Thread() { + public void run() { + maze.changed(null, null, model); + } + }.start(); + + } + } + + void resolve() { + model.reset(); + 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); + } + + void setAutoSize(boolean autoSize) { + this.autoSize = autoSize; + if (autoSize) { + maze.getAutoSize(); + } + } + + void addWallInDrawing(int pX, int pY, Drawing d) { + short walls = model.getWalls(pX, pY); + short wdrawn = 0; + // todo + int w = 2; + int h = 2; + int ox = 0; + int oy = 0; + + int x = ox + (int) (pX * w); + int y = oy + (int) (pY * h); + DrawingLine dl = new DrawingLine(); + // order matters since all points are linked + if ((pY == 0) && LabyModel.isFlagSet(walls, Brick.UP)) { + dl.addPoint(new Point(x, y)); + dl.addPoint(new Point(x + (int) w, y)); + wdrawn |= Brick.UP; + } + if (LabyModel.isFlagSet(walls, Brick.RIGHT)) { + if (!LabyModel.isFlagSet(wdrawn, Brick.UP)) { + dl.addPoint(new Point(x + (int) w, y)); + } + dl.addPoint(new Point(x + (int) w, y + (int) h)); + wdrawn |= Brick.RIGHT; + } + if (LabyModel.isFlagSet(walls, Brick.DOWN)) { + if (!LabyModel.isFlagSet(wdrawn, Brick.RIGHT)) { + if (wdrawn != 0) { + d.addLine(dl); + dl = new DrawingLine(); + } + dl.addPoint(new Point(x + (int) w, y + (int) h)); + } + dl.addPoint(new Point(x, y + (int) h)); + wdrawn |= Brick.DOWN; + } + if ((pX == 0) && LabyModel.isFlagSet(walls, Brick.LEFT)) { + if (!LabyModel.isFlagSet(wdrawn, Brick.DOWN)) { + if (wdrawn != 0) { + d.addLine(dl); + dl = new DrawingLine(); + } + dl.addPoint(new Point(x, y + (int) h)); + } + dl.addPoint(new Point(x, y)); + wdrawn |= Brick.LEFT; + } + if (wdrawn != 0) { + d.addLine(dl); + } + } + + Drawing createDrawing() { + Drawing d = new Drawing(); + { + short walls; + + // draw all walls within clip bounds horiz first then lines + for (int y = 0; y < model.getHeight(); y++) { + for (int x = 0; x < model.getWidth(); x++) { + addWallInDrawing(x, y, d); + } + } + } + return d; + } + + // to allow to log / write somewher into screen... + void writeSentence(String pSentence) { + // TODO + addStatus(pSentence); + } + + void writeError(String pError) { + System.err.println(pError); + } + + void saveImc() { + Drawing d = createDrawing(); + + if (d != null) { + File outfile = getFileForExtension("imc"); + writeSentence("Saving to " + outfile + " ..."); + try { + DataOutputStream out = new DataOutputStream(new FileOutputStream(outfile)); + d.saveLinesKompressed(out); + out.flush(); + out.close(); + writeSentence("... Done."); + } catch (IOException io) { + io.printStackTrace(System.err); + } + } + } + + File getFileForExtension(final String extension) + { + return new File(params.getSaveDir(), params.getName() + "." + extension); + } + + void saveSvg() { + Drawing d = createDrawing(); + + if (d != null) { + File outfile = getFileForExtension("svg"); + writeSentence("Saving to " + outfile + " ..."); + try { + DataOutputStream out = new DataOutputStream(new FileOutputStream(outfile)); + SvgWriter writer = new SvgWriter(d.getInternLines()); + writer.writeTo(out); + out.flush(); + out.close(); + writeSentence("... Done."); + } catch (IOException io) { + io.printStackTrace(System.err); + } + } else { + writeError("drawing creation failed"); + } + } + + + void savePng() { + File file = getFileForExtension("png"); + // BufferedImage bi = new BufferedImage(this.getSize().width, this.getSize().height, BufferedImage.TYPE_INT_ARGB); + BufferedImage bi = new BufferedImage(maze.getSize().width, maze.getSize().height, BufferedImage.TYPE_INT_ARGB); + Graphics g = bi.createGraphics(); + // this.paint(g); + maze.paint(g); + g.dispose(); + try { + ImageIO.write(bi, "png", file); + } catch (Exception e) { + e.printStackTrace(); + } + } + + void addStatus(String pStatus) + { + statusField.setText(pStatus); + } + + + private class MazeControler extends JPanel { + /** + * + */ + private static final long serialVersionUID = 1L; + + private MazeSettings settings = null; + + private JPanel resizecontrol = null; + + private void setMazeName(String pName) { + MazeParamsFixed p = (MazeParamsFixed) params; + p.setName(pName); + } + + private JMenu createLoadingMenu() { + JMenu loadMenu = new JMenu(labels.getString("load") ); + + final JTextField loadName = new JTextField("newlaby"); + + loadMenu.add(loadName); + + Action loadRawAction = new AbstractAction() { + public void actionPerformed(ActionEvent evt) { + // + addStatus("load raw"); + + String filename = loadName.getText(); + + if ((filename.length() > 0)) { + setMazeName(filename); + loadRaw(); + refresh(); + } + + } + }; + JButton loadRawButton = new JButton(labels.getString("load" ) + " raw"); + loadRawButton.addActionListener(loadRawAction); + + loadMenu.add(loadRawButton); + return loadMenu; + } + + private JMenu createSavingMenu() { + final JTextField saveName = new JTextField("newlaby"); + final JButton savePngButton = new JButton(labels.getString("save") + " png"); + Action savePngAction = new AbstractAction() { + public void actionPerformed(ActionEvent evt) { + // + addStatus("save png"); + setMazeName(saveName.getText()); + savePng(); + } + }; + savePngButton.addActionListener(savePngAction); + final JButton saveSvgButton = new JButton(labels.getString("save") + " svg"); + Action saveSvgAction = new AbstractAction() { + public void actionPerformed(ActionEvent evt) { + writeSentence("save svg"); + setMazeName(saveName.getText()); + saveSvg(); + } + }; + saveSvgButton.addActionListener(saveSvgAction); + final JButton saveButton = new JButton(labels.getString("save") + " raw"); + Action saveAction = new AbstractAction() { + public void actionPerformed(ActionEvent evt) { + // + addStatus("save"); + setMazeName(saveName.getText()); + MazeParamsFixed p = (MazeParamsFixed) params; + save(p, model); + } + }; + saveButton.addActionListener(saveAction); + final JButton saveImcButton = new JButton(labels.getString("save") + " imc"); + Action saveImcAction = new AbstractAction() { + public void actionPerformed(ActionEvent evt) { + // + addStatus("save imc"); + setMazeName(saveName.getText()); + saveImc(); + } + }; + saveImcButton.addActionListener(saveImcAction); + final JButton saveStlButton = new JButton(labels.getString("save") + " stl"); + Action saveStlAction = new AbstractAction() { + public void actionPerformed(ActionEvent evt) { + // + addStatus("save stl"); + setMazeName(saveName.getText()); + MazeParamsFixed p = (MazeParamsFixed) params; + saveStl(p, model,stlsettings.createParams()); + } + }; + saveStlButton.addActionListener(saveStlAction); + + stlsettings = new Maze3dSettings(new Maze3dParams()); + + JMenu saveMenu = new JMenu(labels.getString("save") ); + saveMenu.add(saveName); + saveMenu.add(saveSvgButton); + saveMenu.add(savePngButton); + saveMenu.add(saveButton); + saveMenu.add(stlsettings); + saveMenu.add(saveStlButton); + saveMenu.add(saveImcButton); + + return saveMenu; + + } + + private JPanel createResolveQuitBar() { + JPanel resolveQuitBar = new JPanel(new FlowLayout()); + + JButton buttonCreate = new JButton(labels.getString("create")); + buttonCreate.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + recreateModel(); + } + }); + resolveQuitBar.add(buttonCreate); + + JButton resolveButton = new JButton(labels.getString("resolve")); + resolveButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + // + addStatus("Resolving"); + resolve(); + } + }); + + resolveQuitBar.add(resolveButton); + + final JButton quitButton = new JButton(labels.getString("quit")); + Action quitAction = new AbstractAction() { + public void actionPerformed(ActionEvent evt) { + // + addStatus("quit"); + System.exit(0); + } + }; + quitButton.addActionListener(quitAction); + + resolveQuitBar.add(quitButton); + + JButton buttonReset = new JButton("reset");//labels.getString("reset")); + buttonReset.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + resetModel(); + } + }); + resolveQuitBar.add(buttonReset); + + return resolveQuitBar; + + } + + public MazeControler(MazeParams params) { + // super(new BorderLayout()); + super(); + BoxLayout layout = new BoxLayout(this, BoxLayout.Y_AXIS); + setLayout(layout); + + JMenuBar menuBar = new JMenuBar(); + menuBar.add(createSavingMenu()); + menuBar.add(createLoadingMenu()); + add(menuBar); + + add(createResolveQuitBar()); + + JPanel controlMovesPanel = new JPanel(new BorderLayout()); + settings = new MazeSettings(params); + @SuppressWarnings("serial") + Action goNorth = new AbstractAction() { + public void actionPerformed(ActionEvent evt) { + // + addStatus("go North"); + goNorth(); + } + }; + JButton north = addDirection(this, labels.getString("north"), "UP", goNorth); + + @SuppressWarnings("serial") + Action goEast = new AbstractAction() { + public void actionPerformed(ActionEvent evt) { + // + addStatus("go East"); + goEast(); + } + }; + + JButton east = addDirection(this, labels.getString("east"), "RIGHT", goEast); + + @SuppressWarnings("serial") + Action goWest = new AbstractAction() { + public void actionPerformed(ActionEvent evt) { + // + addStatus("go West"); + goWest(); + } + }; + JButton west = addDirection(this, labels.getString("west"), "LEFT", goWest); + + @SuppressWarnings("serial") + Action goSouth = new AbstractAction() { + public void actionPerformed(ActionEvent evt) { + // + addStatus("go South"); + goSouth(); + } + }; + JButton south = addDirection(this, labels.getString("south"), "DOWN", goSouth); + + controlMovesPanel.add(north, BorderLayout.NORTH); + controlMovesPanel.add(west, BorderLayout.WEST); + controlMovesPanel.add(east, BorderLayout.EAST); + controlMovesPanel.add(south, BorderLayout.SOUTH); + + JPanel controlPanel = new JPanel(); + + controlPanel.add(controlMovesPanel,BorderLayout.LINE_START); + statusField = new JTextField("",20); + controlPanel.add(statusField, BorderLayout.CENTER); + add(controlPanel, BorderLayout.NORTH); + + // control display panel contains controls for display + // zoom , autozoom. + final JPanel controlDisplayPanel = new JPanel(new BorderLayout()); + final JCheckBox showAll = new JCheckBox("show all"); + showAll.addChangeListener(new ChangeListener() { + public void stateChanged(ChangeEvent e) { + maze.setShowAll(showAll.isSelected()); + } + }); + + final JSlider slider = new JSlider(2, 40); + slider.addChangeListener(new ChangeListener() { + public void stateChanged(ChangeEvent e) { + setWallSize(slider.getValue()); + } + }); + final JCheckBox autoSlide = new JCheckBox("autoslide"); + autoSlide.addChangeListener(new ChangeListener() { + public void stateChanged(ChangeEvent e) { + setAutoSize(autoSlide.isSelected()); + } + }); + + resizecontrol = new JPanel(new FlowLayout()); + resizecontrol.add(showAll); + resizecontrol.add(slider); + resizecontrol.add(autoSlide); + + add(controlDisplayPanel, BorderLayout.SOUTH); + add(resizecontrol, BorderLayout.WEST); + add(settings, BorderLayout.EAST); + + } + + private JPanel getMoveControl() { + return this; + } + + private JPanel getResizeControl() { + return resizecontrol; + } + + private JPanel getGenerationControl() { + return settings; + } + + public MazeSettings getSettings() { + return settings; + } + + } + + private void loadRaw() { + File infile = new File(params.getSaveDir(), params.getName() + ".raw"); + FileInputStream inputStream = null; + try { + inputStream = new FileInputStream(infile); + model = new LabyModel("raw", inputStream); + } catch (IOException io) { + io.printStackTrace(System.err); + statusField.setText("[ERROR] Can't load " + infile.getAbsolutePath()); + } + finally + { + if (inputStream != null ) + { + // cleanup + try { + inputStream.close(); + } + catch (Exception any) + { + // don't care really + } + } + } + } + + 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); + // attach keys to maze + maze.getInputMap().put(keystroke, actionName); + maze.getActionMap().put(actionName, goAction); + return button; + } + + private static class MazeCellParameters { + double width = 10; // width of one cell + double height = 10; // height of one cell + int offsetX = 5; // x offset of upper corner left + int offsetY = 5; // y offset of upper corner left + + int mapWidth = 0; + int mapHeight = 0; + + public MazeCellParameters(int mapw, int maph, int W, int H, int x, int y) { + double w = (W - x) / mapw; + double h = (H - y) / maph; + mapWidth = mapw; + mapHeight = maph; + if (w < 5) + w = 5; + if (h < 5) + h = 5; + setCellSize(w, h); + offsetX = x; + offsetY = y; + } + + public void resetMazeWidthHeight(int mapw, int maph) { + mapWidth = mapw; + mapHeight = maph; + } + + public void adaptTo(double W, double H) { + double w = (W - offsetX) / mapWidth; + double h = (H - offsetY) / mapHeight; + mapWidth = mapWidth; + mapHeight = mapHeight; + if (w < 5) + w = 5; + if (h < 5) + h = 5; + setCellSize(w, h); + } + + public void setCellSize(double w, double h) { + width = w; + height = h; + } + + public double getWidth() { + return width; + } + + public double getHeight() { + return height; + } + + public int getOffsetX() { + return offsetX; + } + + public int getOffsetY() { + return offsetY; + } + + public Dimension getDimension() { + return new Dimension(offsetX + (int) (mapWidth * width), + offsetY + (int) (mapHeight * height)); + } + + public void drawWalls(Graphics g, int pX, int pY, short walls) { + int x = offsetX + (int) (pX * width); + int y = offsetY + (int) (pY * height); + if ((pY == 0) && ((walls & Brick.UP) == Brick.UP)) + g.drawLine(x, y, x + (int) width, y); + if ((walls & Brick.DOWN) == Brick.DOWN) + g.drawLine(x, y + (int) height, x + (int) width, y + (int) height); + if ((walls & Brick.RIGHT) == Brick.RIGHT) + g.drawLine(x + (int) width, y, x + (int) width, y + (int) height); + if ((pX == 0) && ((walls & Brick.LEFT) == Brick.LEFT)) + g.drawLine(x, y, x, y + (int) height); + } + + public void drawPath(Graphics g, DirectionPosition dp, int pX, int pY, int mX, int mY) { + if (dp != null) { + Position dot = dp.getPosition(); + if ((dot.getX() >= pX) && (dot.getY() >= pY) && (dot.getX() < mX) && (dot.getY() < mY)) { + int x = offsetX + (int) (dot.getX() * width); + int y = offsetY + (int) (dot.getY() * height); + short path = dp.getDirection(); + int xm = x + (int) (width / 2); + int ym = y + (int) (height / 2); + if ((path & LabyModel.HORIZONTAL) == LabyModel.HORIZONTAL) { + if ((path & LabyModel.POSITIVE) == LabyModel.POSITIVE) { + g.drawLine(xm, ym, x + (int) width, ym); + g.drawLine(xm, ym + (int) (height / 4), x + (int) width, ym); + } + // LEFT /_ + if ((path & LabyModel.NEGATIVE) == LabyModel.NEGATIVE) { + g.drawLine(x, ym, xm, ym); + g.drawLine(x, ym, xm, ym - (int) (height / 4)); + } + } + if ((path & LabyModel.VERTICAL) == LabyModel.VERTICAL) { + if ((path & LabyModel.POSITIVE) == LabyModel.POSITIVE) { + g.drawLine(xm, ym, xm, y + (int) height); + g.drawLine(xm - (int) (width / 4), ym, xm, y + (int) height); + } + // UP |\ + if ((path & LabyModel.NEGATIVE) == LabyModel.NEGATIVE) { + g.drawLine(xm, ym, xm, y); + g.drawLine(xm + (int) (width / 4), ym, xm, y); + } + } + } + } + } + + public void drawDot(Graphics g, Position dot, int pX, int pY, int mX, int mY) { + double radius = (height > width) ? width : height; + int a = (int) (radius / 4); + if ((dot.getX() >= pX) && (dot.getY() >= pY) && (dot.getX() < mX) && (dot.getY() < mY)) { + int x = offsetX + (int) (dot.getX() * width); + int y = offsetY + (int) (dot.getY() * height); + int r2 = (int) ((radius * 3) / 4); + g.drawOval(x + 1, y + 1, r2, r2); + // 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 + (int) (pX * width); + int y = offsetY + (int) (pY * height); + g.drawLine(x + 1, y + 1, x + (int) width - 1, y + (int) height - 1); + } + } + + } + + private class MazeComponent + extends JComponent + implements MazeCreationListener, + MazeResolutionListener, + MouseMotionListener { + private static final long serialVersionUID = 3163272907991176390L; + + // WallsProvider map; + LabyModel map; + final MazeCellParameters cp; + Position current = null; + LinkedList solvedPath = null; + LinkedList drawingPath = null; + final Object lockChange = new Object(); + // current postion of human resolving + int sX = 0; + int sY = 0; + // goal exit + int gX = -1; + int gY = -1; + + // not set by default for debug, will show any path + boolean showAll = false; + + // for a given (x,y) pixel return cell position. + Position getPosition(int x, int y) { + int pX = (int) ((double) (x - cp.getOffsetX()) / cp.getWidth()); + int pY = (int) ((double) (y - cp.getOffsetY()) / cp.getHeight()); + return new Position(pX, pY); + } + + @Override + public void mouseDragged(MouseEvent e) { + // should find the cell ... + DirectionPosition last = null; + short path = 0; + Position newPosition = getPosition(e.getX(), e.getY()); + if (drawingPath == null) { + drawingPath = new LinkedList<>(); + last = new DirectionPosition((short) 0, newPosition); + addStatus("Mouse dragged Cell " + newPosition + " Button " + e.getModifiersEx() + " " + InputEvent.BUTTON1_DOWN_MASK); + drawingPath.addLast(last); + } else { + // setShowAll(true); + DirectionPosition first = drawingPath.getLast(); + last = first; + // construct as many move form last to new position as needed. + while (!last.getPosition().equals(newPosition)) { + path = LabyModel.getDirection(last.getPosition(), newPosition); + last.setDirection(path); + // button 1 : add direction; button 2 : replace with direction. + if ((e.getModifiersEx() & InputEvent.BUTTON1_DOWN_MASK) != 0) { + map.addDirection(last.getPosition().getX(), last.getPosition().getY(), path); + } else { + map.setDirection(last.getPosition().getX(), last.getPosition().getY(), path); + } + last = last.moveToAdjacentDirection(); + drawingPath.addLast(last); + } + addStatus("Mouse dragged from Cell " + first.getPosition() + "To" + newPosition + " Button " + e.getModifiersEx() + " " + InputEvent.BUTTON1_DOWN_MASK); + } + changed(null, null, map); + } + + @Override + public void mouseMoved(MouseEvent e) { + // addStatus("Mouse moved (" + e.getX() + ',' + e.getY() + ')'); + Position newPosition = getPosition(e.getX(), e.getY()); + if ((newPosition.getX() >= 0) && (newPosition.getY() >= 0)) { + requestFocus(); + } + } + + public void setShowAll(boolean showall) { + this.showAll = showall; + addStatus(showAll ? "show all" : "X"); + } + + void checkExit() { + if ((sX == gX) && (sY == gY)) { + addStatus("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(); + } + + } + + /** + * width, height of one cell, + * offsetX, offsetY of upper left corner + **/ + // public MazeComponent(WallsProvider map, MazeCellParameters cp) + public MazeComponent(LabyModel map, MazeCellParameters cp) { + super(); + this.cp = cp; + this.map = map; + setPreferredSize(cp.getDimension()); + gX = map.getWidth() - 1; + gY = map.getHeight() - 1; + } + + // public void resetWallsProvider(WallsProvider map) + public void resetWallsProvider(LabyModel map) { + this.map = map; + solvedPath = null; + // could be kept + drawingPath = null; + sX = 0; + sY = 0; + cp.resetMazeWidthHeight(map.getWidth(), map.getHeight()); + setPreferredSize(cp.getDimension()); + } + + public void setWallSize(int size) { + cp.setCellSize((double) size, (double) size); + // should redraw ... + invalidate(); + repaint(); + } + + public int getAutoSize() { + Rectangle r = getBounds(); + cp.adaptTo((int) r.getWidth(), (int) r.getHeight()); + // should redraw ... + invalidate(); + repaint(); + return 50; + } + + 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 !"); + } + } + } + + @Override + protected void paintComponent(Graphics g) { + super.paintComponent(g); + int x = 0; + int y = 0; + short walls = 0; + short path = 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() / cp.getWidth()); + int mY = (int) ((double) r.getHeight() / cp.getHeight()); + int pX = (int) ((double) (r.getX() - cp.getOffsetX()) / cp.getWidth()); + int pY = (int) ((double) (r.getY() - cp.getOffsetY()) / cp.getHeight()); + + 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.green); + cp.drawDot(g, new Position(gX, gY), pX, pY, mX, mY); + g.setColor(Color.blue); + } + cp.drawDot(g, new Position(sX, sY), pX, pY, mX, mY); + + synchronized (lockChange) { + g.setColor(Color.red); + if (current != null) { + cp.drawDot(g, current, pX, pY, mX, mY); + } + if (solvedPath != null) { + for (DirectionPosition resolved : solvedPath) { + // cp.drawDot(g, resolved.getPosition(), pX, pY, mX, mY); + cp.drawPath(g, resolved, pX, pY, mX, mY); + } + } + } + + int aX = pX; + int aY = pY; + + g.setColor(Color.black); + + // draw all walls within clip bounds horiz first then lines + for (; pY < mY; pY++) { + for (pX = 0; pX < mX; pX++) { + walls = map.getWalls(pX, pY); + cp.drawWalls(g, pX, pY, walls); + } + } + + if (this.showAll) { + addStatus("*"); + pX = aX; + pY = aY; + + // draw all path within clip bounds horiz first then lines + for (; pY < mY; pY++) { + for (pX = 0; pX < mX; pX++) { + path = map.getPath(pX, pY); + if ((path & LabyModel.SOLVED) == LabyModel.SOLVED) { + g.setColor(Color.green); + } else { + g.setColor(Color.blue); + } + if (path != 0) { + DirectionPosition dp = new DirectionPosition(path, new Position(pX, pY)); + cp.drawPath(g, dp, pX, pY, pX + 1, pY + 1); + } + } + } + } + } + + + public boolean notifySearch(DirectionPosition pPosition) { + synchronized (lockChange) { + current = pPosition.getPosition(); + } + // 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); + addStatus("resolution completed"); + synchronized (lockChange) { + this.solvedPath = newPath; + } + // should redraw ... + invalidate(); + repaint(); + } + + public void resetResolution() { + solvedPath = null; + } + } + + private static void setupDisplay(LabyModel model, int W, int H, MazeParams params) { + Display display = new Display(model, W, H, params); + } + + public void saveStl(MazeParamsFixed params, LabyModel model, Maze3dParams wallparams) { + File outfile = getFileForExtension("stl"); + if (!outfile.exists()) { + addStatus("Saving to " + outfile + " ..."); + try { + FileOutputStream out = new FileOutputStream(outfile); + new Wall3dStream(params.getName(), model, out, wallparams).stream(); + out.close(); + addStatus("... Done."); + } catch (IOException io) { + io.printStackTrace(System.err); + } + } else { + addStatus("" + outfile + " already exists"); + } + + } + + public void save(MazeParamsFixed params, LabyModel model) { + File outfile = getFileForExtension("raw"); + if (!outfile.exists()) { + addStatus("Saving to " + outfile + " ..."); + try { + FileOutputStream out = new FileOutputStream(outfile); + model.streamOut("raw", out); + out.flush(); + out.close(); + addStatus("... Done."); + } catch (IOException io) { + io.printStackTrace(System.err); + } + } else { + addStatus("" + outfile + " already exists"); + } + } + + + public static void main(String pArgs[]) { + LabyModel model = null; + int W = 600; + int H = 400; + + System.out.println("Default Locale " + Locale.getDefault()); + + if ((pArgs.length > 0) && (pArgs[0].length() > 0)) { + try { + model = new LabyModel("raw", new FileInputStream(pArgs[0])); + } catch (IOException io) { + io.printStackTrace(System.err); + System.exit(1); + } + + setupDisplay(model, W, H, null); + } else { + MazeParamsFixed params = new MazeParamsFixed(new File("lab"), 20, 20, 12,1024L); + model = new LabyModel(params); + + setupDisplay(model, W, H, params); + + /* + model.generateWithEntry(0, 0); + + model.addEntryOrExit(-1, 0); + model.addEntryOrExit(params.getWidth(), params.getHeight() - 1); + + addStatus("Generation completed"); + */ + /* + */ + } + + } +} diff --git a/java/org/artisanlogiciel/games/maze/gui/Maze3dSettings.java b/java/org/artisanlogiciel/games/maze/gui/Maze3dSettings.java new file mode 100644 index 0000000..601e396 --- /dev/null +++ b/java/org/artisanlogiciel/games/maze/gui/Maze3dSettings.java @@ -0,0 +1,72 @@ +package org.artisanlogiciel.games.maze.gui; + +import org.artisanlogiciel.games.MazeParams; +import org.artisanlogiciel.games.stl.Maze3dParams; +import org.artisanlogiciel.games.stl.Wall3d; + +import javax.swing.*; + +public class Maze3dSettings + extends JPanel +{ + // grid size + JTextField xl; + JTextField yl; + JTextField zl; + + JTextField w; + JTextField lg; + JTextField hg; + + JCheckBox reverse; + + Maze3dParams params; + + public Maze3dSettings(Maze3dParams params) { + super(); + this.params = params; + createSettingsGui(); + } + + void createSettingsGui() { + if (params != null) { + JLabel widthLabel = new JLabel(Display.labels.getString("width")); + xl = new JTextField("0" + params.getXl()); + add(widthLabel); + add(xl); + JLabel heightLabel = new JLabel(Display.labels.getString("height")); + zl = new JTextField("0" + params.getZl()); + add(heightLabel); + add(zl); + JLabel depthLabel = new JLabel(Display.labels.getString("depth")); + yl = new JTextField("0" + params.getYl()); + add(depthLabel); + add(yl); + + reverse = new JCheckBox("reverse",params.isReverse()); + add(reverse); + + w = new JTextField("0" + params.getW()); + add(w); + + // lowground hightground + lg = new JTextField("0" + params.getLg()); + add(lg); + hg = new JTextField("0" + params.getHg()); + add(hg); + } + } + + Maze3dParams createParams() + { + return new Maze3dParams( + Integer.parseInt(xl.getText()), + Integer.parseInt(yl.getText()), + Integer.parseInt(zl.getText()), + Integer.parseInt(w.getText()), + Integer.parseInt(lg.getText()), + Integer.parseInt(hg.getText()), + reverse.isSelected()); + } + +} diff --git a/java/org/artisanlogiciel/games/maze/gui/MazeSettings.java b/java/org/artisanlogiciel/games/maze/gui/MazeSettings.java new file mode 100644 index 0000000..3feca25 --- /dev/null +++ b/java/org/artisanlogiciel/games/maze/gui/MazeSettings.java @@ -0,0 +1,56 @@ +package org.artisanlogiciel.games.maze.gui; + +import org.artisanlogiciel.games.MazeParams; +import org.artisanlogiciel.games.MazeParamsFixed; + +import javax.swing.*; +import java.util.Random; + +public class MazeSettings extends JPanel { + MazeParams params; + + JTextField textWidth = null; + JTextField textHeight = null; + JTextField textDepth = null; + JTextField textSeed = null; + + // TODO set width and height and depth of maze with gui + public MazeSettings(MazeParams params) { + super(); + this.params = params; + createSettingsGui(); + } + + void createSettingsGui() { + if (params != null) { + JLabel widthLabel = new JLabel(Display.labels.getString("width")); + textWidth = new JTextField("0" + params.getWidth()); + add(widthLabel); + add(textWidth); + JLabel heightLabel = new JLabel(Display.labels.getString("height")); + textHeight = new JTextField("0" + params.getHeight()); + add(heightLabel); + add(textHeight); + JLabel depthLabel = new JLabel(Display.labels.getString("depth")); + textDepth = new JTextField("0" + params.getMaxDepth()); + add(depthLabel); + add(textDepth); + JLabel seedLabel = new JLabel(Display.labels.getString("seed")); + textSeed = new JTextField( "" + params.getSeed(),16); + add(seedLabel); + add(textSeed); + } + } + + public MazeParams resetParams() { + params = new MazeParamsFixed(params.getSaveDir(), + Integer.parseInt(textWidth.getText()), + Integer.parseInt(textHeight.getText()), + Integer.parseInt(textDepth.getText()), + new Random().nextLong() + ); + textSeed.setText("" + params.getSeed()); + return params; + } + +} diff --git a/java/org/artisanlogiciel/games/stl/Maze3dParams.java b/java/org/artisanlogiciel/games/stl/Maze3dParams.java new file mode 100644 index 0000000..c8c34f3 --- /dev/null +++ b/java/org/artisanlogiciel/games/stl/Maze3dParams.java @@ -0,0 +1,104 @@ +package org.artisanlogiciel.games.stl; + +public class Maze3dParams { + + // grid size + int xl; + int yl; + int zl; + + int w; + int lg; + int hg; + + Wall3d south; + Wall3d west; + Wall3d north; + Wall3d east; + Wall3d highGround; + Wall3d lowGround; + + // reverse : means that resolved path will be HighGround actual making maze more difficult to play with + boolean reverse; + + // default + public Maze3dParams() + { + this(10,10,10, 1 , 1, 3, false); + } + + // w width is wall thickness + public Maze3dParams(int xl, int yl, int zl, int w, int lg, int hg, boolean reverse) + { + this.xl = xl; + this.yl = yl; + this.zl = zl; + this.reverse = reverse; + int h = zl; + + this.w = w; + this.lg = lg; + this.hg = hg; + + south = new Wall3d(xl, w, h, 0, 0, 0); + west = new Wall3d(w, yl, h, 0, 0, 0); + north = new Wall3d(xl, w, h, 0, yl, 0); + east = new Wall3d(w, yl, h, xl, 0, 0); + highGround = new Wall3d(xl, yl, hg, 0, 0, 0); + lowGround = new Wall3d(xl, yl, lg, 0, 0, 0); + } + + public boolean isReverse() { + return reverse; + } + + public int getXl() { + return xl; + } + + public int getYl() { + return yl; + } + + public int getZl() { + return zl; + } + + public Wall3d getSouth() { + return south; + } + + public Wall3d getWest() { + return west; + } + + public Wall3d getNorth() { + return north; + } + + public Wall3d getEast() { + return east; + } + + public Wall3d getHighGround() { + return highGround; + } + + public Wall3d getLowGround() { + return lowGround; + } + + public int getLg() { + return lg; + } + + public int getHg() { + return hg; + } + + public int getW() { + return w; + } + + +} diff --git a/java/org/artisanlogiciel/games/stl/Wall3d.java b/java/org/artisanlogiciel/games/stl/Wall3d.java index cb0a582..7f77f3d 100644 --- a/java/org/artisanlogiciel/games/stl/Wall3d.java +++ b/java/org/artisanlogiciel/games/stl/Wall3d.java @@ -11,114 +11,134 @@ import java.io.IOException; /** * Wall3d to create walls in 3d for stl conversion South, West North East... + * + * wall3D will create a rectangular cuboid with 2 triangle vertex for each face **/ -public class Wall3d -{ +public class Wall3d { + // 4 triangles in 2 dim space reused 3 times - final static int BASE[][][] = { { { 0, 0 }, { 1, 0 }, { 0, 1 } }, { { 1, 0 }, { 1, 1 }, { 0, 1 } }, - { { 0, 0 }, { 1, 0 }, { 1, 1 } }, { { 0, 0 }, { 1, 1 }, { 0, 1 } } }; + // in facts order matters... + final static int BASE[][][] = { + // lower left + {{0, 0}, {1, 0}, {0, 1}}, + // higher right + {{1, 0}, {1, 1}, {0, 1}}, + // lower right + {{0, 0}, {1, 0}, {1, 1}}, + // higher left + {{0, 0}, {1, 1}, {0, 1}}}; final static short X = 1; final static short Y = 2; final static short Z = 4; // final static short AXIS[][]= {{X,Y},{-Z,Y},{X,Y},{Z,Y},{X,-Z},{X,-Z}}; - final static short AXIS[][] = { { X, Y, 0 }, { Z, Y, 0 }, { X, Y, 1 }, { Z, Y, 1 }, { X, Z, 0 }, { X, Z, 1 } }; - public final static Wall3d South = new Wall3d(10, 1, 10, 0, 0, 0); - public final static Wall3d West = new Wall3d(1, 10, 10, 0, 0, 0); - public final static Wall3d North = new Wall3d(10, 1, 10, 0, 10, 0); - public final static Wall3d East = new Wall3d(1, 10, 10, 10, 0, 0); - public final static Wall3d HighGround = new Wall3d(10, 10, 3, 0, 0, 0); - public final static Wall3d LowGround = new Wall3d(10, 10, 2, 0, 0, 0); + // [face][axis] + final static short AXIS[][] = { + // face 0 (x,y) back + {X, Y, 0}, + // face 1 (z,y) left + {Z, Y, 0}, + // face 2 (x,y,1) + {X, Y, 1}, + // face 3 + {Z, Y, 1}, + // face 4 + {X, Z, 0}, + // face 5 + {X, Z, 1}}; + + final static String normalx = "1.0 0.0 0.0"; + final static String normaly = "0.0 1.0 0.0"; + final static String normalz = "0.0 0.0 1.0"; + final static String normalmx = "-1.0 0.0 0.0"; + final static String normalmy = "0.0 -1.0 0.0"; + final static String normalmz = "0.0 0.0 -1.0"; + + // final static short NORMAL[] = {Z,X,Z,X,Y,Y}; + + final static String normalstr[] = + { normalmz, normalx, + normalz, normalmx, + normaly, normalmy + }; + + boolean reverse_vertex[] = { + true,false,false,true,false,true + }; int triangle[][][] = null; - public Wall3d(int t[][][]) - { + public Wall3d(int t[][][]) { triangle = t; } - public Wall3d(Wall3d origin, int dx, int dy, int dz) - { + public Wall3d(Wall3d origin, int dx, int dy, int dz) { triangle = origin.translate(dx, dy, dz); } - Wall3d(int xl, int yl, int zl, int dx, int dy, int dz) - { + Wall3d(int xl, int yl, int zl, int dx, int dy, int dz) { int f = 0; triangle = new int[12][3][3]; - int[] factor = { xl, yl, zl }; - int[] translate = { dx, dy, dz }; - for (int i = 0; i < 12; i++) - { - // point in a triangle - for (int p = 0; p < 3; p++) - { + int[] factor = {xl, yl, zl}; + int[] translate = {dx, dy, dz}; + for (int facet = 0; facet < 12; facet++) { + // point in a triangle / facet + for (int p = 0; p < 3; p++) { short uaxis = 0; - for (int axis = 0; axis < 2; axis++) - { - short caxis = AXIS[i / 2][axis]; - if (caxis > 0) - { + // a square face is two facets. + int face = facet / 2; + // first two axis ( projected on x,y,z depending on AXIS[face][] ) + for (int axis = 0; axis < 2; axis++) { + short caxis = AXIS[face][axis]; + if (caxis > 0) { f = 1; - } - else if (caxis < 0) - { + } else if (caxis < 0) { + // wait wait in AXIS there is no negative value ... + // so we never enter here, what was the purpose ? might be history. f = -1; caxis = (short) -caxis; } + // what if caxis == 0 ? f is kept as its previous value ? + // which is 1 in facts due to AXIS[..][0] > 0 + + // uaxis keep track of used axes for this face to find last missing axis uaxis |= caxis; - if (caxis == X) - { + if (caxis == X) { caxis = 0; - } - else if (caxis == Y) - { + } else if (caxis == Y) { caxis = 1; - } - else - { + } else { caxis = 2; } - // if ( f == 0 ) - // { - // System.out.println("ERROR"); - // } - // System.out.println("i " + i + " p " + p + " a " + caxis + - // " , " + BASE[i%4][p][axis] ); - triangle[i][p][caxis] = translate[caxis] + BASE[i % 4][p][axis] * f * factor[caxis]; + triangle[facet][p][caxis] = translate[caxis] + BASE[facet % 4][p][axis] * f * factor[caxis]; } - if ((uaxis & X) == 0) - { + // last remaining axis + if ((uaxis & X) == 0) { + // no X was used => X uaxis = 0; - } - else if ((uaxis & Y) == 0) - { + } else if ((uaxis & Y) == 0) { + // X was used byt not Y => Y uaxis = 1; - } - else - { + } else { + // default X, and Y were used => Z uaxis = 2; } - triangle[i][p][uaxis] = translate[uaxis] + AXIS[i / 2][2] * factor[uaxis]; + triangle[facet][p][uaxis] = translate[uaxis] + AXIS[facet / 2][2] * factor[uaxis]; } } } - public int[][][] translate(int dx, int dy, int dz) - { - int[] translate = { dx, dy, dz }; + public int[][][] translate(int dx, int dy, int dz) { + int[] translate = {dx, dy, dz}; int t[][][] = new int[12][3][3]; - for (int i = 0; i < 12; i++) - { + for (int facet = 0; facet < 12; facet ++) { // point in a triangle - for (int p = 0; p < 3; p++) - { - for (int axis = 0; axis < 3; axis++) - { - t[i][p][axis] = translate[axis] + triangle[i][p][axis]; + for (int p = 0; p < 3; p++) { + for (int axis = 0; axis < 3; axis++) { + t[facet][p][axis] = translate[axis] + triangle[facet][p][axis]; } } } @@ -127,22 +147,27 @@ public class Wall3d } /** - write triangles as stl text + * write triangles as stl text **/ - public String toString() - { + public String toString() { String s = ""; - for (int t = 0; t < 12; t++) - { - s += "facet normal 0 0 0\nouter loop\n"; - for (int p = 0; p < 3; p++) - { + for (int facet = 0; facet < 12; facet++) { + int face = facet / 2; + // most software don't care normal but just rely on vertex order ( right hand ). + String normal = normalstr[face]; + s += "facet normal " + normal + "\nouter loop\n"; + boolean reverse = reverse_vertex[face]; + for (int p = 0; p < 3; p++) { s += "vertex"; - for (int a = 0; a < 3; a++) + // p that accept right hand + int rhp = p; + if ( reverse ) { - // s+=" t "+ t + " p " + p + " a " + a + "=" + - // triangle[t][p][a]; - s += " " + triangle[t][p][a]; + // reverse 2 and 1 ; 0 => 0, 1 => 2 , 2 => 1 + rhp = ( p * 5 ) % 3; + } + for (int a = 0; a < 3; a++) { + s += " " + triangle[facet][rhp][a]; } s += "\n"; } @@ -151,94 +176,4 @@ public class Wall3d return s; } - public static void prepare() - { - System.out.println(South.toString()); - System.out.println(East.toString()); - System.out.println(North.toString()); - System.out.println(West.toString()); - } - - public static void streamWallsOut(String name, LabyModel provider, OutputStream stream) throws IOException - { - int width = provider.getWidth(); - int height = provider.getHeight(); - int xl = 10; - int yl = 10; - int zl = 10; - - // WARNING DOWN - UP reversed ( in 2D Y is oriented to lower, in 3D it - // is to upper ). - stream.write(("solid " + name + "\n").getBytes()); - for (int x = 0; x < width; x++) - { - short walls = provider.getWalls(x, 0); - if ((walls & Brick.UP) != 0) - { - stream.write(new Wall3d(South, x * xl, 0, 0).toString().getBytes()); - } - if ((walls & Brick.LEFT) != 0) - { - stream.write(new Wall3d(West, x * xl, 0, 0).toString().getBytes()); - } - } - - for (int y = 0; y < height; y++) - { - short walls = provider.getWalls(0, y); - if ((walls & Brick.LEFT) != 0) - { - stream.write(new Wall3d(West, 0, y * yl, 0).toString().getBytes()); - } - for (int x = 0; x < width; x++) - { - // south and east - walls = provider.getWalls(x, y); - if ((walls & Brick.DOWN) != 0) - { - stream.write(new Wall3d(North, x * xl, y * yl, 0).toString().getBytes()); - } - if ((walls & Brick.RIGHT) != 0) - { - stream.write(new Wall3d(East, x * xl, y * yl, 0).toString().getBytes()); - } - short path = provider.getPath(x,y); - if ( ( path & LabyModel.SOLVED ) == LabyModel.SOLVED ) - // if ( (walls & ( Brick.GOAL | Brick.ENTRY ) ) == 0 ) - { - // if ( ( (x+y) % 2) == 0 ) - - stream.write(new Wall3d(LowGround, x * xl, y * yl, 0).toString().getBytes()); - } - else - { - stream.write(new Wall3d(HighGround, x * xl, y * yl, 0).toString().getBytes()); - } - } - } - - stream.write("endsolid wall\n\n".getBytes()); - } - - public static void main(String args[]) - { - - Scanner console = new Scanner(System.in); - int xl = console.nextInt(); - int yl = console.nextInt(); - int zl = console.nextInt(); - - int dx = console.nextInt(); - int dy = console.nextInt(); - int dz = console.nextInt(); - - String s = "solid wall\n"; - - Wall3d instance = new Wall3d(xl, yl, zl, dx, dy, dz); - - prepare(); - - s += "endsolid wall\n\n"; - System.out.println(s); - } } diff --git a/java/org/artisanlogiciel/games/stl/Wall3dStream.java b/java/org/artisanlogiciel/games/stl/Wall3dStream.java new file mode 100644 index 0000000..f2ed4d6 --- /dev/null +++ b/java/org/artisanlogiciel/games/stl/Wall3dStream.java @@ -0,0 +1,93 @@ +package org.artisanlogiciel.games.stl; + +import org.artisanlogiciel.games.Brick; +import org.artisanlogiciel.games.LabyModel; + +import java.io.IOException; +import java.io.OutputStream; + +public class Wall3dStream +{ + + String name; + LabyModel provider; + OutputStream stream; + + Maze3dParams params; + + /** + * + * @param name + * @param provider + * @param stream + * @param params + */ + public Wall3dStream(String name, LabyModel provider, OutputStream stream, Maze3dParams params) + { + this.name = name; + this.provider = provider; + this.stream = stream; + this.params = params; + } + + private void writeWall3D(Wall3d wall3d) + throws IOException + { + stream.write(wall3d.toString().getBytes()); + } + + public void stream() throws IOException { + int width = provider.getWidth(); + int height = provider.getHeight(); + + int xl = params.getXl(); + int yl = params.getYl(); + int zl = params.getZl(); + boolean reverse = params.isReverse(); + + // WARNING DOWN - UP reversed ( in 2D Y is oriented to lower, in 3D it + // is to upper ). + stream.write(("solid " + name + "\n").getBytes()); + for (int x = 0; x < width; x++) { + short walls = provider.getWalls(x, 0); + if ((walls & Brick.UP) != 0) { + writeWall3D(new Wall3d(params.getSouth(), x * xl, 0, 0)); + } + if ((walls & Brick.LEFT) != 0) { + writeWall3D(new Wall3d(params.getWest(), x * xl, 0, 0)); + } + } + + for (int y = 0; y < height; y++) { + short walls = provider.getWalls(0, y); + if ((walls & Brick.LEFT) != 0) { + writeWall3D(new Wall3d(params.getWest(), 0, y * yl, 0)); + } + for (int x = 0; x < width; x++) { + // south and east + walls = provider.getWalls(x, y); + if ((walls & Brick.DOWN) != 0) { + writeWall3D(new Wall3d(params.getNorth(), x * xl, y * yl, 0)); + } + if ((walls & Brick.RIGHT) != 0) { + writeWall3D(new Wall3d(params.getEast(), x * xl, y * yl, 0)); + } + short path = provider.getPath(x, y); + + // where resolved path is leaked to stl model. + Wall3d ground = reverse ? params.getLowGround() : params.getHighGround(); + if ((path & LabyModel.SOLVED) == LabyModel.SOLVED) + // if ( (walls & ( Brick.GOAL | Brick.ENTRY ) ) == 0 ) + { + // if ( ( (x+y) % 2) == 0 ) + ground = reverse ? params.getHighGround() : params.getLowGround(); + } + writeWall3D(new Wall3d(ground, x * xl, y * yl, 0)); + } + } + + stream.write("endsolid wall\n\n".getBytes()); + + stream.flush(); + } +} diff --git a/lang/LabelsBundle.properties b/lang/LabelsBundle.properties index c1691a5..de03b04 100644 --- a/lang/LabelsBundle.properties +++ b/lang/LabelsBundle.properties @@ -9,4 +9,6 @@ resolve = Resolve create = Create save = Save quit = Quit -title = A Maz ing \ No newline at end of file +title = A Maz ing +seed = seed +load = Load \ No newline at end of file diff --git a/lang/LabelsBundle_fr.properties b/lang/LabelsBundle_fr.properties index a85b857..cd279bf 100644 --- a/lang/LabelsBundle_fr.properties +++ b/lang/LabelsBundle_fr.properties @@ -9,4 +9,6 @@ resolve = Résoudre create = Créer save = Sauver quit = Quitter -title = La Bireinte \ No newline at end of file +title = La Bireinte +seed = graine +load = Charger \ No newline at end of file diff --git a/mybuild.xml b/mybuild.xml index 973e799..d99e9ad 100644 --- a/mybuild.xml +++ b/mybuild.xml @@ -10,10 +10,10 @@ - + - - + + @@ -26,6 +26,7 @@ + @@ -37,8 +38,8 @@ - - + + @@ -50,5 +51,4 @@ - diff --git a/project_params b/project_params index 182ff1a..8041926 100644 --- a/project_params +++ b/project_params @@ -2,6 +2,6 @@ project_name=artloglaby project_default=dist project_basedir=$(pwd) project_mainpackage=org.artisanlogiciel.games -project_mainclass=$project_mainpackage.Display -project_version=1.0 +project_mainclass=$project_mainpackage.maze.gui.Display +project_version=1.1 default_args='lab/lab30x30.raw' diff --git a/renamelabindir.sh b/renamelabindir.sh new file mode 100755 index 0000000..5e8bc1d --- /dev/null +++ b/renamelabindir.sh @@ -0,0 +1,42 @@ +#!/bin/bash + +labdir=$1 +newprefix=$2 + +if [[ -z $labdir ]] +then + echo "[ERROR] expect a lab directory" + echo "will rename all labxxx.* within this directory to directory name labdirxxx.*" + exit 1 +fi + +if [[ -z $newprefix ]] +then + newprefix=$labdir +fi + +{ + echo "[INFO] changing lab prefix to $newprefix" + + declare -i index=1 + + pushd $labdir + for f in $(find . -regextype egrep -regex "./lab[0-9]+x[0-9]+\..*") + do + if [[ $f =~ ./lab([0-9]+)x([0-9]+)\.(.+) ]] + then + width=${BASH_REMATCH[1]} + height=${BASH_REMATCH[2]} + extension=${BASH_REMATCH[3]} + newname="${newprefix}_${index}_${width}x${height}.$extension" + if [[ ! -e ./$newname ]] + then + mv $f ./$newname + else + echo "[ERROR] skip $f -> ./$newname since new name already used" >&2 + fi + fi + index=index+1 + done + popd +} diff --git a/scripts/laby.sh b/scripts/laby.sh new file mode 100755 index 0000000..9255fab --- /dev/null +++ b/scripts/laby.sh @@ -0,0 +1,37 @@ +#!/bin/bash + +findmainjar() +{ + for MJ in $(find . -name "*.jar") + do + echo "$MJ" + if [[ $MJ =~ artloglaby-(.*)\.jar ]] + then + VERSION=${BASH_REMATCH[1]} + echo "found $MJ version $VERSION" + MAINJAR=$MJ + fi + done + +} + +echo "Ce script a été conçu pour une distribution linux" +echo "This script was created for linux" + +findmainjar + +if [[ -z $MAINJAR ]] +then + echo "[ERREUR] aucun jar artloglaby-xxx.jar dans ce répertoire" + exit 1 +fi + +if which java +then + java -jar $MAINJAR +else + echo "Aucun java/jre de trouvé" + echo + echo "Pour ubuntu :" + echo "sudo apt install openjdk-8-jre" +fi diff --git a/specificdoit.sh b/specificdoit.sh index 3fca2ef..6966d04 100644 --- a/specificdoit.sh +++ b/specificdoit.sh @@ -1,5 +1,19 @@ #!/bin/bash +function findmainjar() +{ + for MJ in $(find . -name "*.jar") + do + echo "$MJ" + if [[ $MJ =~ (artloglaby-.*\.jar) ]] + then + MAINJAR=${BASH_REMATCH[1]} + echo "found $MJ jar $MAINJAR" + fi + done + +} + specific_run() { local action=$1 @@ -33,9 +47,25 @@ create_zip_package() mkdir $dest # will be used at run time to save mazes mkdir $dest/lab + mkdir $dest/libs cp scripts/laby.sh $dest + + MAINJAR="" + pushd dist/lib + findmainjar + popd + + if [[ -n $MAINJAR ]] + then + printf '%b\n' "java -jar $MAINJAR" + printf '%b\r\n' "java -jar $MAINJAR" >$dest/laby.bat + else + echo "[ERROR] can't find main jar" + fi + + cp LISEZMOI $dest - cp libs/* $dest + cp libs/* $dest/libs/ cp dist/lib/* $dest zip -r $zip_package $dest diff --git a/toolbox.param b/toolbox.param new file mode 100644 index 0000000..5b6f920 --- /dev/null +++ b/toolbox.param @@ -0,0 +1 @@ +ARTLOG_TOOLBOX=../artlog_toolbox