Related
I wrote a program that solves a peg solitaire in java.
My program gets a starting board and a destination board and try to finish the game.
I have a sort of counter that count my turn because I have a destination with more the 1 peg rest and as assume that if I have to remove only 2 pegs so I can solve it in only 2 moves.
I have an board class that I create:
public class Board {
private int board[][] = new int[7][7];
public Board(String place)
{
board[0][0]=2;
board[1][0]=2;
board[5][0]=2;
board[6][0]=2;
board[0][1]=2;
board[1][1]=2;
board[5][1]=2;
board[6][1]=2;
board[0][5]=2;
board[1][5]=2;
board[5][5]=2;
board[6][5]=2;
board[0][6]=2;
board[1][6]=2;
board[5][6]=2;
board[6][6]=2;
int loca=0;//location on the string of place
for(int i=0;i<7;i++){
for(int j=0;j<7;j++){
if(board[i][j]!=2) {
if (place.charAt(loca) == 'O') {
loca++;
board[i][j] = 1;
} else if (place.charAt(loca) == '.') {
loca++;
board[i][j] = 0;
}
}
System.out.print(board[i][j]);//print for test
}
System.out.println();//print for test
}
System.out.println();
}
public Board(Board copy){
for(int i=0;i<7;i++)
{
for(int j=0;j<7;j++)
{
board[i][j]=copy.getValue(i,j);
}
}
}
public int getValue(int x, int y)
{
return board[x][y];
}
public boolean isFinished(Board destination)
{
for(int i=0;i<7;i++)
{
for(int j=0;j<7;j++)
{
if(this.getValue(i,j)!=destination.getValue(i,j))
{
return false;
}
}
}
return true;
}
public Board turn(Board board,String direction,int x,int y)
{
if(direction.equals("right"))
{
board.setValue(x,y,0);
board.setValue(x+1,y,0);
board.setValue(x+2,y,1);
return board;
}
else if(direction.equals("left"))
{
board.setValue(x,y,0);
board.setValue(x-1,y,0);
board.setValue(x-2,y,1);
return board;
}
else if(direction.equals("up"))
{
board.setValue(x,y,0);
board.setValue(x,y-1,0);
board.setValue(x,y-2,1);
return board;
}
else if(direction.equals("down"))
{
board.setValue(x,y,0);
board.setValue(x,y+1,0);
board.setValue(x,y+2,1);
return board;
}
else{
System.out.println("there is not such direction, method turn on board class");
return null;//just for caution
}
}
public boolean isLegal(int x, int y){
if(board[x][y]==2)
{
return false;
}
else{
return true;
}
}
public boolean canTurn(String direction,int x,int y){
if(direction.equals("right"))
{
if(x<5) {
if (board[x][y] == 1 && board[x + 1][y] == 1 && board[x + 2][y] == 0) {
return true;
}
}
}
else if(direction.equals("left"))
{
if(x>1) {
if (board[x][y] == 1 && board[x - 1][y] == 1 && board[x - 2][y] == 0) {
return true;
}
}
}
else if(direction.equals("up"))
{
if(y>1) {
if (board[x][y] == 1 && board[x][y - 1] == 1 && board[x][y - 2] == 0) {
return true;
}
}
}
else if(direction.equals("down"))
{
if(y<5) {
if (board[x][y] == 1 && board[x][y + 1] == 1 && board[x][y + 2] == 0) {
return true;
}
}
}
else{
System.out.println("there is not such direction, method canTurn on board class");
return false;//just for caution
}
return false;
}
public void setValue(int x, int y, int value)
{
board[x][y]=value;
}
}
and I wrote my "solver" class.
public class PegSolver {
public int peg =1;
Board destinationBoard = new Board("OOOOOOOOOOOOOOOOO..OOOOOOOOOOOOOO");
Board board = new Board("OOOOOOOOOOOOOOOO.OOOOOOOOOOOOOOOO");
public void start(){
solve(0,board);
}
public boolean solve(int turn, Board board){
Board temp = new Board(board);
if(turn>peg)
{
return false;
}
else if(turn==peg){
//todo:check if solve
if(temp.isFinished(destinationBoard))
{
System.out.println("solution");
return true;
}
else
{
return false;
}
}
else//lower then 8
{
for(int i=0;i<7;i++){
for (int j=0;j<7;j++)
{
if(board.isLegal(i,j)) {
if(board.canTurn("right",i,j) && solve(turn++, temp.turn(temp, "right", i, j)))
{
return true;
}
else if(board.canTurn("left",i,j) && solve(turn++, temp.turn(temp, "left", i, j)))
{
return true;
}
else if(board.canTurn("up",i,j) && solve(turn++, temp.turn(temp, "up", i, j)))
{
return true;
}
else if(board.canTurn("down",i,j) && solve(turn++, temp.turn(temp, "down", i, j)))
{
return true;
}
}
}
}
}
return false;
}
}
When the program finds a solution, it needs to print "solution" but for some reason my program can't find a solution even when it's a basic destination with one move.
Can someone help me please?
Please review the modified code.
Many of the changes are related to indices and directions inconsistency across the code: where x represents horizontal index and y represents vertical index: array index should be board[y][x] (and not board[x][y]).
Many "magic numbers" were changed to constants for better readability of the code.
A toString method was added to the Boardclass to print out a board state. It uses special characters to make a nice printout :
This is helpful when debugging.
public class PegSolver {
private final Board startBoard, destinationBoard;
public PegSolver(Board startBoard, Board destinationBoard) {
super();
this.startBoard = startBoard;
this.destinationBoard = destinationBoard;
}
public void start(){
solve(0,startBoard);
}
private boolean solve(int turn, Board board){
//check if solve
if(board.isFinished(destinationBoard))
{
System.out.println("solved after "+ turn +" turns");
return true;
}
for(int x=0;x<board.boardSize();x++){
for (int y=0;y<board.boardSize();y++)
{
if(board.isLegal(x,y)) {
if(board.canTurn("right",x,y)
//turn++ changed to turn+1 so turn is incremented before invoking next solve
//and avoid changing the value of turn
&& solve(turn+1, board.turn(new Board(board), "right", x, y)))
return true;
else if(board.canTurn("left",x,y)
&& solve(turn+1, board.turn(new Board(board), "left", x, y)))
return true;
else if(board.canTurn("up",x,y)
&& solve(turn+1, board.turn(new Board(board), "up", x, y)))
return true;
else if(board.canTurn("down",x,y)
&& solve(turn+1, board.turn(new Board(board), "down", x, y)))
return true;
}
}
}
return false;
}
public static void main(String[] args) {
Board[] destinationBoards = {
//by order of number of turns
new Board("OOOOOOOOOOOOOO..OOOOOOOOOOOOOOOOO"), //one right turn
new Board("OOO.OOOO.OOOOO.OOOOOOOOOOOOOOOOOO"), //right, down
new Board("OOO.OO..OOOOOO.OOOOOOOOOOOOOOOOOO"), //right, down,right
new Board("OOO.OOO.OOOOO..OOOOO.OOOOOOOOOOOO"), //right, down,right,up
new Board("OOOOOOO..OOOO...OOOO.OOOOOOOOOOOO"), //right, down,right,up,up
new Board(".OO.OOO.OOOOO...OOOO.OOOOOOOOOOOO"), //right, down,right,up,up,down
new Board(".OO.OOO.OOOOO...OOOOO..OOOOOOOOOO"), //right, down,right,up,up,down, left
new Board(".OO.OOO.OOOOO...OOOOO.OOOOO.OO.OO"), //right, down,right,up,up,down,left,up
new Board(".OO.OO..O.OOO...OOOOO.OOOOO.OO.OO"), //10 turns
new Board("..O..O.O..OOO...OOOO..OOOOO..O..O"), //15 turns
new Board(".........O................O......"), //30 turns
new Board("...................O............."), //31 turns
};
Board startBoard = new Board("OOOOOOOOOOOOOOOO.OOOOOOOOOOOOOOOO");
for(Board destinationBoard : destinationBoards ){
new PegSolver(startBoard, destinationBoard).start();
}
}
}
class Board {
//int representation of the three states of a board cell
private final static int EMPTY = 0, PEG = 1, BORDER = 2;
/*cahr representation of the three states of a board cell
special chars are used to get nice printout
todo: change board to char[][] to avoid the need for two
representations (int and char)
*/
private final static char[] CHAR_REPRESENTATION = {9898,9899,10062};
private final static char ERROR = '?';
private final int BOARD_SIZE=7, CORNER_SIZE=2;
private final int board[][] = new int[BOARD_SIZE][BOARD_SIZE];
public Board(String place) {
int loca=0;
for(int y=0;y<BOARD_SIZE;y++){
for(int x=0;x<BOARD_SIZE;x++){
if(isWithinBoard(x,y)) {
if (place.charAt(loca) == 'O') {
loca++;
board[y][x] = PEG;
} else if (place.charAt(loca) == '.') {
loca++;
board[y][x] = EMPTY;
}
}else{
board[y][x] = BORDER;
}
}
}
//for testing
//System.out.println(this);
}
//copy constructor
public Board(Board copy){
for(int x=0;x<BOARD_SIZE;x++)
{
for(int y=0;y<BOARD_SIZE;y++)
{
board[y][x]=copy.getValue(x,y);
}
}
}
public int getValue(int x, int y)
{
return board[y][x]; //and not return board[x][y];
}
public boolean isFinished(Board destination)
{
for(int i=0;i<BOARD_SIZE;i++)
{
for(int j=0;j<BOARD_SIZE;j++)
{
if(this.getValue(i,j)!=destination.getValue(i,j))
return false;
}
}
return true;
}
public Board turn(Board board,String direction,int x,int y)
{
if(direction.equals("right"))
{
board.setValue(x,y,EMPTY);
board.setValue(x+1,y,EMPTY);
board.setValue(x+2,y,PEG);
return board;
}
else if(direction.equals("left"))
{
board.setValue(x,y,EMPTY);
board.setValue(x-1,y,EMPTY);
board.setValue(x-2,y,PEG);
return board;
}
else if(direction.equals("up"))
{
board.setValue(x,y,EMPTY);
board.setValue(x,y-1,EMPTY);
board.setValue(x,y-2,PEG);
return board;
}
else if(direction.equals("down"))
{
board.setValue(x,y,EMPTY);
board.setValue(x,y+1,EMPTY);
board.setValue(x,y+2,PEG);
return board;
}
System.out.println("there is not such direction, method turn on startBoard class");
return null;
}
public boolean isLegal(int x, int y){
if(board[y][x]==BORDER) //and not if(board[x][y]==BORDER)
return false;
return true;
}
public boolean canTurn(String direction,int x,int y){
if(direction.equals("right") && x < BOARD_SIZE - 2)
{
if (board[y][x] == PEG && board[y][x + 1] == PEG && board[y][x + 2] == EMPTY)
return true;
}
else if(direction.equals("left") && x > 1)
{
if (board[y][x] == PEG && board[y][x - 1] == PEG && board[y][x - 2] == EMPTY)
return true;
}
else if(direction.equals("up") && y > 1)
{
if (board[y][x] == PEG && board[y - 1][x] == PEG && board[y - 2][x] == EMPTY)
return true;
}
else if(direction.equals("down") && y < BOARD_SIZE - 2)
{
if (board[y][x] == PEG && board[y + 1][x] == PEG && board[y + 2][x] == EMPTY)
return true;
}
return false;
}
public void setValue(int x, int y, int value)
{
board[y][x]=value; //and not board[x][y]=value;
}
//for square nxn board
public int boardSize(){
return board.length;
}
public boolean isWithinBoard(int x, int y){
//check bounds
if (x < 0 || y < 0 || x >= BOARD_SIZE || y >= BOARD_SIZE) return false;
//left top corner
if (x < CORNER_SIZE && y < CORNER_SIZE) return false;
//right top corner
if(x >= BOARD_SIZE - CORNER_SIZE && y < CORNER_SIZE) return false;
//left bottom corner
if(x < CORNER_SIZE && y >= BOARD_SIZE - CORNER_SIZE) return false;
//right bottom corner
if(x >= BOARD_SIZE - CORNER_SIZE && y >= BOARD_SIZE - CORNER_SIZE) return false;
return true;
}
#Override
public String toString() {
StringBuilder sb = new StringBuilder();
for (int[] row : board) {
for(int cell : row){
if(cell<CHAR_REPRESENTATION.length && cell >= 0) {
sb.append(CHAR_REPRESENTATION[cell]);
}else{
sb.append(ERROR);
}
}
sb.append("\n"); //new line
}
return sb.toString();
}
}
Todo:
The code is working but it needs further in-depth testing and debugging.
If something is not clear, don't hesitate to ask.
I'm attempting to create a game "dungeon" map generator in Java in the style of roguelike etc. games. I generate rooms randomly, and then connect them with corridors. I'm trying to use A* pathfinding in corridor creation. I currently am creating just one corridor between rooms in the indices 1 and 2, if there is more than one room.
For some reason, the corridor creation seems to fail when I try to generate more than 1 maps ("floors"). The amount of floors is specified as a command-line parameter. Thus far, when I've generated just one floor, everything works perfectly. My gut feeling says that there's something about the floors that messes up my algorithm.
I thought that maybe an outside view of the project could help. There is quite a lot of code, but I'd be very grateful if someone took the time to review it. I can provide more information if it's needed.
THE RESULTS
The result, when correct, should look like this:
Map 1
# wall
. floor
+ door
$ corridor
..............................
..............................
..............................
..............................
..............................
..............................
..............................
..............................
...........$$$$$$$$$$.........
...........$......##+#######..
.....######$......#........#..
.....#....#$......#........#..
.....#....#$......#........#..
.....#....#$......#........#..
.....#....#$......#........#..
.....#....#$......#........#..
.....#....#$......#........#..
.....##+###$......##########..
.......$$$$$..................
..............................
The buggy result looks like this (the corridor does not go door-to-door, it just ends in a random location):
...........$$$...#########....
...........$#+##.#.......#....
...........$#..#.#.......#....
...........$#..#.#.......+....
###+###....$#..#.#.......#....
#.....#....$#..#.#.......#....
#.....#....$#..#.#.......#....
#.....#....$#..#.#########....
#.....#....$####..............
#.....#....$..................
#.....#....$..................
#######....$..................
...........$..................
...........$..................
...........$..................
...........$..................
...........$..................
...........$..................
.......$$$$$..................
..............................
THE CODE
AStar.java:
/**
* See https://www.raywenderlich.com/4946/introduction-to-a-pathfinding
*/
public class AStar {
private List<AStarSquare> openList;
private List<AStarSquare> closedList;
private Exporter debugExporter;
private static final Coords[] squareOffsetsToCheck = new Coords[] {
new Coords(0, 1),
new Coords(1, 0),
new Coords(0, -1),
new Coords(-1, 0)
};
public AStar() {
openList = new ArrayList<>();
closedList = new ArrayList<>();
debugExporter = new Exporter();
}
public List<Coords> findPath(Coords start, Coords end, Map map) {
List<Coords> path = new ArrayList<>(); // each square on the generated path
AStarSquare currentSquare = new AStarSquare(start, null); // current square around which possible squares are evaluated - start point
closedList.add(currentSquare); // add start point to closed list
createUpdateOpenSquares(currentSquare, start, end, map); // create open squares for first iteration
calculateScores(start, end, map); // calculate scores for first iteration
int loopGuard = 0;
// loop until break
while(true) {
if(openList.size() == 0) {
break;
}
currentSquare = getLowestOpenSquare(); // get the square with the lowest score
if(isAdjacentToDoor(currentSquare.getCoords(), end) /*|| currentSquare.getCoords().equalz(end) || loopGuard >= 1000*/) // end point reached or no possible next squares
break; // - exclude last square (door)
openList.remove(currentSquare);
closedList.add(currentSquare);
createUpdateOpenSquares(currentSquare, start, end, map); // create and/or update squares next to the current square
calculateScores(start, end, map);
map.setDebugCorridor(formulatePath(currentSquare));
loopGuard++;
}
path = formulatePath(currentSquare);
return path;
}
private void createUpdateOpenSquares(AStarSquare currentSquare, Coords start, Coords end, Map map) {
for(Coords squareOffsetToCheck : squareOffsetsToCheck) {
Coords coordsToCheck = currentSquare.getCoords().vectorAdd(squareOffsetToCheck);
if(map.isFloor(coordsToCheck)
&& !map.isInsideRoom(coordsToCheck)
&& isWithinMap(map, coordsToCheck)
&& !isClosed(coordsToCheck)) {
AStarSquare openSquare = getOpen(coordsToCheck);
if(openSquare == null)
openList.add(new AStarSquare(coordsToCheck, currentSquare));
else // is open
openSquare.setPrevious(currentSquare);
}
}
}
private boolean isClosed(Coords coords) {
for(AStarSquare closed : closedList) {
if(closed.getCoords().equalz(coords))
return true;
}
return false;
}
private AStarSquare getOpen(Coords coords) {
for(AStarSquare open : openList) {
if(open.getCoords().equalz(coords))
return open;
}
return null;
}
private boolean isWithinMap(Map map, Coords coords) {
if(coords.getX() < 0
|| coords.getY() < 0
|| coords.getX() >= map.getW()
|| coords.getY() >= map.getH())
return false;
return true;
}
private boolean isAdjacentToDoor(Coords coords, Coords end) {
for(Coords squareOffset : squareOffsetsToCheck) {
Coords offsetSquare = coords.vectorAdd(squareOffset);
if(offsetSquare.equalz(end))
return true;
}
return false;
}
private void calculateScores(Coords start, Coords end, Map map) {
for(AStarSquare square : openList) {
square.calculateScores(map, start, end);
}
}
private AStarSquare getLowestOpenSquare() {
AStarSquare lowestScore = null;
for(AStarSquare square : openList) {
// if lowestScore not set or if square.f is lower than lowestScore.f, set square to lowestScore
if(lowestScore == null || lowestScore.getF() > square.getF())
lowestScore = square;
}
return lowestScore;
}
// exclude first square (door)
private List<Coords> formulatePath(AStarSquare currentSquare) {
List<Coords> path = new ArrayList<>();
while(currentSquare.getPrevious() != null) {
path.add(currentSquare.getCoords());
currentSquare = currentSquare.getPrevious();
}
return path;
}
}
AStarSquare.java:
/**
* See https://www.raywenderlich.com/4946/introduction-to-a-pathfinding
*/
public class AStarSquare {
private Coords coords;
private AStarSquare previous;
private int g, h;
private boolean calculated;
public AStarSquare() {
g = h = 0;
calculated = false;
}
public AStarSquare(Coords coords) {
this();
this.coords = coords;
previous = null;
}
public AStarSquare(Coords coords, AStarSquare previous) {
this();
this.coords = coords;
this.previous = previous;
}
public void calculateScores(Map map, Coords start, Coords destination) {
g = previous.getG() + 1; // g = distance from start point
h = destination.getDistance(coords); // h = estimated (=shortest) distance from the current location to the destination
calculated = true;
}
}
Main class:
public class DungeonMapGenerator {
public static void main(String[] args) {
List<String> argsList = Arrays.asList(args);
if(!argsList.contains("-w")
|| !argsList.contains("-h")
|| !argsList.contains("-f")) {
System.out.println("Usage: java -jar DungeonMapGenerator.jar -w [width] -h [height] -f [floors] -[export option]");
System.exit(1);
}
int width = 0, height = 0, floors = 0;
for(int i = 0; i < args.length; i++) {
if(args[i].equalsIgnoreCase("-w"))
width = tryParseInt(args, i + 1, 30);
else if(args[i].equalsIgnoreCase("-h"))
height = tryParseInt(args, i + 1, 20);
else if(args[i].equalsIgnoreCase("-f"))
floors = tryParseInt(args, i + 1, 1);
}
Generator mapGenerator = new Generator(width, height, floors);
List<Map> maps = mapGenerator.generateMaps();
Exporter mapExporter = new Exporter();
if(argsList.contains("-c"))
mapExporter.exportToConsole(maps);
else
System.out.println("No export option selected, quitting");
}
private static int tryParseInt(String[] args, int index, int deflt) {
int res;
if(index >= args.length) // index out of range
res = deflt;
try {
res = Integer.parseInt(args[index], 10);
} catch(NumberFormatException ex) {
res = deflt;
}
return res;
}
}
Generator.java
public class Generator {
private static final int
MIN_ROOMS = 1,
MAX_ROOMS = 5,
MIN_DIM = 3, // dim = min and max room dimensions
MAX_DIM = 10;
private AStar pathfinder;
private Random random;
private int mapWidth, mapHeight, floors;
public Generator(int mapWidth, int mapHeight, int floors) {
pathfinder = new AStar();
random = new Random(System.currentTimeMillis());
this.mapWidth = mapWidth;
this.mapHeight = mapHeight;
this.floors = floors;
}
public List<Map> generateMaps() {
List<Map> mapList = new ArrayList<>();
for(int i = 0; i < floors; i++) {
Map map = new Map(i + 1, mapWidth, mapHeight, generateRooms(mapWidth, mapHeight), null);
generateDoors(map, map.getRooms());
debugFindPath(map);
mapList.add(map);
}
return mapList;
}
private List<Room> generateRooms(int mapWidth, int mapHeight) {
List<Room> roomList = new ArrayList<>();
int nRooms = random.nextInt(5) + 1;
for(int i = 0; i < nRooms; i++) {
Room room = null;
do {
int w = 0, h = 0, x = 0, y = 0;
w = getRandomDim();
h = getRandomDim();
x = random.nextInt(mapWidth - w);
y = random.nextInt(mapHeight - h);
room = new Room(x, y, w, h);
} while(roomsOverlap(room, roomList));
roomList.add(room);
}
return roomList;
}
private boolean roomsOverlap(Room room, List<Room> rooms) {
for(Room listRoom : rooms) {
if(room.overlapsWithRoom(listRoom))
return true;
}
return false;
}
private int getRandomDim() {
return random.nextInt(MAX_DIM - MIN_DIM + 1) + MIN_DIM;
}
private void generateDoors(Map map, List<Room> roomList) {
for(int i = 0; i < roomList.size(); i++) {
Door door = new Door(roomList.get(i));
do {
door.setSide(getRandomCardinal());
door.setDistNW(getRandomDistNW(roomList.get(i), door.getSide()));
} while(!validateDoor(map, door));
roomList.get(i).setDoors(Arrays.asList(new Door[] { door }));
map.getDoors().add(door);
}
}
private Cardinal getRandomCardinal() {
int cardinalInt = random.nextInt(4);
Cardinal cardinal;
switch(cardinalInt) {
case 1:
cardinal = Cardinal.EAST;
break;
case 2:
cardinal = Cardinal.SOUTH;
break;
case 3:
cardinal = Cardinal.WEST;
case 0:
default:
cardinal = Cardinal.NORTH;
break;
}
return cardinal;
}
private int getRandomDistNW(Room room, Cardinal cardinal) {
int distNW = 0;
if(cardinal == Cardinal.NORTH || cardinal == Cardinal.SOUTH)
distNW = random.nextInt(room.getW() - 2) + 1; // exclude corners
else if(cardinal == Cardinal.EAST || cardinal == Cardinal.WEST)
distNW = random.nextInt(room.getH() - 2) + 1; // exclude corners
return distNW;
}
private boolean validateDoor(Map map, Door door) {
Coords doorCoordsOnMap = door.getCoordsOnMap();
if(door.getSide() == Cardinal.NORTH
&& (door.getParent().getTop() == 0
// check if adjacent to another room
|| map.isWall(new Coords(doorCoordsOnMap.getX(), doorCoordsOnMap.getY() - 1))))
return false;
else if(door.getSide() == Cardinal.EAST
&& (door.getParent().getRight() == mapWidth - 1
// check if adjacent to another room
|| map.isWall(new Coords(doorCoordsOnMap.getX() + 1, doorCoordsOnMap.getY()))))
return false;
else if(door.getSide() == Cardinal.SOUTH
&& (door.getParent().getBottom() == mapHeight - 1
// check if adjacent to another room
|| map.isWall(new Coords(doorCoordsOnMap.getX(), doorCoordsOnMap.getY() + 1))))
return false;
else if(door.getSide() == Cardinal.WEST
&& (door.getParent().getLeft() == 0
// check if adjacent to another room
|| map.isWall(new Coords(doorCoordsOnMap.getX() - 1, doorCoordsOnMap.getY()))))
return false;
return true;
}
private void debugFindPath(Map map) {
if(map.getRooms().size() == 1)
return;
map.setDebugCorridor(pathfinder.findPath(
map.getRooms().get(0).getDoors().get(0).getCoordsOnMap(),
map.getRooms().get(1).getDoors().get(0).getCoordsOnMap(),
map
));
}
}
Room.java
public class Room {
private Coords topLeft;
private int w, h;
private List<Door> doors;
public Room(int topLeftX, int topLeftY, int w, int h) {
topLeft = new Coords(topLeftX, topLeftY);
this.w = w;
this.h = h;
doors = new ArrayList<>();
}
public boolean overlapsWithRoom(Room otherRoom) {
return !(otherRoom.getLeft() > this.getRight()
|| otherRoom.getRight() < this.getLeft()
|| otherRoom.getTop() > this.getBottom()
|| otherRoom.getBottom() < this.getTop());
}
#Override
public String toString() {
return "Room ~ top: " + getTop() + " right: " + getRight()
+ " bottom: " + getBottom() + " left: " + getLeft()
+ " width: " + w + " height: " + h;
}
public boolean isWall(Coords coords) { /*** TESTAA!!! ***/
if(
// x is either left or right, y is between top and bottom
((coords.getX() == topLeft.getX() || coords.getX() == topLeft.getX() + w)
&& coords.getY() >= topLeft.getY() && coords.getY() < topLeft.getY() + h + 1)
||
// y is either top or bottom, x is between left and right
((coords.getY() == topLeft.getY() || coords.getY() == topLeft.getY() + h)
&& coords.getX() >= topLeft.getX() && coords.getX() < topLeft.getX() + w)
)
return true;
return false;
}
}
Door.java
(Cardinal is a simple enum containing NORTH, EAST, SOUTH and WEST)
public class Door {
private Room parent;
private Cardinal side;
private int distNW = 0;
public Door(Room parent) {
this.parent = parent;
this.side = null;
}
public Door(Room parent, Cardinal side) {
this.parent = parent;
this.side = side;
}
public Coords getCoordsOnMap() {
Coords coords = null;
if(side == Cardinal.NORTH)
coords = new Coords(parent.getLeft() + distNW, parent.getTop());
else if(side == Cardinal.EAST)
coords = new Coords(parent.getRight(), parent.getTop() + distNW);
else if(side == Cardinal.SOUTH)
coords = new Coords(parent.getLeft() + distNW, parent.getBottom());
else if(side == Cardinal.WEST)
coords = new Coords(parent.getLeft(), parent.getTop() + distNW);
return coords;
}
}
In AStar, your A* pathfinding algorithm adds to its opened and closed lists before returning the chosen path
When pathfinding with different start/end destinations or a different map, those lists will need to be reset
The issue is that you're reusing the AStar object for each path you're trying to find, causing conflicts with old searches
To fix it, use a new AStar object for every path you search, or add a method to clear the old data
I removed this line from the Generator constructor:
public Generator(int mapWidth, int mapHeight, int floors) {
// pathfinder = new AStar(); // REMOVED THIS LINE
...
}
And I added the following line to the Generator.generateMaps method:
public List<Map> generateMaps() {
List<Map> mapList = new ArrayList<>();
for(int i = 0; i < floors; i++) {
pathfinder = new AStar(); // ADDED THIS LINE
Map map = new Map(i + 1, mapWidth, mapHeight, generateRooms(mapWidth, mapHeight), null);
generateDoors(map, map.getRooms());
debugFindPath(map);
mapList.add(map);
}
return mapList;
}
And now everything seems to work.
Another option is to add the following lines to AStar.findPath():
public List<Coords> findPath(Coords start, Coords end, Map map) {
openList = new ArrayList<>();
closedList = new ArrayList<>();
...
}
I have made a custom bow that fires really quickly, and I have learned that all mobs are invulnerable for a short period of time after they get hit. This renders my bow pretty much useless. I was wondering if there is any way to change the duration that the mobs are invulnerable for, or even remove it at all.
Code for bow firing method:
#Override
public ActionResult<ItemStack> onItemRightClick(ItemStack stack, World worldIn, EntityPlayer playerIn, EnumHand hand) {
// int charge = 1;
// charge = ForgeEventFactory.onArrowLoose(stack, worldIn, playerIn, charge, true);
player123 = playerIn;
if (!worldIn.isRemote) {
final ItemArrow itemArrow = (ItemArrow) (Items.ARROW instanceof ItemArrow ? Items.ARROW : Items.ARROW);
final ItemStack ammo = new ItemStack(Items.ARROW, 1);
final float arrowVelocity = 1;
final EntityArrow entityArrow = itemArrow.createArrow(worldIn, ammo, playerIn);
entityArrow.setAim(playerIn, playerIn.rotationPitch, playerIn.rotationYawHead, 0.0F, arrowVelocity * 3.5F, 0F);
entityArrow.setDamage(2);
entityArrow.pickupStatus = EntityArrow.PickupStatus.DISALLOWED;
// entityArrow.setVelocity(0.2F, 0.1F, 0.2F);
final ItemStack magicarrow = new ItemStack(TheGalaxyMod.magicarrow, 1);
EntityLivingBase living = (playerIn);
if (FastMode.fastmode == 1){
int index = GetItemSlot(playerIn, magicarrow);
if (playerIn.inventory.hasItemStack(new ItemStack(TheGalaxyMod.creativequiver)))
{
entityArrow.setDamage(5000);
worldIn.spawnEntityInWorld(entityArrow);
}
else if (playerIn.inventory.hasItemStack(new ItemStack(TheGalaxyMod.magicquiver)))
{
worldIn.spawnEntityInWorld(entityArrow);
}
else if (!(playerIn.inventory.getStackInSlot(index).getItem() == null) && playerIn.inventory.getStackInSlot(index).getItem() == TheGalaxyMod.magicarrow)
{
damageItem(playerIn, magicarrow, 1);
worldIn.spawnEntityInWorld(entityArrow);
ItemStack stack2 = playerIn.inventory.getStackInSlot(index);
if(stack2.getItemDamage() > 500)
{
playerIn.inventory.setInventorySlotContents(index, null);
}
}
else if(playerIn.inventory.hasItemStack(ammo) ){
worldIn.spawnEntityInWorld(entityArrow);
removeItem(playerIn, ammo);
}
}
else if
(FastMode.fastmode == 2)
{
final ItemArrow itemArrow2 = (ItemArrow) (Items.ARROW instanceof ItemArrow ? Items.ARROW : Items.ARROW);
final ItemStack ammo2 = new ItemStack(Items.ARROW);
final float arrowVelocity2 = 1;
final EntityArrow entityArrow2 = itemArrow2.createArrow(worldIn, ammo2, playerIn);
entityArrow2.setAim(playerIn, playerIn.rotationPitch, playerIn.rotationYawHead, 0.0F, arrowVelocity2 * 3.5F, 0F);
entityArrow2.setDamage(2);
entityArrow2.pickupStatus = EntityArrow.PickupStatus.DISALLOWED;
int index = GetItemSlot(playerIn, magicarrow);
if (playerIn.inventory.hasItemStack(new ItemStack(TheGalaxyMod.creativequiver)))
{
entityArrow.setDamage(5000);
worldIn.spawnEntityInWorld(entityArrow);
entityArrow2.setDamage(5000);
worldIn.spawnEntityInWorld(entityArrow2);
}
else if (playerIn.inventory.hasItemStack(new ItemStack(TheGalaxyMod.magicquiver)))
{
worldIn.spawnEntityInWorld(entityArrow);
worldIn.spawnEntityInWorld(entityArrow2);
}
else if (!(playerIn.inventory.getStackInSlot(index).getItem() == null) && playerIn.inventory.getStackInSlot(index).getItem() == TheGalaxyMod.magicarrow)
{
damageItem(playerIn, magicarrow, 2);
worldIn.spawnEntityInWorld(entityArrow);
worldIn.spawnEntityInWorld(entityArrow2);
ItemStack stack2 = playerIn.inventory.getStackInSlot(index);
if(stack2.getItemDamage() > 500)
{
playerIn.inventory.setInventorySlotContents(index, null);
}
}
else if(playerIn.inventory.hasItemStack(ammo) ){
worldIn.spawnEntityInWorld(entityArrow);
worldIn.spawnEntityInWorld(entityArrow2);
removeItem(playerIn, ammo);
}
}
else if
(FastMode.fastmode == 3)
{
entityArrow.setFire(50);
int index = GetItemSlot(playerIn, magicarrow);
if (playerIn.inventory.hasItemStack(new ItemStack(TheGalaxyMod.creativequiver)))
{
entityArrow.setDamage(5000);
worldIn.spawnEntityInWorld(entityArrow);
}
else if (playerIn.inventory.hasItemStack(new ItemStack(TheGalaxyMod.magicquiver)))
{
worldIn.spawnEntityInWorld(entityArrow);
}
else if (!(playerIn.inventory.getStackInSlot(index).getItem() == null) && playerIn.inventory.getStackInSlot(index).getItem() == TheGalaxyMod.magicarrow)
{
damageItem(playerIn, magicarrow, 2);
worldIn.spawnEntityInWorld(entityArrow);
ItemStack stack2 = playerIn.inventory.getStackInSlot(index);
if(stack2.getItemDamage() > 500)
{
playerIn.inventory.setInventorySlotContents(index, null);
}
}
else if(playerIn.inventory.hasItemStack(ammo) ){
worldIn.spawnEntityInWorld(entityArrow);
removeItem(playerIn, ammo);
}
}
}
return super.onItemRightClick(stack, worldIn, playerIn, hand);
}
EDIT:
Solution Found:
Thanks to #Draco18s I have figured out the solution.
All I had to do was add the line
entityArrow.hurtResistantTime = 0
You need this:
entityHit.hurtResistantTime = 0;
Note: exact field name may change depending on Minecraft version and the build number of Forge.
Call it either just before you hurt the entity (bypassing the hurt resistance timers inflicted by all other sources of damage) or after (your damage is ignored if there's a timer, but if it inflicts damage, the next source is not ignored due to the timer). This line needs to be in the arrow class. If you don't have a custom arrow class, now's a good time to make one.
There's no (good) way to conditionally set the timer to 0 based on what the prior damage type was, unfortunately. If you are on 1.10 or newer, you could use capabilities, although possibly unwieldy.
I'm trying to make a responsive design like in Word. I used Toolbar and ToolbarSkin code and made changes to it. Now I don't know how to make it so it will check the priority of the control, and the lowest priority gets resized, while the control with the highest priority still shows full-size.
Code I changed in ToolbarSkin:
private void addNodesToToolBar() {
final Responser toolbar = getSkinnable();
double length = 0;
if (getSkinnable().getOrientation() == Orientation.VERTICAL) {
length = snapSize(toolbar.getHeight()) - snappedTopInset() - snappedBottomInset() + getSpacing();
} else {
length = snapSize(toolbar.getWidth()) - snappedLeftInset() - snappedRightInset() + getSpacing();
}
// Is there overflow ?
double x = 0;
boolean hasOverflow = false;
for (Node node : getSkinnable().getItems()) {
if (getSkinnable().getOrientation() == Orientation.VERTICAL) {
x += snapSize(node.prefHeight(-1)) + getSpacing();
} else {
x += snapSize(node.prefWidth(-1)) + getSpacing();
}
if (x > length) {
hasOverflow = true;
break;
}
}
if (hasOverflow) {
if (getSkinnable().getOrientation() == Orientation.VERTICAL) {
length -= snapSize(overflowMenu.prefHeight(-1));
} else {
length -= snapSize(overflowMenu.prefWidth(-1));
}
length -= getSpacing();
}
// Determine which node goes to the toolbar and which goes to the overflow.
x = 0;
overflowMenuItems.clear();
box.getChildren().clear();
for (Node node : getSkinnable().getItems()) {
node.getStyleClass().remove("menu-item");
node.getStyleClass().remove("custom-menu-item");
if (getSkinnable().getOrientation() == Orientation.VERTICAL) {
x += snapSize(node.prefHeight(-1)) + getSpacing();
} else {
x += snapSize(node.prefWidth(-1)) + getSpacing();
}
if (x <= length) {
box.getChildren().add(node);
} else {
if (node.isFocused()) {
if (!box.getChildren().isEmpty()) {
Node last = engine.selectLast();
if (last != null) {
last.requestFocus();
}
} else {
overflowMenu.requestFocus();
}
}
if (node instanceof Separator) {
overflowMenuItems.add(new SeparatorMenuItem());
} else {
CustomMenuItem customMenuItem = new CustomMenuItem(node);
// RT-36455:
// We can't be totally certain of all nodes, but for the
// most common nodes we can check to see whether we should
// hide the menu when the node is clicked on. The common
// case is for TextField or Slider.
// This list won't be exhaustive (there is no point really
// considering the ListView case), but it should try to
// include most common control types that find themselves
// placed in menus.
final String nodeType = node.getTypeSelector();
switch (nodeType) {
case "Button":
case "Hyperlink":
case "Label":
customMenuItem.setHideOnClick(true);
break;
case "CheckBox":
case "ChoiceBox":
case "ColorPicker":
case "ComboBox":
case "DatePicker":
case "MenuButton":
case "PasswordField":
case "RadioButton":
case "ScrollBar":
case "ScrollPane":
case "Slider":
case "SplitMenuButton":
case "SplitPane":
case "TextArea":
case "TextField":
case "ToggleButton":
case "ToolBar":
customMenuItem.setHideOnClick(false);
break;
}
overflowMenuItems.add(customMenuItem);
box.getChildren().clear();
setNameToButton(make,box.getParent().getId());
box.getChildren().add(make);
// overflowMenuItems.remove(overflowMenuItems.size()-1);
}
}
}
// Check if we overflowed.
overflow = overflowMenuItems.size() > 0;
if (!overflow && overflowMenu.isFocused()) {
Node last = engine.selectLast();
if (last != null) {
last.requestFocus();
}
}
overflowMenu.setVisible(overflow);
overflowMenu.setManaged(overflow);
}
and:
#Override protected void layoutChildren(final double x,final double y,
final double w, final double h) {
final Responser toolbar = getSkinnable();
if (toolbar.getOrientation() == Orientation.VERTICAL) {
if (snapSize(toolbar.getHeight()) != previousHeight || needsUpdate) {
((VBox)box).setSpacing(getSpacing());
((VBox)box).setAlignment(getBoxAlignment());
previousHeight = snapSize(toolbar.getHeight());
addNodesToToolBar();
}
} else {
if (snapSize(toolbar.getWidth()) != previousWidth || needsUpdate) {
((HBox)box).setSpacing(getSpacing());
((HBox)box).setAlignment(getBoxAlignment());
previousWidth = snapSize(toolbar.getWidth());
addNodesToToolBar();
}
}
needsUpdate = false;
double toolbarWidth = w;
double toolbarHeight = h;
if (getSkinnable().getOrientation() == Orientation.VERTICAL) {
toolbarHeight -= (overflow ? snapSize(overflowMenu.prefHeight(-1)) : 0);
} else {
toolbarWidth -= (overflow ? snapSize(overflowMenu.prefWidth(-1)) : 0);
}
box.resize(toolbarWidth, toolbarHeight);
positionInArea(box, x, y,
toolbarWidth, toolbarHeight, /*baseline ignored*/0, HPos.CENTER, VPos.CENTER);
// If popup menu is not null show the overflowControl
if (overflow) {
double overflowMenuWidth = snapSize(overflowMenu.prefWidth(-1));
double overflowMenuHeight = snapSize(overflowMenu.prefHeight(-1));
double overflowX = x;
double overflowY = x;
if (getSkinnable().getOrientation() == Orientation.VERTICAL) {
// This is to prevent the overflow menu from moving when there
// are no items in the toolbar.
if (toolbarWidth == 0) {
toolbarWidth = savedPrefWidth;
}
HPos pos = ((VBox)box).getAlignment().getHpos();
if (HPos.LEFT.equals(pos)) {
overflowX = x + Math.abs((toolbarWidth - overflowMenuWidth)/2);
} else if (HPos.RIGHT.equals(pos)) {
overflowX = (snapSize(toolbar.getWidth()) - snappedRightInset() - toolbarWidth) +
Math.abs((toolbarWidth - overflowMenuWidth)/2);
} else {
overflowX = x +
Math.abs((snapSize(toolbar.getWidth()) - (x) +
snappedRightInset() - overflowMenuWidth)/2);
}
overflowY = snapSize(toolbar.getHeight()) - overflowMenuHeight - y;
} else {
// This is to prevent the overflow menu from moving when there
// are no items in the toolbar.
if (toolbarHeight == 0) {
toolbarHeight = savedPrefHeight;
}
VPos pos = ((HBox)box).getAlignment().getVpos();
if (VPos.TOP.equals(pos)) {
overflowY = y +
Math.abs((toolbarHeight - overflowMenuHeight)/2);
} else if (VPos.BOTTOM.equals(pos)) {
overflowY = (snapSize(toolbar.getHeight()) - snappedBottomInset() - toolbarHeight) +
Math.abs((toolbarHeight - overflowMenuHeight)/2);
} else {
overflowY = y + Math.abs((toolbarHeight - overflowMenuHeight)/2);
}
overflowX = snapSize(toolbar.getWidth()) - overflowMenuWidth - snappedRightInset();
}
overflowMenu.resize(overflowMenuWidth, overflowMenuHeight);
positionInArea(overflowMenu, overflowX, overflowY, overflowMenuWidth, overflowMenuHeight, /*baseline ignored*/0,
HPos.CENTER, VPos.CENTER);
}
}
Also I don't know how can I implement so only one control will shrink at the time, not all of them.
Thank you for your help.
i'm stuck in a task which I could use a little help with. Basically, my job is to design a factory for wrapping presents. Our stocks are boxes with a square base (not necessarily cubes). For each box, we know the dimensions of the base of the box (I called it side) and the height (height). When the factory receives a request to wrap a present, the customer knows the side and height values of the smallest box that fits the present, but we will provide the box with the minimal volume that we have at the moment.
The idea is to plan a data structure to manage the boxes. The data structure has to support the following methods:
INSERTBOX(side, height) - adding a box to the data structure with the given dimensions
REMOVEBOX(side, height) - removing a box from the data structure with the given dimensions
GETBOX(side, height) - returning the dimensions of a box with minimal volume that fits for wrapping the present (of course, its dimension should be equal or larger than the given side and height...)
CHECKBOX(side,height) - checking whether there is a box in the storage that fits the present.
The parameters of the entire problem are the number of side values, m, and the number of height values, n.
I was thinking of a red-black tree that uses the side as the key, every node also holds a red-black tree in which all sides are equal and the height is now the key.
What I got stuck with is implenting the GetBox method, I just can't figure out a better way than checking every single node for the smallest volume in which the side and height are equal or larger than those who were entered by the user.
MAIN PROGRAM
import java.util.Scanner;
class ProgramBody
{
public static BoxesRedBlackTree sideTree = new BoxesRedBlackTree();
public static void Main()
{
final int INSERTBOX = 1;
final int REMOVEBOX = 2;
final int GETBOX = 3;
final int CHECKBOX = 4;
final int EXIT = 5;
System.out.println("Welcome to the factory! What would you like to do?");
System.out.println("Insert a box - " + INSERTBOX);
System.out.println("Remove a box - " + REMOVEBOX);
System.out.println("Get a box - " + GETBOX);
System.out.println("Check if a box exists - " + CHECKBOX);
System.out.println("Exit - " + EXIT);
System.out.println("Please enter your choice: ");
Scanner in = new Scanner(System.in);
int Choise = in.nextInt();
// While the user doesn't exit the program
while (Choise != EXIT)
{
switch (Choise)
{
case (INSERTBOX):
{
System.out.println("Enter the size of the side");
int side = in.nextInt();
System.out.println("Enter the size of the height");
int height = in.nextInt();
InsertBox(side, height);
break;
}
case (REMOVEBOX):
{
System.out.println("Enter the size of the side");
int side = in.nextInt();
System.out.println("Enter the size of the height");
int height = in.nextInt();
RemoveBox(side, height);
break;
}
case (GETBOX):
{
System.out.println("Enter the size of the side");
int side = in.nextInt();
System.out.println("Enter the size of the height");
int height = in.nextInt();
GetBox(side, height);
break;
}
case (CHECKBOX):
{
System.out.println("Enter the size of the side");
int side = in.nextInt();
System.out.println("Enter the size of the height");
int height = in.nextInt();
boolean boxExists;
if (CheckBox(side, height))
{ System.out.println("The box exists."); }
else
{ System.out.println("The box doesn't exist."); }
break;
}
default:
{ System.out.println("You have entered an unfamiliar choice. Please try again."); }
}
System.out.println("\nWhat would you like to do now?");
System.out.println("Insert a box - " + INSERTBOX);
System.out.println("Remove a box - " + REMOVEBOX);
System.out.println("Get a box - " + GETBOX);
System.out.println("Check if a box exists - " + CHECKBOX);
System.out.println("Exit - " + EXIT);
System.out.println("Please enter your choice: ");
Choise = in.nextInt();
}
}
private static void InsertBox(int side, int height) {
if (sideTree.sideExist(side))
{
//Check if There is same box
if(sideTree.searchBySide(side).getTree().heightExist(height))
{ sideTree.searchBySide(side).getTree().searchByHeight(height).setCounter(sideTree.searchBySide(side).getTree().searchByHeight(height).getCounter()+1); }
else
{
BoxNode temp = new BoxNode(side, height, 1);
sideTree.searchBySide(side).getTree().insert(temp);
}
}
else
{
BoxNode heightTempNode = new BoxNode(side, height, 1);
BoxesRedBlackTree tempTree = new BoxesRedBlackTree();
tempTree.insert(heightTempNode);
BoxNode sideTempNode = new BoxNode(side, height, tempTree);
sideTree.insert(sideTempNode);
}
System.out.println("The box with side " + side + " and height " + height + " has been added.");
}
private static void RemoveBox(int side, int height) {
if(sideTree.sideExist(side) && sideTree.searchBySide(side).getTree().heightExist(height)) {
if (sideTree.searchBySide(side).getTree().searchByHeight(height).getCounter() > 1)
{
sideTree.searchBySide(side).getTree().searchByHeight(height).setCounter(sideTree.searchBySide(side).getTree().searchByHeight(height).getCounter()-1);
System.out.println("The box with side " + side + " and height " + height + " has been removed.");
}
else
{
sideTree.searchBySide(side).getTree().delete(sideTree.searchBySide(side).getTree().searchByHeight(height));
System.out.println("The box with side " + side + " and height " + height + " has been removed.");
}
if (!sideTree.searchBySide(side).getTree().sideExist(side))
{
sideTree.delete(sideTree.searchBySide(side));
} }
else
{ System.out.println("There isn't a box with the requested size!"); }
}
private static BoxNode GetBox(int side, int height) {
BoxNode currentNode = sideTree.getMin(sideTree.getRoot());
BoxNode minimalBox = sideTree.getRoot();
// Check all The Tree
while (currentNode != null)
{ if(currentNode.getSide() >= side && currentNode.getHeight() >= height && currentNode.getVolume() < minimalBox.getVolume()) { minimalBox = currentNode; }}
currentNode = sideTree.getSuccessor(currentNode);
if (minimalBox != null)
{ System.out.println("The box with side " + minimalBox.getSide() + " and height " + minimalBox.getSide() + " has returned."); }
else
{ System.out.println("No box was found."); }
return (minimalBox);
}
private static boolean CheckBox(int side, int height) {
if ((GetBox(side,height)) != null)
{ return true; }
return false; }
}
RED BLACK TREE
public class BoxesRedBlackTree
{
private BoxNode _root;
/**
* Constructor
*/
public BoxesRedBlackTree ()
{
_root = null;
}
/**
* Reset root
*/
public BoxesRedBlackTree(BoxNode box)
{
_root = new BoxNode(box);
}
/**
* get -root method
*/
public BoxNode getRoot()
{
return _root;
}
/**
* LEFT-ROTATE algorithm
*/
public void leftRotate(BoxNode x)
{
BoxNode y = x.getRightSon();
x.setRightSon(y.getLeftSon());
if(y.getLeftSon() != null)
{
y.getLeftSon().setParent(x);
}
y.setParent(x.getParent());
if(x.getParent() == null)
{
_root = y;
}
else if( x == x.getParent().getLeftSon())
{
x.getParent().setLeftSon(y);
}
else
{
x.getParent().setRightSon(y);
}
y.setLeftSon(x);
x.setParent(y);
}
/**
* RIGHT-ROTATE algorithm
* Assumes right son of x is not null.
*/
public void rightRotate(BoxNode x)
{ //assumes left son is not null
BoxNode y = x.getLeftSon();
x.setLeftSon(y.getRightSon());
if(y.getRightSon() != null)
{
y.getRightSon().setParent(x);
}
y.setParent(x.getParent());
if(x.getParent() == null)
{
_root = y;
}
else if( x == x.getParent().getRightSon())
{
x.getParent().setRightSon(y);
}
else
{
x.getParent().setLeftSon(y);
}
y.setRightSon(x);
x.setParent(y);
}
/**
* RB-INSERT algorithm
*/
public void insert(BoxNode toInsert)
{
BoxNode x = _root;
BoxNode y = null;
while(x != null){
y = x;
if(toInsert.isBigger(x))
{
x = x.getLeftSon();
}
else
{
x = x.getRightSon();
}
}
toInsert.setParent(y);
if(y == null)
{
_root = toInsert;
}
else if(toInsert.isBigger(y))
{
y.setLeftSon(toInsert);
}
else
{
y.setRightSon(toInsert);
}
toInsert.setLeftSon(null);
toInsert.setRightSon(null);
toInsert.setColor(1); // red
insertFixUp(toInsert);
}
/**
* RB-INSERT-FIXUP algorithm
*/
private void insertFixUp(BoxNode z)
{
BoxNode y = null;
while (z != _root && z.getParent().getColor() == 1)
{
if(z.getParent() == z.getParent().getParent().getLeftSon())
{
y = z.getParent().getParent().getRightSon();
if(y != null && y.getColor()== 1)
{
z.getParent().setColor(0);
y.setColor(0);
z = z.getParent().getParent();
}
else
{
if (z == z.getParent().getRightSon())
{
z = z.getParent();
this.leftRotate(z);
}
z.getParent().setColor(0);
z.getParent().getParent().setColor(1);
rightRotate(z.getParent().getParent());
}
}
else
{
y = z.getParent().getParent().getLeftSon();
if(y != null && y.getColor() == 1)
{
z.getParent().setColor(0);
y.setColor(0);
z = z.getParent().getParent();
}
else
{
if (z == z.getParent().getLeftSon()){
z = z.getParent();
this.rightRotate(z);
}
z.getParent().setColor(0);
z.getParent().getParent().setColor(1);
this.leftRotate(z.getParent().getParent());
}
}
}
_root.setColor(0);
}
/**
* RB-DELETE algorithm
*/
public void delete(BoxNode z)
{
BoxNode y = null;
BoxNode x = null;
if(z.getLeftSon() == null || z.getRightSon() == null)
{
y = z;
}
else
{
y = getSuccessor(z);
}
if(y.getLeftSon() != null)
{
x = y.getLeftSon();
}
else
{
x=y.getRightSon();
}
if (x != null && y != null)
{
x.setParent(y.getParent());
}
if(y.getParent() == null)
{
_root = x;
}
else if(y == y.getParent().getLeftSon())
{
y.getParent().setLeftSon(x);
}
else
{
y.getParent().setRightSon(x);
}
if(y != z)
{
z.setSide(y.getSide());
}
if(y.isBlack())
{
deleteFixUp(x);
}
}
/**
* RB-DELETE-FIXUP algorithm
*/
private void deleteFixUp(BoxNode x)
{
BoxNode temp = null;
while (x != null && x != _root && x.isBlack())
{
if (x == x.getParent().getLeftSon())
{
temp = x.getParent().getRightSon();
if (!temp.isBlack())
{
temp.setColor(0);
x.getParent().setColor(1);
leftRotate(x.getParent());
temp = x.getParent().getRightSon();
}
if (temp.getLeftSon().isBlack() && temp.getRightSon().isBlack() )
{
temp.setColor(1);
x = x.getParent();
}
else
{
if (temp.getRightSon().isBlack())
{
temp.getLeftSon().setColor(0);
temp.setColor(1);
rightRotate (temp);
temp = x.getParent().getRightSon();
}
temp.setColor(x.getParent().getColor());
x.getParent().setColor(0);
temp.getRightSon().setColor(0);
leftRotate (x.getParent());
x = _root;
}
}
else
{
temp = x.getParent().getLeftSon();
if (!temp.isBlack())
{
temp.setColor(0);
x.getParent().setColor(1);
rightRotate(x.getParent());
temp = x.getParent().getLeftSon();
}
if (temp.getRightSon().isBlack() && temp.getLeftSon().isBlack())
{
temp.setColor(1);
x = x.getParent();
}
else
{
if (temp.getLeftSon().isBlack())
{
temp.getRightSon().setColor(0);
temp.setColor(1);
leftRotate (temp);
temp = x.getParent().getLeftSon();
}
temp.setColor(x.getParent().getColor());
x.getParent().setColor(0);
temp.getLeftSon().setColor(0);
rightRotate (x.getParent());
x = _root;
}
}
x.setColor(0);
}
}
/**
* returns the successor BoxNode of a certain BoxNodeFrom.
*/
public BoxNode getSuccessor(BoxNode BoxNodeFrom)
{
if(BoxNodeFrom.getRightSon() != null)
{
return getMin(BoxNodeFrom.getRightSon());
}
BoxNode temp = BoxNodeFrom.getParent();
while ((temp != null) && (BoxNodeFrom == temp.getRightSon()))
{
BoxNodeFrom = temp;
temp = temp.getParent();
}
return temp;
}
/**
* Get the minimum Valued BoxNode from a given subtree
*/
public BoxNode getMin(BoxNode BoxNodeFrom)
{
while (BoxNodeFrom.getLeftSon() != null)
{
BoxNodeFrom = BoxNodeFrom.getLeftSon();
}
return BoxNodeFrom;
}
/**
* Get the maximum Valued BoxNode from a given subtree
*/
public BoxNode getMax(BoxNode BoxNodeFrom)
{
while (BoxNodeFrom.getRightSon() != null)
{
BoxNodeFrom = BoxNodeFrom.getRightSon();
}
return BoxNodeFrom;
}
/**
* search height in the Tree
* return the BoxNode Node if it exict, and NULL otherwise
*/
public BoxNode searchByHeight(int boxCode)
{
BoxNode temp = _root;
while (temp != null)
{
if (temp.getHeight() > boxCode)
{
temp = temp.getRightSon();
}
else if (temp.getHeight() < boxCode)
{
temp = temp.getLeftSon();
}
else
{
return temp;
}
}
return null;
}
/**
* check if there is a height
*/
public boolean heightExist(int height)
{
BoxNode temp = searchByHeight(height);
if(temp == null)
{ return false; }
return true;
}
/**
* search side in the Tree
* return the BoxNode Node if it exict, and NULL otherwise
*/
public BoxNode searchBySide(int boxCode)
{
BoxNode temp = _root;
while (temp != null)
{
if (temp.getSide() > boxCode)
{
temp = temp.getRightSon();
}
else if (temp.getSide() < boxCode)
{
temp = temp.getLeftSon();
}
else
{
return temp;
}
}
return null;
}
/**
* check if there is a side
*/
public boolean sideExist(int side)
{
BoxNode temp = searchBySide(side);
if(temp == null)
{ return false; }
return true;
}
}
NODE
public class BoxNode
{
private int color=0; // black is 0 red is 1.
private BoxNode rightSon, leftSon, parent;
private int _side;
private int _height;
private int _counter;
private BoxesRedBlackTree _pointer;
/**
* Constructor
*/
public BoxNode(int side,int height)
{
_side = side;
_height = height;
}
public BoxNode(int side,int height,int counter)
{
_side = side;
_height = height;
_counter = counter;
}
public BoxNode(int side,int height, BoxesRedBlackTree pointer)
{
_side = side;
_height = height;
_pointer = pointer;
}
/**
* Copy Constructor
*/
public BoxNode(BoxNode box)
{
_side = box._side;
_height = box._height;
}
//get Method
/**
* get the height of the node
*/
public int getHeight()
{
return _height;
}
/**
* get the side of the box
*/
public int getSide()
{
return _side;
}
public int getCounter()
{
return _counter;
}
public BoxesRedBlackTree getTree()
{
return _pointer;
}
/**
* get the volume of the box
*/
public int getVolume()
{
return (_side)*(_side)*(_height);
}
public BoxNode getRightSon()
{
return rightSon;
}
public BoxNode getLeftSon()
{
return leftSon;
}
public BoxNode getParent()
{
return parent;
}
public int getColor()
{
return color;
}
//set Method
/**
* set the side
*/
public void setSide(int side)
{
_side = side;
}
/**
* set the height
*/
public void setHeight(int height)
{
_height = height;
}
public void setCounter(int counter)
{
_counter = counter;
}
public void setTree(BoxesRedBlackTree pointer)
{
_pointer = pointer;
}
public void setRightSon(BoxNode toSet)
{
rightSon = toSet;
}
public void setLeftSon(BoxNode toSet)
{
leftSon = toSet;
}
public void setParent(BoxNode toSet )
{
parent = toSet;
}
public void setColor(int toSet)
{
if(toSet == 1 || toSet == 0)
{
color = toSet;
}
}
public boolean isBigger(BoxNode other)
{
if(_side > other._side)
{
return true;
}
return false;
}
public boolean isBlack()
{ return true; }
}
Have you considered using the volume of the box as the key? In your getBox method, you can get the box with the smallest volume that is greater than or equal to side^2*height. Then, check if it can fit the given side and height. If not, try the next bigger one. This can perform decently, depending on the sizes of your boxes and presents.
If you're really bent on nested red-black trees, you can do th following:
1) Find the sideNode with the minimum side that's greater than or equal to the given side.
2) Find the heightNode with the minimum height that's greater than or equal to the given height. Hold it in a variable named minVolumeBox.
3) Get the next sideNode. Keep doing this until
a) You check all the available sideNodes or
b) The side of the sideNode is bigger than (volume of minVolumeBox / given height).
Case 3b will save you some time over checking every single node.