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.
Related
I have an A* pathfinding algorithm that I've used for a Spigot plugin which worked fine. I then added a requirements system so it won't try and pathfind through places it shouldn't. Now it seems REALLY slow, and it looks like it has nothing to do with the requirements code itself, and more to do with the algorithm having many more incorrect paths when calculating. I seem to be getting 1500ms+ on this, which is definitely not good xD
Here is the pathfinder code:
public Path calculate(PathfinderGoal goal, PathScorer scorer, List<PathRequirement> requirements, int maxNodes) {
PathNode start = toNode(npc.getLocation());
PathNode end = toNode(goal.getLocation());
List<PathNode> open = new ArrayList<>() {{ add(start); }};
List<PathNode> navigated = new ArrayList<>();
start.setF(scorer.computeCost(start, end));
Timer timer = new Timer().start();
while (!open.isEmpty()) {
PathNode current = null;
for (PathNode node : open) {
if (current == null || node.getH() < current.getH()) {
current = node;
}
}
if (scorer.computeCost(current, end) < 1 || (navigated.size() >= maxNodes && maxNodes != -1)) {
navigated.add(navigated.size() < maxNodes ? end : current);
return reconstruct(navigated, navigated.size() - 1);
}
open.remove(current);
current.close();
for (PathNode node : current.getNeighbors()) {
if (node.isClosed()) {
continue;
}
double tentG = current.getG() + scorer.computeCost(current, node);
if (!open.contains(node) || tentG < node.getG()) {
boolean requirementsMet = true;
for (PathRequirement requirement : requirements) {
requirement.setNavigated(navigated);
if (!navigated.isEmpty() && !requirement.canMoveToNewNode(navigated.get(navigated.size() - 1), node)) {
requirementsMet = false;
break;
}
}
if (!navigated.contains(current)) {
navigated.add(current);
}
node.setG(tentG);
node.setH(scorer.computeCost(node, end));
node.setF(tentG + node.getH());
if (!open.contains(node) && requirementsMet) {
open.add(node);
}
}
}
Bukkit.broadcastMessage("Open Set Size: " + open.size());
Bukkit.broadcastMessage(timer.stop() + "ms");
}
return null;
}
private Path reconstruct(List<PathNode> navigated, int index) {
final PathNode current = navigated.get(index);
Path withCurrent = new Path(new ArrayList<>() {{ add(current); }});
if (index > 0 && navigated.contains(current)) {
return reconstruct(navigated, index - 1).append(withCurrent);
}
return withCurrent;
}
And here is the PathNode class:
public PathNode(Pathfinder pathfinder, int x, int y, int z) {
this.pathfinder = pathfinder;
this.x = x;
this.y = y;
this.z = z;
}
#Override
public boolean equals(Object other) {
if (!(other instanceof PathNode otherNode)) {
return false;
}
return otherNode.x == x && otherNode.y == y && otherNode.z == z;
}
public List<PathNode> getNeighbors() {
return new ArrayList<>() {
{
for (int x = -1; x <= 1; x++) {
for (int y = -1; y <= 1; y++) {
for (int z = -1; z <= 1; z++) {
add(new PathNode(pathfinder, PathNode.this.x + x, PathNode.this.y + y, PathNode.this.z + z));
}
}
}
}
};
}
public Location getLocation() {
return new Location(pathfinder.getNPC().getLocation().getWorld(), x, y, z);
}
public double getF() {
return F;
}
public void setF(double f) {
this.F = f;
}
public double getG() {
return G;
}
public void setG(double g) {
this.G = g;
}
public double getH() {
return H;
}
public void setH(double h) {
this.H = h;
}
public boolean isClosed() {
return closed;
}
public void close() {
this.closed = true;
}
Valid Requirements Class:
public class ValidPathRequirement extends PathRequirement {
#Override
public boolean canMoveToNewNode(PathNode from, PathNode to) {
Block fromBlock = from.getLocation().getBlock();
Block toBlock = to.getLocation().getBlock();
boolean validHeight = toBlock.getType().isAir() && toBlock.getRelative(BlockFace.UP).getType().isAir(); // checks if is player height
boolean validGround = toBlock.getRelative(BlockFace.DOWN).getType().isSolid(); // is there a block underneath that they can stand on?
boolean validFromPrev = toBlock.getLocation().subtract(fromBlock.getLocation()).getY() <= 1; // is it max one block higher than the last one?
// is this one causing issues?
Location fromLocDist = from.getLocation().clone();
Location toLocDist = to.getLocation().clone();
toLocDist.setY(fromLocDist.getY());
boolean validDistance = fromLocDist.distance(toLocDist) <= 1;
return validHeight && validGround && validFromPrev;
}
}
Without looking at the rest of the algorithm, the first thing that stands out is that your data structures are incorrect. The "open" list needs to be a Priority Queue, and "closed" (or "navigated") should be a set.
Basically, this code works as I need it to, perfectly fine, however, If I move even the slightest bit or get out of the water, my console gets SPAMMED with NPE's... What have I done wrong with my code? What I'm trying to do is, if a player is in a specific location, inside of water, it will start a countdown, in this case, of 5 seconds. And after the 5 seconds, it's meant to say "Testing", etc. That part works, just not when I move or step out of the water. And yes, I'm aware that the code is sloppy and a bunch of junk but I don't want to create a whole class for cuboid for just one event.
private HashMap<UUID, Integer> afkCountdown;
private HashMap<UUID, BukkitRunnable> afkCountdownTask;
#EventHandler
public void onPlayerMoveEvent(PlayerMoveEvent e) {
afkCountdown = new HashMap<UUID, Integer>();
afkCountdownTask = new HashMap<UUID, BukkitRunnable>();
Player p = e.getPlayer();
int x = 52;
int y = 80;
int z = 255;
int x1 = 72;
int y1 = 100;
int z1 = 275;
Location l1 = new Location(Bukkit.getWorld("Void"), x, y, z);
Location l2 = new Location(Bukkit.getWorld("Void"), x1, y1, z1);
Location loc = new Location(Bukkit.getWorld("Void"), p.getLocation().getBlockX(), p.getLocation().getBlockY(), p.getLocation().getBlockZ());
if (p.getWorld().equals(loc.getWorld())) {
if (Objects.requireNonNull(e.getTo()).getBlock().isLiquid()) {
if (loc.getX() > l1.getX() && loc.getX() < l2.getX()) {
if (loc.getY() > l1.getY() && loc.getY() < l2.getY()) {
if (loc.getZ() > l1.getZ() && loc.getZ() < l2.getZ()) {
if (!afkCountdown.containsKey(p.getUniqueId())) {
afkCountdown.put(p.getUniqueId(), 5);
afkCountdownTask.put(p.getUniqueId(), new BukkitRunnable() {
#Override
public void run() {
afkCountdown.put(p.getUniqueId(), afkCountdown.get(p.getUniqueId()) - 1);
if (afkCountdown.get(p.getUniqueId()) == 0) {
p.sendMessage("Testing");
afkCountdown.remove(p.getUniqueId());
afkCountdownTask.remove(p.getUniqueId());
afkCountdown.put(p.getUniqueId(), 5);
} else if (!afkCountdown.containsKey(p.getUniqueId())) {
cancel();
}
}
});
afkCountdownTask.get(p.getUniqueId()).runTaskTimer(plugin, 20, 20);
} else {
return;
}
} else {
if (afkCountdown.containsKey(p.getUniqueId())) {
afkCountdown.remove(p.getUniqueId());
afkCountdownTask.remove(p.getUniqueId());
} else {
return;
}
}
} else {
if (afkCountdown.containsKey(p.getUniqueId())) {
afkCountdown.remove(p.getUniqueId());
afkCountdownTask.remove(p.getUniqueId());
} else {
return;
}
}
} else {
if (afkCountdown.containsKey(p.getUniqueId())) {
afkCountdown.remove(p.getUniqueId());
afkCountdownTask.remove(p.getUniqueId());
} else {
return;
}
}
} else {
if (afkCountdown.containsKey(p.getUniqueId())) {
afkCountdown.remove(p.getUniqueId());
afkCountdownTask.remove(p.getUniqueId());
} else {
return;
}
}
} else {
if (afkCountdown.containsKey(p.getUniqueId())) {
afkCountdown.remove(p.getUniqueId());
afkCountdownTask.remove(p.getUniqueId());
} else {
return;
}
}
}
I have multiple things to say about this code :
The value of e.getTo() will never be null. So the Objects.requireNonNull code is a useless import.
You get multiple time the same world instance with Bukkit.getWorld("Void"), but only one time is better.
Instead of create Location's object, you can check directly the x/y/z values that are given just before.
For the issue :
When you left liquid, all task will remove the value linked with the player's uuid. So, you will not be able to get id with afkCountdown.get() or afkCountdownTask.get(). To fix it, you should use afkCountdown.getOrDefault(p.getUniqueId(), 0).
For timers, you never stop already started timer. So, your server with crash because of overflow. When you do afkCountdownTask.remove() you should do :
BukkitRunnable task = afkCountdownTask.remove(p.getUniqueId()); // return the removed one, or null if nothing removed
if(task != null) // if was existing
task.cancel(); // cancel task
You are creating and resetting hashmap EACH move with afkCountdown = new HashMap<UUID, Integer>(). You should do it only one time.
Finally, this is the code that I propose to fix everything :
private final HashMap<UUID, Integer> afkCountdown = new HashMap<>();
private final HashMap<UUID, BukkitRunnable> afkCountdownTask = new HashMap<>();
#EventHandler
public void onPlayerMoveEvent(PlayerMoveEvent e) {
Player p = e.getPlayer();
int x = 52;
int y = 80;
int z = 255;
int x1 = 72;
int y1 = 100;
int z1 = 275;
Location loc = e.getTo();
if (p.getWorld().getName().equals("Void")) {
if (loc.getBlock().isLiquid()) {
if (loc.getX() > x && loc.getX() < x1) {
if (loc.getY() > y && loc.getY() < y1) {
if (loc.getZ() > z && loc.getZ() < z1) {
if (!afkCountdown.containsKey(p.getUniqueId())) {
afkCountdown.put(p.getUniqueId(), 5);
afkCountdownTask.put(p.getUniqueId(), new BukkitRunnable() {
#Override
public void run() {
int amount = afkCountdown.getOrDefault(p.getUniqueId(), 0) - 1;
afkCountdown.put(p.getUniqueId(), amount);
if (amount <= 0) {
p.sendMessage("Testing");
afkCountdown.put(p.getUniqueId(), 5);
} else if (!afkCountdown.containsKey(p.getUniqueId())) {
cancel();
}
}
});
afkCountdownTask.get(p.getUniqueId()).runTaskTimer(Main.getInstance(), 20, 20);
}
return; // don't want to remove, so end method now
}
}
}
}
}
if (afkCountdown.containsKey(p.getUniqueId())) {
afkCountdown.remove(p.getUniqueId());
BukkitRunnable task = afkCountdownTask.remove(p.getUniqueId());
if(task != null)
task.cancel();
}
}
Hi I am implementing an A* search algorithm based on WikiLink but I am not getting an answer from my search at all. any hints and/or help on what I may be doing wrong
I get a map that contains dirt and obstacles, I need to travel to all dirt particles and hoover them up and return to my starting point
Search Function
private void search_Astar(State start){ // we have confirmed a valid start state
// needed vars
Map<State, Integer> g_score = new HashMap<>();
Map<State, Integer> f_score = new HashMap<>();
Queue<State> openQueue = new PriorityQueue<State>(((State o1, State o2) -> f_score.get(o1) - f_score.get(o2)));
HashSet<State> closedSet = new HashSet<>();
// inits
g_score.put(start, 0);
f_score.put(start, start.heuristic());
openQueue.add(start);
// search
while (!openQueue.isEmpty()){
State current = openQueue.poll();
if (current.isGoal()){
// create path
solutionStack.push(Actions.Turn_Off);
while (current.takenAction != null){
solutionStack.push(current.takenAction);
current = current.parent;
}
return;
}
closedSet.add(current);
for (String action:current.legalActions()){
State child = current.CreateState(action);
if (closedSet.contains(child)) continue;
int tentativeG = g_score.get(current) + 1; // the cost to take an action
if (!openQueue.contains(child)) openQueue.add(child);
else if (tentativeG >= g_score.getOrDefault(child, Integer.MAX_VALUE)) continue;
g_score.put(child, tentativeG);
f_score.put(child, tentativeG + child.heuristic());
}
}
}
and State class (sorry for huge-ness)
import java.util.ArrayList;
import java.util.Collection;
public class State {
// keep track of position and orientation
public Orientation orientation;
public Position position;
public boolean turned_on;
// track world state
private Collection<Position> dirt;
private Collection<Position> obstacles;
private Position size;
// variables for detirmining goal state and heuristic
private Position home;
// for calculating G
public State parent;
public String takenAction;
public State(Position position, Orientation orientation, boolean turned_on, Collection dirt, Collection obs, Position home,
Position size, State parent, String action) {
this.position = position;
this.orientation = orientation;
this.turned_on = turned_on;
this.dirt = dirt;
this.obstacles = obs;
this.home = home;
this.size = size;
this.parent = parent;
this.takenAction = action;
}
public int G(){
State curr = parent;
int total = 0;
while (curr != null){
total += 1;
curr = parent.parent;
}
return total;
}
public String toString() {
return "State{position: " + position + ", orientation: " + orientation + ", on:" + turned_on + "}";
}
#Override
public int hashCode(){
int hashVal = 23;
hashVal = ((hashVal + position.x) << 5) - (hashVal + position.x);
hashVal = ((hashVal + position.y) << 5) - (hashVal + position.y);
if(turned_on){
hashVal += 1243;
}
if (orientation == Orientation.NORTH){
hashVal += 12;
}else if (orientation == Orientation.EAST){
hashVal += 2234;
}else if (orientation == Orientation.WEST){
hashVal += 32345;
}
return hashVal + dirt.size();
}
#Override
public boolean equals(Object o){
if (o instanceof State){
State other = (State)o;
if(!other.position.equals(this.position)){
return false;
}else if( other.turned_on != this.turned_on){
return false;
}else if( other.orientation != this.orientation){
return false;
}else if (other.dirt.size() != this.dirt.size()){
return false;
} else{
return true;
}
}else {
return false;
}
}
/**
* check to see if current node is a valid goal node
*
* #return true if valid, false otherwise
*/
public Boolean isGoal(){
return dirt.isEmpty() && position.equals(home);
}
/**
* Calculate all legal moves from current state
*
* #return Collection of legal actions (as string)
*/
public Collection<String> legalActions(){
Collection<String> actions = new ArrayList<String>();
if (!turned_on){
actions.add(Actions.Turn_On);
}else{
// no reason we couldn't turn
actions.add(Actions.Turn_Left);
actions.add(Actions.Turn_Right);
if (dirt.contains(position)){
actions.add(Actions.Suck);
}
// check if we can move forward
// ATTENTION this must be at bottom of function
if (orientation == Orientation.NORTH){
for (Position o: obstacles) {
if (o.y == position.y + 1 && o.x == position.x || position.y == size.y){
return actions;
}
}
// we have checked every obstacle and we would not collide
actions.add(Actions.Go);
}else if (orientation == Orientation.SOUTH){
for (Position o: obstacles) {
if (o.y == position.y - 1 && o.x == position.x || position.y == 1){
return actions;
}
}
actions.add(Actions.Go);
}else if (orientation == Orientation.WEST){
for (Position o: obstacles) {
if (o.x == position.x - 1 && o.y == position.y|| position.x == 1){
return actions;
}
}
actions.add(Actions.Go);
}else {
// we are pointing east
for (Position o: obstacles) {
if (o.x == position.x + 1 && o.y == position.y|| position.x == size.x){
return actions;
}
}
actions.add(Actions.Go);
}
}
return actions;
}
/**
* return a value to determine how optimal this state is
*
* evaluation methood: number of dirt left times a constant
* which is the added to the distance to the closest dirt
* if there are no dirts left it is the mannhatan distance to home
*
* note: should add state depth?
* #return int
*/
public int heuristic(){
int h = 0;
for (Position p:obstacles){
h += mannhatandist(p);
}
h += mannhatandist(home);
return h + dirt.size();
}
private int mannhatandist(Position p){
return Math.abs(p.x - position.x) + Math.abs(p.y - position.y);
}
private int distToClosest(){
int min = Integer.MAX_VALUE;
if (dirt.isEmpty()){
int dist = Math.abs(home.x - position.x) + Math.abs(home.y - position.y);
return dist;
}
for (Position p: dirt) {
int dist = Math.abs(p.x - position.x) + Math.abs(p.y - position.y);
min = Math.min(min, dist);
}
if (!turned_on) min++;
return min;
}
public State CreateState(String action) {
//Copy the "old" values from the current state.
//Before any action done, the "new" state has the same values.
Position new_position = new Position(position.x, position.y);
Orientation new_orientation = orientation;
boolean new_turned_on = turned_on;
Collection new_dirt = new ArrayList(dirt);
State new_parent = this;
//If the action is to turn the robot on, the turned_on variable for the new state takes the value "true".
if(action == "TURN_ON") {
new_turned_on = true;
}
//If the action is to turn the robot on, the turned_on variable for the new state takes the value "true".
else if(action == "TURN_OFF") {
new_turned_on = false;
}
//If the action is to suck, the current position needs to be taken out of the new state's dirt collection.
else if(action == "SUCK") {
new_dirt.remove(position);
}
//If the action is to go it depends on the orientation of the robot how the position of the new state will change.
else if(action == "GO") {
//If it's facing north, the y position increases by one, considering the old state and so on.
if(orientation == Orientation.NORTH) {
new_position.y++;
}
else if(orientation == Orientation.EAST) {
new_position.x++;
}
else if(orientation == Orientation.SOUTH) {
new_position.y--;
}
else {
new_position.x--;
}
}
//If the action is to turn left it depends on the orientation of the robot in the current state what the new orientation will be.
else if(action == "TURN_LEFT") {
if(orientation == Orientation.NORTH) {
new_orientation = Orientation.WEST;
}
else if(orientation == Orientation.EAST) {
new_orientation = Orientation.NORTH;
}
else if(orientation == Orientation.SOUTH) {
new_orientation = Orientation.EAST;
}
else {
new_orientation = Orientation.SOUTH;
}
}
//If the action is to turn right it depends on the orientation of the robot in the current state what the new orientation will be.
else if(action == "TURN_RIGHT") {
if(orientation == Orientation.NORTH) {
new_orientation = Orientation.EAST;
}
else if(orientation == Orientation.EAST) {
new_orientation = Orientation.SOUTH;
}
else if(orientation == Orientation.SOUTH) {
new_orientation = Orientation.WEST;
}
else {
new_orientation = Orientation.NORTH;
}
}
//Make a new state from the new variables that have been changed according to the action done.
State new_state = new State(new_position, new_orientation, new_turned_on, new_dirt, obstacles, home, size, new_parent, action);
return new_state;
}
}
I have Fixed it now. the problem was that my heuristic was not consistent. I have modified it and it is performing correctly now
I will not share my answer since this was for a school project and since the automatic anti-cheating system at school will probably detect this I will add my school username to this comment (dagur13)
p.s. I will post my updated code when I have recieved a grade for the project
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.
I'm trying to get the location of the user's finger touch and detect if it is within the bounds of an image view. I'm getting all the right integers returned to me, there is no problem with the values. The problem I'm getting is when trying to detect whether returnX is greater or less than the imageview X int.
ImageView img;
float eventX;
float eventY;
float x,y,x2,y2;
String TAG = "imgPos";
String TAG2 = "downcheck";
String TAG3 = "bounds";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
img = (ImageView)findViewById(R.id.imageView1);
}
public void fingerDownLoop(){
if (SingleTouchEventView.userDown == true){
Log.e(TAG2, "Down is true");
Thread myThread = new Thread(myRunnable);
myThread.start();
} else if (SingleTouchEventView.userDown == false){
Log.e(TAG2, "Down is false");
}
}
public void getFingerLocation(){
float returnX = SingleTouchEventView.eventX;
float returnY = SingleTouchEventView.eventY;
Log.e(TAG3, " - X -");
Log.e(TAG3, " getX: " + returnX);
Log.e(TAG3, " - Y -");
Log.e(TAG3, " getY: " + returnY);
if (returnX < x){
Log.e(TAG3, "Outside bounds");
}
if (returnX > x){
Log.e(TAG3, "Inside bounds");
}
}
Runnable myRunnable = new Runnable() {
#Override
public void run() {
while (SingleTouchEventView.userDown == true) {
try {
Log.e(TAG2, "FingerDown");
getFingerLocation();
x = img.getLeft();
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} // Waits for 1 second (1000 milliseconds)
}
}
};
}
testText is a button click, it gets the imageviews position and stores it as the values. The problem I'm getting is here:
if (returnX < x){
Log.e(TAG3, "Outside bounds");
}
if (returnX > x){
Log.e(TAG3, "Inside bounds");
}
It is all ways showing as Inside bounds even though returnX is less than X. Any ideas?
Updated get img position.
int[] img_coordinates = new int[2];
img.getLocationOnScreen(img_coordinates);
double x_center = (double)img_coordinates[0] + img.getWidth()/2.0;
double y_center = (double)img_coordinates[1] + img.getHeight()/2.0;
int halfwidth = 150 / 2;
int halfheight = 150 / 2;
yboundtop = y_center - halfheight;
yboundbottom = y_center + halfheight;
xboundleft = x_center - halfwidth;
xboundright = x_center + halfwidth;
float returnX = eventX;
float returnY = eventY;
if (returnX < xboundleft || returnX > xboundright || returnY < yboundtop || returnY > yboundbottom) {
Log.e(TAG3, "Outside bounds");
}
if (returnX > xboundleft && returnX < xboundright && returnY > yboundtop && returnY < yboundbottom) {
Log.e(TAG3, "Inside bounds");
}
I'am not sure what you want to accomplish by your code, but checking in a loop is not very efficient and in this case probably not necessary.
Your imageView has a method getHitRect() and you could use it like this:
Rect hitRect = new Rect();
imageView.getHitRect(hitRect);
if(hitRect.contains(touchEvent.getX(), touchEvent.getY())){
// your touch is inside, do something useful
}
Use it inside your touch-method
Now that I kind of know what you are looking for I think I may be able to help. Override the onTouchEvent() method of your Activity to capture your touch events. This will be much more efficient than doing the loop yourself.
private boolean mTouchInImage = false;
public boolean onTouchEvent(MotionEvent event) {
boolean handledTouch = false;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Rect hitRect = new Rect((int)img.getX(), (int)img.getY(),
(int)(mg.getX() + img.getWidth()), (int)(img.getY() + img.getHeight()));
handledTouch = mTouchInImage = hitRect.contains((int)event.getX(), (int)event.getY());
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
handledTouch = mTouchInImage;
mTouchInImage = false;
break;
case MotionEvent.ACTION_MOVE:
if (mTouchInImage) {
img.setX(event.getX() - img.getWidth() / 2);
img.setY(event.getY() - img.getHeight() / 2);
img.invalidate();
handledTouch = true;
}
break;
}
return handledTouch;
}