From 850744326a5969064a3800f788b09440883b4e86 Mon Sep 17 00:00:00 2001 From: philippe lhardy Date: Wed, 24 Dec 2014 17:28:23 +0100 Subject: [PATCH] initial commit for labryinth project on git_hub. Signed-off-by: philippe lhardy --- java/Makefile | 48 ++ java/README | 21 + java/TODO | 15 + java/generate_newclass.sh | 74 ++ java/org/artisanlogiciel/games/Brick.java | 122 +++ java/org/artisanlogiciel/games/Display.java | 542 ++++++++++++ java/org/artisanlogiciel/games/LabyMap.java | 106 +++ java/org/artisanlogiciel/games/LabyModel.java | 808 ++++++++++++++++++ java/org/artisanlogiciel/games/Main.java | 59 ++ .../games/MazeCreationListener.java | 9 + .../games/MazeResolutionListener.java | 17 + java/org/artisanlogiciel/games/Position.java | 38 + java/org/artisanlogiciel/games/Wall3d.java | 220 +++++ .../artisanlogiciel/games/WallsProvider.java | 27 + 14 files changed, 2106 insertions(+) create mode 100644 java/Makefile create mode 100644 java/README create mode 100644 java/TODO create mode 100755 java/generate_newclass.sh create mode 100644 java/org/artisanlogiciel/games/Brick.java create mode 100644 java/org/artisanlogiciel/games/Display.java create mode 100644 java/org/artisanlogiciel/games/LabyMap.java create mode 100644 java/org/artisanlogiciel/games/LabyModel.java create mode 100644 java/org/artisanlogiciel/games/Main.java create mode 100644 java/org/artisanlogiciel/games/MazeCreationListener.java create mode 100644 java/org/artisanlogiciel/games/MazeResolutionListener.java create mode 100644 java/org/artisanlogiciel/games/Position.java create mode 100644 java/org/artisanlogiciel/games/Wall3d.java create mode 100644 java/org/artisanlogiciel/games/WallsProvider.java diff --git a/java/Makefile b/java/Makefile new file mode 100644 index 0000000..7f05adf --- /dev/null +++ b/java/Makefile @@ -0,0 +1,48 @@ +PACKAGE=org.artisanlogiciel.games +PACKAGE_DIR=$(subst .,/,$(PACKAGE)) +OUT=out + +$(OUT): + mkdir -p $(OUT) + +clean: + find $(OUT) -name "*.class" -type f -print0|xargs -0 rm + +test: + javac -d $(OUT) $(PACKAGE_DIR)/LabyModel.java + javac -d $(OUT) $(PACKAGE_DIR)/Main.java + java -cp $(OUT) $(PACKAGE).Main + + +run/%: $(OUT) + javac -d $(OUT) $(PACKAGE_DIR)/$(subst run/,,$@).java + java -cp $(OUT) $(PACKAGE)/$(subst run/,,$@) + +display: run/Display + + +display/%: $(OUT) + javac -d $(OUT) $(PACKAGE_DIR)/Display.java + java -cp $(OUT) $(PACKAGE).Display $(subst display/,,$@) + +compile/%: + javac -d $(OUT) $(PACKAGE_DIR)/$(subst compile/,,$@).java + +$(PACKAGE_DIR)/%.java: + ./generate_newclass.sh $(subst .java,,$(subst $(PACKAGE_DIR)/,,$@)) + +work/%: $(PACKAGE_DIR)/$(subst work/,,%).java + emacs $< + +work: work/LabyModel + +save: + git citool + +.PHONY: clean test work display work/% run/% save compile/% + +# tried to avoid intermediate file removal : does not work +# .SECONDARY: $(PACKAGE_DIR)/%.java + +# this does work : once precious intermediate file is not removed. +.PRECIOUS: $(PACKAGE_DIR)/%.java diff --git a/java/README b/java/README new file mode 100644 index 0000000..5cdbd15 --- /dev/null +++ b/java/README @@ -0,0 +1,21 @@ +A Maze generator that keep all reverse paths to solutions. + +See Makefile + +#clean +make clean + +#console +make test + +#gui +make display + +#code emacs : work/ClassName +make work/Display + +#run a class +make run/Display + +#save work with git (ie git citool ) +make save \ No newline at end of file diff --git a/java/TODO b/java/TODO new file mode 100644 index 0000000..f9c82d4 --- /dev/null +++ b/java/TODO @@ -0,0 +1,15 @@ +By pref + +- rendering +GUI +* WALK it... +* regenerate it + +CONSOLE : +* provide interactive ( view a part of maze ) + +- model +* saving in stream ( better than in toString() that basically fails... ) + +- format : +3D format ( for 3Dprinting ) \ No newline at end of file diff --git a/java/generate_newclass.sh b/java/generate_newclass.sh new file mode 100755 index 0000000..51512c8 --- /dev/null +++ b/java/generate_newclass.sh @@ -0,0 +1,74 @@ +#!/bin/bash + +PACKAGE=org.artisanlogiciel.games + +while [[ $# > 0 ]] +do + case $1 in + package=*) + PACKAGE=${1/package=/} + ;; + *) + CLASS=${1} + ;; + esac + shift 1 +done + +if [[ -z $CLASS ]] +then + echo "[ERROR] Missing class" >&2 + exit 1 +fi + +PACKAGE_DIR=${PACKAGE//\./\/} + +if [[ ! -d $PACKAGE_DIR ]] +then + echo "Missing $PACKAGE_DIR" >&2 + exit 1 +fi + +JAVA_FILE=$PACKAGE_DIR/$CLASS.java +if [[ -e $JAVA_FILE ]] +then + echo "[ERROR] $JAVA_FILE already exists" >&2 + exit 1 +else + +cat <$JAVA_FILE +package $PACKAGE; + +/** + $CLASS was autogenerated by $0 + **/ +public class $CLASS +{ + Object param; + + $CLASS(Object param) + { + this.param=param; + } + + void method() + { + System.out.println("Method of $PACKAGE.$CLASS"); + } + + public static void main(String args[]) { + if ( args.length > 1 ) + { + $CLASS instance=new $CLASS(args[1]); + instance.method(); + } + else + { + System.err.println("Missing argument for $CLASS"); + } + } +} +EOF +fi + +echo "$JAVA_FILE generated" diff --git a/java/org/artisanlogiciel/games/Brick.java b/java/org/artisanlogiciel/games/Brick.java new file mode 100644 index 0000000..dc4988b --- /dev/null +++ b/java/org/artisanlogiciel/games/Brick.java @@ -0,0 +1,122 @@ +package org.artisanlogiciel.games; + +/* + 2x2 Tile to represent a labyrinth position + +ab +cd + +a is 'H' or '.' or ' ' +bcd is 'H' or ' ' + +*/ +public class Brick { + + public final static short LEFT=1; + public final static short DOWN=2; + public final static short RIGHT=4; + public final static short UP=8; + public final static short ENTRY=16; + public final static short GOAL=32; + + char a; + char b; + char c; + char d; + short walls; // according to LabyModel.getWalls(); + + public Brick(char center, char right, char down, char downright,short walls) + { + a=center; + b=right; + c=down; + d=downright; + this.walls=walls; + } + + public Brick(String up, String low, short walls) + { + a = up.charAt(0); + b = up.charAt(1); + c = low.charAt(0); + d = low.charAt(1); + this.walls=walls; + } + + private boolean check() + { + // Not Yet Implemented + return false; + } + + public String getUpString() + { + return "" + a + b; + } + + public String getLowString() + { + return "" + c + d; + } + + public static String getDirLine() + { + char dir[] = new char[16]; + String s=""; + + /* + dir[LEFT | DOWN | RIGHT | UP]='O'; + dir[LEFT | DOWN | RIGHT]='U'; + dir[LEFT | UP | RIGHT]='M'; + dir[LEFT | UP | DOWN]='['; + dir[RIGHT | UP | DOWN]=']'; + dir[UP | DOWN]='='; + dir[LEFT | RIGHT]='|'; + dir[RIGHT | DOWN]='J'; + dir[LEFT | DOWN]='L'; + dir [LEFT | UP]='T'; + dir[UP | RIGHT]='7'; + dir[LEFT] = '!'; + dir[RIGHT] ='|'; + dir[DOWN]= '_'; + dir[UP]= '¨'; + dir[0]=' '; + */ + + dir[LEFT | DOWN | RIGHT | UP]='O'; + dir[LEFT | DOWN | RIGHT]='U'; + dir[LEFT | UP | RIGHT]='M'; + dir[LEFT | UP | DOWN]='['; + dir[RIGHT | UP | DOWN]=']'; + dir[UP | DOWN]='='; + dir[LEFT | RIGHT]=226; + dir[RIGHT | DOWN]='J'; + dir[LEFT | DOWN]='L'; + dir [LEFT | UP]=169; + dir[UP | RIGHT]=170; + dir[LEFT] = 173; + dir[RIGHT] ='|'; + dir[DOWN]= '_'; + dir[UP]= '¨'; + dir[0]=' '; + + for (int i=0;i< 16; i++) + { + s=s+dir[i]; + } + + return s; + + } + + public char getChar() + { + // return getDirLine().charAt(walls & 0xFFF0); + return getDirLine().charAt(walls); + } + + public short getWalls() + { + return walls; + } +} diff --git a/java/org/artisanlogiciel/games/Display.java b/java/org/artisanlogiciel/games/Display.java new file mode 100644 index 0000000..44606b4 --- /dev/null +++ b/java/org/artisanlogiciel/games/Display.java @@ -0,0 +1,542 @@ +package org.artisanlogiciel.games; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Container; +import java.awt.Rectangle; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.LinkedList; +import java.util.Scanner; + +import javax.swing.Action; +import javax.swing.AbstractAction; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JSlider; +import javax.swing.KeyStroke; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; + +/** + Display + **/ +public class Display extends JFrame + +{ + Object param; + JPanel pane = new JPanel(); + MazeComponent maze; + MazeControler controler; + LabyModel model; + + Display(LabyModel model, MazeComponent comp) + { + super("Maze display"); + maze=comp; + this.param=param; + this.model=model; + Container con = this.getContentPane(); + con.add(pane); + con.add(new JScrollPane(comp), BorderLayout.CENTER); + controler = new MazeControler(); + con.add(controler,BorderLayout.SOUTH); + } + + void resolve() + { + model.resolve(model.getWidth()-1,model.getHeight()-1,maze); + } + + void goNorth() + { + maze.goNorth(); + } + + void goSouth() + { + maze.goSouth(); + } + + void goEast() + { + maze.goEast(); + } + + void goWest() + { + maze.goWest(); + } + + void setWallSize(int size) + { + maze.setWallSize(size); + } + + private class MazeControler extends JPanel + { + public MazeControler() + { + JPanel controlPanel = new JPanel(); + JButton button=new JButton("Resolve"); + button.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) + { + // + System.out.println("Resolving"); + resolve(); + } + }); + JButton north = new JButton("North"); + Action goNorth = new AbstractAction() + { + public void actionPerformed(ActionEvent evt) + { + // + System.out.println("go North"); + goNorth(); + } + }; + KeyStroke kup=KeyStroke.getKeyStroke("UP"); + getInputMap().put(kup,"goNorth"); + getActionMap().put("goNorth",goNorth); + controlPanel.getInputMap().put(kup,"goNorth"); + controlPanel.getActionMap().put("goNorth",goNorth); + north.getActionMap().put("goNorth",goNorth); + JButton east = new JButton("East"); + Action goEast = new AbstractAction() { + public void actionPerformed(ActionEvent evt) + { + // + System.out.println("go East"); + goEast(); + } + }; + east.addActionListener(goEast); + KeyStroke kright=KeyStroke.getKeyStroke("RIGHT"); + getInputMap().put(kright,"goEast"); + getActionMap().put("goEast",goEast); + controlPanel.getInputMap().put(kright,"goEast"); + controlPanel.getActionMap().put("goEast",goEast); + JButton west = new JButton("West"); + Action goWest = new AbstractAction() { + public void actionPerformed(ActionEvent evt) + { + // + System.out.println("go West"); + goWest(); + } + }; + west.addActionListener(goWest); + KeyStroke kwest=KeyStroke.getKeyStroke("LEFT"); + getInputMap().put(kwest,"goWest"); + getActionMap().put("goWest",goWest); + controlPanel.getInputMap().put(kwest,"goWest"); + controlPanel.getActionMap().put("goWest",goWest); + JButton south = new JButton("South"); + Action goSouth = new AbstractAction() { + public void actionPerformed(ActionEvent evt) + { + // + System.out.println("go South"); + goSouth(); + } + }; + south.addActionListener(goSouth); + KeyStroke kdown=KeyStroke.getKeyStroke("DOWN"); + controlPanel.getInputMap().put(kdown,"goSouth"); + controlPanel.getActionMap().put("goSouth",goSouth); + getInputMap().put(KeyStroke.getKeyStroke("DOWN"),"goSouth"); + getActionMap().put("goSouth",goSouth); + controlPanel.getInputMap().put(KeyStroke.getKeyStroke("DOWN"),"goSouth"); + controlPanel.getActionMap().put("goSouth",goSouth); + + controlPanel.add(button,BorderLayout.NORTH); + controlPanel.add(north,BorderLayout.NORTH); + controlPanel.add(east,BorderLayout.EAST); + controlPanel.add(west,BorderLayout.WEST); + controlPanel.add(south,BorderLayout.SOUTH); + add(controlPanel,BorderLayout.NORTH); + final JSlider slider=new JSlider(2,40); + slider.addChangeListener(new ChangeListener() { + public void stateChanged(ChangeEvent e) + { + setWallSize(slider.getValue()); + } + }); + add(slider,BorderLayout.SOUTH); + } + + } + + private static class MazeComponent extends JComponent + implements MazeCreationListener, + MazeResolutionListener + { + WallsProvider map; + int width = 10; + int height = 10; + int offsetX = 5; + int offsetY = 5; + Position current = null; + LinkedList solvedPath = null; + final Object lockChange = new Object(); + // current postion of human resolution + int sX = 0; + int sY = 0; + // goal exit + int gX = -1; + int gY = -1; + + void checkExit() + { + if ((sX == gX) && (sY == gY)) + { + System.out.println("Exit found by human !"); + } + } + void goNorth() + { + resetResolution(); + if ( (map.getWalls(sX,sY) & Brick.UP) == 0 ) + { + sY = sY -1; + // should redraw ... + invalidate(); + repaint(); + + checkExit(); + } + + } + + void goSouth() + { + resetResolution(); + if ( (map.getWalls(sX,sY) & Brick.DOWN) == 0 ) + { + sY =sY + 1; + // should redraw ... + invalidate(); + repaint(); + + checkExit(); + } + + } + + void goEast() + { + resetResolution(); + if ( (map.getWalls(sX,sY) & Brick.RIGHT) == 0 ) + { + sX = sX + 1; + // should redraw ... + invalidate(); + repaint(); + + checkExit(); + } + } + + void goWest() + { + resetResolution(); + if ( (map.getWalls(sX,sY) & Brick.LEFT) == 0 ) + { + sX = sX - 1; + // should redraw ... + invalidate(); + repaint(); + + checkExit(); + } + + } + + public MazeComponent(WallsProvider map, int width, int height, int offsetX, int offsetY) + { + super(); + this.map=map; + this.width=width; + this.height=height; + this.offsetX=offsetX; + this.offsetY=offsetY; + setPreferredSize(new Dimension(1+2*offsetX+map.getWidth()*width, + 1+2*offsetY+map.getHeight()*height)); + gX = map.getWidth() -1; + gY = map.getHeight() -1; + } + + public void setWallSize(int size) + { + + width=size; + height=size; + // should redraw ... + invalidate(); + repaint(); + } + + public void changed(Position cl, Position cr, WallsProvider map) + { + // should redraw ... + invalidate(); + repaint(); + Object waiter = new Object(); + synchronized(waiter) + { + try { + waiter.wait(10); + } + catch( InterruptedException e) + { + System.err.println("Interrupted !"); + } + } + } + + private void drawWalls(Graphics g, int x, int y, short walls, boolean full) + { + if ( (full) && (( walls & Brick.UP ) == Brick.UP )) g.drawLine(x, y, x+width, y); + if ( ( walls & Brick.DOWN ) == Brick.DOWN ) g.drawLine(x, y+height, x+width, y+height); + if ( ( walls & Brick.RIGHT ) == Brick.RIGHT ) g.drawLine(x+width, y, x+width, y+height); + if ( (full) && (( walls & Brick.LEFT ) == Brick.LEFT )) g.drawLine(x, y, x, y+height); + } + + @Override + protected void paintComponent(Graphics g) { + super.paintComponent(g); + int x = 0; + int y = 0; + short walls=0; + + // try to display only visible part... + // should compute pX, pY, mX, mY based on clip... + // Shape s=g.getClip(); + Rectangle r= g.getClipBounds(); + + int mX = (int) ( (double) r.getWidth() / width); + int mY = (int) ( (double) r.getHeight() / height); + int pX = (int) ( (double) (r.getX() - offsetX) / width); + int pY = (int) ( (double) (r.getY() - offsetY) / height); + + if ( pX < 0) pX = 0; + if ( pY < 0) pY = 0; + if ( pX >= map.getWidth()) return; + if ( pY >= map.getHeight()) return; + + mX=mX+pX; + mY=mY+pY; + + if ( mX > map.getWidth() ) + { + mX = map.getWidth(); + } + if ( mY > map.getHeight()) + { + mY = map.getHeight(); + } + + if ((sX == gX ) && (sY == gY )) + { + g.setColor(Color.red); + } + else + { + g.setColor(Color.blue); + } + drawDot(g,new Position(sX,sY),pX,pY,mX,mY); + + synchronized(lockChange) + { + g.setColor(Color.red); + if ( current != null) + { + drawDot(g,current,pX,pY,mX,mY); + } + if (solvedPath != null) + { + for ( Position resolved : solvedPath ) + { + drawDot(g,resolved,pX,pY,mX,mY); + } + } + } + + g.setColor(Color.black); + for ( ; pY < mY ; pY ++) + { + for ( pX = 0; pX < mX ; pX ++) + { + x=offsetX+pX*width; + y=offsetY+pY*height; + walls = map.getWalls(pX,pY); + drawWalls(g,x,y,walls,(pX == 0) || (pY==0)); + + } + } + } + + private void drawDot(Graphics g,Position dot, int pX, int pY, int mX, int mY) + { + int radius = ( height > width ) ? width : height; + int a = radius / 4; + if (( dot.getX() >= pX ) && ( dot.getY() >= pY ) && ( dot.getX() < mX ) && ( dot.getY() < mY ) ) + { + int x=offsetX+dot.getX()*width; + int y=offsetY+dot.getY()*height; + g.drawOval(x+1,y+1,radius-a,radius-a); + //g.drawLine(x+a,y+a,x+width-a,y+height-a); + //g.drawLine(x+a,y+height-a,x+width-a,y+a); + + } + else + { + int x=offsetX+pX*width; + int y=offsetY+pY*height; + g.drawLine(x+1,y+1,x+width-1,y+height-1); + } + } + + public boolean notifySearch(Position pPosition) + { + synchronized(lockChange) + { + current = pPosition; + } + // should redraw ... + invalidate(); + repaint(); + return true; + + } + + public void notifySearchError(String error) + { + System.err.println(error); + } + + public void notifyCompletion(LinkedList solvedPath) + { + LinkedList newPath = (LinkedList) solvedPath.clone(); + System.out.println("resolution completed"); + synchronized(lockChange) + { + this.solvedPath = newPath; + } + // should redraw ... + invalidate(); + repaint(); + } + + public void resetResolution() + { + solvedPath = null; + } + } + + public static void main(String pArgs[]) + { + LabyModel model = null; + int W = 600; + int H = 400; + + if ( pArgs.length > 0 ) + { + try { + model = new LabyModel("raw",new FileInputStream(pArgs[0])); + } + catch (IOException io) + { + io.printStackTrace(System.err); + } + + int w = W / model.getWidth(); + int h = H / model.getHeight(); + if ( w < 5 ) w = 5; + if ( h < 5 ) h = 5; + MazeComponent comp = new MazeComponent(model,w,h,3,3); + Display display=new Display(model, comp); + model.setMazeListener(comp); + display.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + display.setBounds(W,H,W,H); + display.setVisible(true); + } + else + { + System.out.println("enter width height and maxdepth"); + Scanner console = new Scanner(System.in); + int width = console.nextInt(); + int height = console.nextInt(); + int maxdepth = console.nextInt(); + model = new LabyModel(width, height,maxdepth, new java.util.Random()); + int w = W / width; + int h = H / height; + if ( w < 5 ) w = 5; + if ( h < 5 ) h = 5; + MazeComponent comp = new MazeComponent(model,w,h,3,3); + Display display=new Display(model,comp); + model.setMazeListener(comp); + display.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + display.setBounds(W,H,W,H); + display.setVisible(true); + + model.generateWithEntry(0, 0); + LinkedList exits = new LinkedList(); + model.addEntryOrExit(-1,0); + model.addEntryOrExit(width,height -1); + System.out.println("Generation completed"); + String name="lab" + width + "x" + height; + File outfile = new File(new File("lab"), name + ".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(new File("lab"),name + ".stl"); + if ( ! outfile.exists()) + { + System.out.println( "Saving to " + outfile + " ..."); + try { + FileOutputStream out = new FileOutputStream(outfile); + Wall3d.streamWallsOut(name,(WallsProvider) model,out); + out.flush(); + out.close(); + System.out.println( "... Done."); + } + catch (IOException io) + { + io.printStackTrace(System.err); + } + } + else { + System.out.println( "" + outfile + " already exists"); + } + } + + } +} diff --git a/java/org/artisanlogiciel/games/LabyMap.java b/java/org/artisanlogiciel/games/LabyMap.java new file mode 100644 index 0000000..8cea2a3 --- /dev/null +++ b/java/org/artisanlogiciel/games/LabyMap.java @@ -0,0 +1,106 @@ +package org.artisanlogiciel.games; + +import java.util.LinkedList; + +class LabyMap +implements WallsProvider +{ + + Brick[][] tileMap; + LinkedList exits = new LinkedList(); + + public LabyMap(Brick[][] tileMap, LinkedList exits) + { + this.tileMap = tileMap; + this.exits = exits; + } + + Brick getAt(int x, int y) + { + return tileMap[x][y]; + } + + public short getWalls(int x, int y) + { + return getAt(x,y).getWalls(); + } + + public int getWidth() + { + return tileMap[0].length; + } + + public int getHeight() + { + return tileMap.length; + } + + public String toString() + { + int entryX = -1; + int entryY = -1; + for ( Position exit: exits ) + { + if ( exit.getX() == -1 ) + { + entryY = exit.getY(); + } + if ( exit.getY() == -1 ) + { + entryX= exit.getX(); + } + } + String laby="H"; + for ( int x=0; x < tileMap.length; x ++) { + if ( entryX == x ) { laby += " H" ; } + else { laby+="HH";} + } + laby+="\n"; + for ( int y=0; y < tileMap[0].length; y++) + { + if ( entryY == y ) { laby += ">";} + else { laby += "H"; } + for ( int x=0; x < tileMap.length; x ++) + { + laby+= tileMap[x][y].getUpString(); + } + laby+="\n"; + laby += "H"; + for ( int x=0; x < tileMap.length; x ++) + { + laby+= tileMap[x][y].getLowString(); + } + laby+="\n"; + } + return laby; + } + + + public String toShortString() + { + int entryX = -1; + int entryY = -1; + for ( Position exit: exits ) + { + if ( exit.getX() == -1 ) + { + entryY = exit.getY(); + } + if ( exit.getY() == -1 ) + { + entryX= exit.getX(); + } + } + String laby=""; + for ( int y=0; y < tileMap[0].length; y++) + { + for ( int x=0; x < tileMap.length; x ++) + { + laby+= "" + tileMap[x][y].getChar(); + } + laby+="\n"; + } + return laby; + } + +} diff --git a/java/org/artisanlogiciel/games/LabyModel.java b/java/org/artisanlogiciel/games/LabyModel.java new file mode 100644 index 0000000..f0b4cfc --- /dev/null +++ b/java/org/artisanlogiciel/games/LabyModel.java @@ -0,0 +1,808 @@ +package org.artisanlogiciel.games; + +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 from here to go do deeper path. + a node is tagged OPEN and contained in openList if all moves from its position have not been resolved + a node is tagged CLOSED when fully processed + **/ +public class LabyModel +implements WallsProvider +{ + + /** WARNING don't change those values, they are used as it is for optimisation */ + private final static short FLAGLENGTH=7; + private final static short CLEAR=0; // mandatory 0 since array creation is initialized with 0. + private final static short HORIZONTAL=1; + private final static short VERTICAL=2; + private final static short DIRECTION=4; // could we get rid of that to free one bit for other purpose ? + private final static short POSITIVE=8; + private final static short NEGATIVE=16; + private final static short OPEN=32; // can be reused once generation is completed + private final static short CLOSED=64; // can be reused once generation is completed + private final static short LEFT=Brick.LEFT< 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 ), + + MazeCreationListener listener = null; + + + public LabyModel(int width, int height,int maxdepth, Random random){ + this.width=width; + this.height=height; + this.maxdepth=maxdepth; + this.random = random; + // CLEAR == 0 and array is initialized with 0s + t= new short[width][height]; + } + + public LabyModel(String pFormat, InputStream pIn) + throws IOException + { + parseInputStream(pFormat, pIn); + } + + public void setMazeListener(MazeCreationListener listener) + { + this.listener=listener; + } + + // entry and exit can be outside the model boundaries a one x or one y out. + public boolean addEntryOrExit(int x, int y) + { + entryExits.add(new Position(x,y)); + 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++) + { + out4XY(x,y); + } + System.out.print('\n'); + } + } + + public void out4XY(int x,int y) + { + int direction=t[x][y]; + if ( ( direction & OPEN ) == OPEN ) + { + System.out.print("?"); + } + else if ( ( direction & CLOSED ) == CLOSED ) + { + System.out.print("."); + } + 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(".."); + } + + if ( ( (t[x][y] & RIGHT) == RIGHT ) || + ( ( x+ 1 < width) && ( ( t[x+1][y] & LEFT ) == LEFT ) )) + { + System.out.print("-"); + } + else + { + System.out.print("H"); + } + + } + + public String outHorizWall2XY(int x, int y) + { + String freeway= " "; + if ( (t[x][y] & SOLVED) == SOLVED) + { + freeway="*"; + } + if ( ( x < 0 ) || ( x >width ) ) + { + return " H"; + } + if ( ( y < 0 ) || ( y >= height ) ) + { + return "HH"; + } + + if ( ( (t[x][y] & DOWN) == DOWN ) || (( y+ 1 < height) && ( ( t[x][y+1] & UP ) == UP ))) + { + return freeway + "H"; + } + else + { + return "HH"; + } + } + + public String out2XY(int x,int y) + { + // can check for entry exits. + + 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) + { + freeway="*"; + } + // don't display information about short. + direction &= ~OPEN; + 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="."; + } + + + 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 ) )) + { + low=low+freeway; + } + else + { + low=low+"H"; + } + + return low; + } + + 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++) + { + brickMap[x][y]=new Brick(out2XY(x,y), outHorizWall2XY(x, y),getWalls(x,y)); + } + } + return new LabyMap(brickMap, (LinkedList) entryExits.clone()); + } + } + + public int getWidth(){ + return this.width; + } + + public int getHeight(){ + return this.height; + } + + public LinkedList getOpenList() + { + return openList; + } + + public void reset() + { + depth = 0; + computeOpenList(); + } + + 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++) + { + t[x][y] &= ~OPEN; + } + + } + } + + /** + 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() + { + boolean check = true; + synchronized(coherentLock) + { + // node in OPEN should be tagged OPEN and not CLOSED. + for ( Position p : openList) + { + int x = p.getX(); + int y = p.getY(); + if ( (t[x][y] & OPEN ) != OPEN ) + { + check = false; + t[x][y] |= OPEN; + } + if ( (t[x][y] & CLOSED ) == CLOSED ) + { + check = false; + t[x][y] &= ~CLOSED; + } + } + // should do reverse : every node tagged OPEN should be in open list. + // a little more cpu consuming... + } + return check; + } + + public void computeOpenList() + { + openList.clear(); + for (int y=0; y < height; y++) + { + for (int x=0; x < width; x++) + { + if ( (t[x][y] & CLOSED ) == CLOSED ) + { + t[x][y] &= ~OPEN; + } + if ( ( (t[x][y] & OPEN) == OPEN ) || ( t[x][y] == CLEAR )) + { + openList.add(new Position(x,y)); + } + } + + } + } + + public void generateWithEntry(int x, int y){ + + openList.add(new Position(x,y)); + while ( ! openList.isEmpty() ) + { + step(0); // fixme depth not set + + synchronized(coherentLock) + { + linearwork ++; + + if ( linearwork % maxdepth == 0 ) + { + coherentLock.notifyAll(); + if (listener != null) + { + listener.changed(null,null,this); + } + // should not continue in depth first... + if ( ! openList.isEmpty() ) + { + // insert head as next position to pick up. + Position current = openList.removeFirst(); + openList.add(current); + } + } + } + + } + + if ( deepestEnd != null ) + { + t[deepestEnd.getX()][deepestEnd.getY()]|=GOAL; + } + } + + public int getDepth() + { + return depth; + } + + public Position getDeepestEnd() + { + return deepestEnd; + } + + public int getDeepestPath() + { + return deepest; + } + + public boolean maxReached() + { + return maxreached; + } + + /** + @param p Position + @return true if newly added , false if already open. + **/ + private boolean open(boolean first, Position p) + { + int x = p.getX(); + int y = p.getY(); + if ( ( t[x][y] & OPEN ) != OPEN ) + { + t[x][y]|=OPEN; + openList.addLast(p); + return true; + } + return false; + } + + /** + resolve this labrynth using internal representation + **/ + public LinkedList resolve(int x, int y, MazeResolutionListener rlistener) + { + int newx=x; + int newy=y; + LinkedList backpath = new LinkedList(); + + while ( ! (( newx == 0 ) && ( newy == 0 ))) + { + Position found=null; + x = newx; + y = newy; + backpath.addFirst( new Position(x,y)); + + // should find from all direction one that point to this. + ArrayList freeDirection = new ArrayList(); + + for (int didx=0; didx < 4; didx ++ ) { + int delta=0; + short direction=AllDirections[didx]; + short reversedirection=AllDirections[(didx+2)%4]; + short pointingdirection=DIRECTION; + + if ( ( direction & POSITIVE ) == POSITIVE ) { delta = 1; pointingdirection |= NEGATIVE ; } + else { delta = -1; pointingdirection |= POSITIVE ;} + + if ( ( direction & HORIZONTAL ) == HORIZONTAL ) { newx = x + delta; newy = y; pointingdirection |= HORIZONTAL; } + else { newy = y + delta; newx = x; pointingdirection |= VERTICAL ;} + + // internal GUARD. + if ( (reversedirection & pointingdirection) != pointingdirection ) + { + System.out.println("Internal ERROR. Please check AllDirections order " + ( reversedirection & pointingdirection ) + " " + pointingdirection ); + return backpath; + } + + if ( (newx >= 0) && ( newy >= 0 ) && (newx < width) && (newy < height) ) { + if ( ( t[newx][newy] & reversedirection ) == reversedirection){ + if ( found != null ) { + // 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 (rlistener != null) + { + rlistener.notifySearchError("Model Error : two parents " + found.toString() + " " + new Position(newx,newy).toString()); + } + return backpath; + } + found = new Position(newx,newy); + if (rlistener != null) + { + rlistener.notifySearch(found); + } + break; + } + } + } + if ( found == null ) + { + rlistener.notifySearchError("No path found !"); + break; + } + // System.out.println(found); + newx=found.getX(); + newy=found.getY(); + t[newx][newy] |= SOLVED; + } + if (rlistener != null) + { + rlistener.notifyCompletion(backpath); + } + return backpath; + } + + /** + @returns wheter process closed current node. + **/ + public boolean step(int depth) + { + boolean complete = false; + Position current = null; + + synchronized(coherentLock) + { + + if ( ! openList.isEmpty() ) + { + // last : in depth before. + current = openList.getLast(); + } + else + { + return true; + } + + if ( current != null ) + { + int x = current.getX(); + int y = current.getY(); + + // should find all free positions... + ArrayList freeDirection = new ArrayList(); + + for (short direction : AllDirections ) { + int delta=0; + int newx = -1; + int newy = -1; + + if ( ( direction & POSITIVE ) == POSITIVE ) { delta = 1; } + else { delta = -1; } + + if ( ( direction & HORIZONTAL ) == HORIZONTAL ) { newx = x + delta; newy = y; } + 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 ( ! freeDirection.isEmpty()) + { + // control random using our own pseudorandom + short direction = 0; + if ( freeDirection.size() > 1 ) + { + direction=freeDirection.get(random.nextInt(freeDirection.size())); + } + else + { + direction=freeDirection.get(0); + Position last=openList.removeLast(); + if ( last != current ) + { + // GUARD, should not happen ( or multi-thread access to model error ) + System.err.println("INTERNAL ERROR 3"); + return false; + } + } + + int delta=0; + int newx = -1; + int newy = -1; + + if ( ( direction & POSITIVE ) == POSITIVE ) { delta = 1; } + else { delta = -1; } + + if ( ( direction & HORIZONTAL ) == HORIZONTAL ) { newx = x + delta; newy = y; } + else { newy = y + delta; newx = x; } + + Position target = new Position(newx,newy,current.getDepth()+1); + open(false,target); + if (( t[x][y] & DIRECTION ) == DIRECTION ) { t[x][y] |= direction; } + else { t[x][y] = direction; } // not a 'direction' ... is it necessary to check ? + if ( freeDirection.size() > 1 ) + { + // keep it open at the very same place, previous open did add another node to inspect. + return false; + } + // else this was the unique direction , object already removed ( since if we try to remove it now it is not the last ) + + // can proceed to close + } + else { + // no free direction remaining => closing + Position last=openList.removeLast(); + 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; + deepestEnd = current; + } + } // current != null; + } // synchronized + return complete; + } + + public short getWalls(int x, int y) + { + short walls=0; + for (short direction: AllDirections) + { + if (hasWallInDirection(x,y,direction)) + { + walls |= (direction>>FLAGLENGTH); + } + } + return walls; + } + + /** is there a wall in that direction ? + **/ + public boolean hasWallInDirection(int x, int y, short direction) + { + int newx=0; + int newy=0; + int delta=0; + // best way to find reversedirection ? + int reversedirection=0; + + // is this direction on the path ? yes => no wall + if ( ( t[x][y] & direction ) == direction ) + { + return false; + } + + // is adjacent tile in direction pointing in reverse direction ? yes => no wall + if ( ( direction & POSITIVE ) == POSITIVE ) { delta = 1; } + else { delta = -1; } + + if ( ( direction & HORIZONTAL ) == HORIZONTAL ) { + newx = x + delta; + newy = y; + reversedirection = (direction == RIGHT) ? LEFT : RIGHT; + } + else { + newy = y + delta; + newx = x; + reversedirection = (direction == UP) ? DOWN : UP; + } + + if ( (newx >= 0) && ( newy >= 0 ) && (newx < width) && (newy < height) ) { + return ( (t[newx][newy]&reversedirection) != reversedirection ); + } + else { + // outside boundaries. + // TODO CHECK exits. + return true; + } + } + + 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.writeInt(getWidth()); + dataOut.writeInt(getHeight()); + dataOut.flush(); + for (int y = 0; y < getHeight(); y++) + { + for (int x = 0 ; x < getWidth() ; x++) + { + dataOut.writeShort(t[x][y]); + } + } + dataOut.flush(); + } + else + { + throw new IOException("Format " + pFormat + " Not yet implemented."); + } + } + + 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") )) + { + 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(); + } + } + // should be at end of stream ? Not necessary can stream multiple labs ( or tiling ). + } + 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 new file mode 100644 index 0000000..607c7a4 --- /dev/null +++ b/java/org/artisanlogiciel/games/Main.java @@ -0,0 +1,59 @@ +package org.artisanlogiciel.games; + +import java.util.Scanner; +import java.util.LinkedList; + +public class Main +{ + + public WallsProvider generate(int width,int height, int maxdepth, MazeListener listener) + { + LabyModel model = new LabyModel(width, height,maxdepth, new java.util.Random()); + if ( listener != null ) + { + model.setListener(listener); + } + 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); + 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); + LinkedList exits = new LinkedList(); + model.addEntryOrExit(-1,0); + model.addEntryOrExit(width,height -1); + System.out.println(model.toLabyMap().toString()); + if ( ! model.check() ) + { + System.out.println("Check failed"); + } + model.debugOut(); + model.resolve(width-1,height-1); + LabyMap labyMap =model.toLabyMap(); + return labyMap; + } + + public static void main(String pArgs[]) + { + System.out.println("enter width height and maxdepth"); + Scanner console = new Scanner(System.in); + int width = console.nextInt(); + int height = console.nextInt(); + int maxdepth = console.nextInt(); + Main m= new Main(); + LabyMap map = m.generate2(width,height,maxdepth); + 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 new file mode 100644 index 0000000..7059485 --- /dev/null +++ b/java/org/artisanlogiciel/games/MazeCreationListener.java @@ -0,0 +1,9 @@ +package org.artisanlogiciel.games; + +/** + MazeCreationListener + **/ +interface MazeCreationListener +{ + void changed(Position cornerleft, Position cornerright, WallsProvider provider); +} diff --git a/java/org/artisanlogiciel/games/MazeResolutionListener.java b/java/org/artisanlogiciel/games/MazeResolutionListener.java new file mode 100644 index 0000000..7c6fd5c --- /dev/null +++ b/java/org/artisanlogiciel/games/MazeResolutionListener.java @@ -0,0 +1,17 @@ +package org.artisanlogiciel.games; + +import java.util.LinkedList; + +/** + MazeResolutionListener used as interface between resolver and (mostly) GUI + **/ +public interface MazeResolutionListener +{ + + public boolean notifySearch(Position pPosition); + + public void notifySearchError(String error); + + public void notifyCompletion(LinkedList solvedPath); + +} diff --git a/java/org/artisanlogiciel/games/Position.java b/java/org/artisanlogiciel/games/Position.java new file mode 100644 index 0000000..bd23ccb --- /dev/null +++ b/java/org/artisanlogiciel/games/Position.java @@ -0,0 +1,38 @@ +package org.artisanlogiciel.games; + +public class Position { + private int x, y; + private int depth; + + public Position(int x, int y,int depth){ + this.x=x; + this.y=y; + this.depth=depth; + } + + public Position(int x, int y){ + this.x=x; + this.y=y; + this.depth=0; + } + + public int getX() + { + return this.x; + } + + public int getY() + { + return this.y; + } + + public int getDepth() + { + return depth; + } + + public String toString() + { + return "(" + x + "," + y + ")" + "/" + depth ; + } +} diff --git a/java/org/artisanlogiciel/games/Wall3d.java b/java/org/artisanlogiciel/games/Wall3d.java new file mode 100644 index 0000000..76349fe --- /dev/null +++ b/java/org/artisanlogiciel/games/Wall3d.java @@ -0,0 +1,220 @@ +package org.artisanlogiciel.games; + +import java.util.Scanner; +import java.io.OutputStream; +import java.io.IOException; + +/** + Wall3d to create walls in 3d + South, West North East... + **/ +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}}}; + + 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); + + int triangle[][][] = null; + + public Wall3d(int t[][][]) + { + triangle = t; + } + + 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) + { + 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++) + { + short uaxis=0; + for (int axis=0; axis <2; axis++) + { + short caxis = AXIS[i/2][axis]; + if ( caxis >0 ) + { + f=1; + } + else + if ( caxis <0 ) + { + f=-1; + caxis=(short) -caxis; + } + uaxis|=caxis; + if ( caxis == X ) + { + caxis=0; + } + else if ( caxis == Y) + { + caxis=1; + } + 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]; + } + if ( (uaxis & X) == 0 ) + { + uaxis=0; + } + else if ( (uaxis & Y) == 0) + { + uaxis=1; + } + else + { + uaxis=2; + } + triangle[i][p][uaxis]=translate[uaxis] + AXIS[i/2][2]*factor[uaxis]; + } + } + } + + + 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++) + { + // 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]; + } + } + } + return t; + + } + + 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++) + { + s+="vertex"; + for (int a=0; a<3; a++) + { + // s+=" t "+ t + " p " + p + " a " + a + "=" + triangle[t][p][a]; + s+= " " + triangle[t][p][a]; + } + s+="\n"; + } + s=s+"endloop\nendfacet\n"; + } + 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, WallsProvider 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 v < + U R D L + p i o e + g w f + h n t + t + **/ + public short getWalls(int x, int y); +}