initial commit for labryinth project on git_hub.

Signed-off-by: philippe lhardy <philippe@pavilionartlogiciel>
This commit is contained in:
philippe lhardy
2014-12-24 17:28:23 +01:00
commit 850744326a
14 changed files with 2106 additions and 0 deletions

View File

@@ -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;
}
}

View File

@@ -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<Position> 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<Position> solvedPath)
{
LinkedList<Position> newPath = (LinkedList<Position>) 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<Position> exits = new LinkedList<Position>();
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");
}
}
}
}

View File

@@ -0,0 +1,106 @@
package org.artisanlogiciel.games;
import java.util.LinkedList;
class LabyMap
implements WallsProvider
{
Brick[][] tileMap;
LinkedList<Position> exits = new LinkedList<Position>();
public LabyMap(Brick[][] tileMap, LinkedList<Position> 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;
}
}

View File

@@ -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<<FLAGLENGTH|DIRECTION|HORIZONTAL|NEGATIVE;
private final static short DOWN=Brick.DOWN<<FLAGLENGTH|DIRECTION|VERTICAL|POSITIVE;
private final static short RIGHT=Brick.RIGHT<<FLAGLENGTH|DIRECTION|HORIZONTAL|POSITIVE;
private final static short UP=Brick.UP<<FLAGLENGTH|DIRECTION|VERTICAL|NEGATIVE;
private 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.
private final static short SOLVED=64<<FLAGLENGTH; // flag when solution is on this path.
private final static short FREE=128<<FLAGLENGTH; // free flag
// remains 2 free bits ( keep one out for sign )
private static final short[] AllDirections = {LEFT,DOWN,RIGHT,UP};
private int width;
private int height;
private short [][] t;
private int depth=0;
/** will enforce maxdepth as maxium length of path in depth first.
It means that after every maxdepth moves there should be crossing paths.
I think that to shorter maxdepth is the harder the labyrinth is ...
**/
private int maxdepth=0;
private int deepest=0; // longest path found
private int linearwork=0;
private Position deepestEnd = null;
boolean maxreached = false;
java.util.Random random;
private final LinkedList<Position> openList = new LinkedList<Position>();
//list of entries and exits.
private final LinkedList<Position> entryExits = new LinkedList<Position>();
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("<v");
break;
case LEFT|UP:
System.out.print("<^");
break;
case LEFT|RIGHT|UP:
System.out.print("LL");
break;
case LEFT|RIGHT|DOWN:
System.out.print("TT");
break;
case LEFT|DOWN|UP:
System.out.print("<|");
break;
case RIGHT|DOWN|UP:
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<Position>) entryExits.clone());
}
}
public int getWidth(){
return this.width;
}
public int getHeight(){
return this.height;
}
public LinkedList<Position> 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<Position> resolve(int x, int y, MazeResolutionListener rlistener)
{
int newx=x;
int newy=y;
LinkedList<Position> backpath = new LinkedList<Position>();
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<Short> freeDirection = new ArrayList<Short>();
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<Short> freeDirection = new ArrayList<Short>();
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.");
}
}
}

View File

@@ -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<Position> exits = new LinkedList<Position>();
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<Position> exits = new LinkedList<Position>();
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());
}
}

View File

@@ -0,0 +1,9 @@
package org.artisanlogiciel.games;
/**
MazeCreationListener
**/
interface MazeCreationListener
{
void changed(Position cornerleft, Position cornerright, WallsProvider provider);
}

View File

@@ -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<Position> solvedPath);
}

View File

@@ -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 ;
}
}

View File

@@ -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<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());
}
}
}
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);
}
}

View File

@@ -0,0 +1,27 @@
package org.artisanlogiciel.games;
/**
WallsProvider provide a Walls representation
**/
public interface WallsProvider
{
int getWidth();
int getHeight();
/**
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
t
**/
public short getWalls(int x, int y);
}