Jump to content
Wrecker

favorite bit of code you wrote?

Recommended Posts

what's your favorite implementation that you have come up with to solve a problem?

my favorite to date is on a project that I pretty much gave up on when I realized how much work was ahead of me when I wanted to rewrite it into a proper implementation. I was just writing it for an end result and when I realized how fucked I was going to be if I ever released it and expected to be able to update/modify it without breaking a stupid amount of dependencies, I pretty much left it where it was. it was supposed to be a MC Spigot RPG plugin where you created a wolf pack, fought to steal resources and shit from other packs, leveled up your characters, had other npc wolves that were characters and a part of packs that claimed their own territories and stuff like that who you could wage war against/try to coerce into joining your own pack alongside other players, but it worked into a giant mess that would work initially, but would be susceptible to a shit-ton of broken dependencies for some simple additions/changes, and I was embarrassed so I left it where it was.

One thing I was proud of, though, was using A* search to figure out paths to travel, the idea was to be able to send AI wolves from your pack to go out and scout other packs and report their findings when they returned... The next step was to build a few separate databases of blocks and hold them in a queue, so individual threads could access them and figure out where they were going without fucking over the main thread... Maybe even build some better heuristics, but fuck I was happy when everything worked as it was the first time...

Node:

 class Node implements Comparable<Node> {
     private Node parentNode = null;
     ArrayList<Node> neighbours = new ArrayList<>();

     private boolean isClosed = false;

     private int f;
     private int g = Integer.MAX_VALUE;
     private int h;

     private int x;
     private int y;
     private int z;

    private RouteFinder finder;

     Node(int x, int y, int z, RouteFinder finder) {
        this.x = x;
        this.y = y;
        this.z = z;

        this.finder = finder;
        finder.nodes.add(this);
        setHeuristic();
    }

    private boolean nodeExistsAt(int x, int y, int z) {
        for (Node node : finder.nodes) if (node.x == x && node.y == y && node.z == z) return true;
        return false;
    }

    private boolean isObstacle(int x, int y, int z) {
        World world = finder.world;

        return world.getBlockAt(x,y,z).getType().isSolid() ||
                !world.getBlockAt(x,y,z).getType().isSolid() &&
                        !world.getBlockAt(x,y-1,z).getType().isSolid();
    }

    private void setHeuristic() {
        h = Math.abs(x - finder.toX) + Math.abs(y - finder.toY) + Math.abs(z - finder.toZ);
    }

    void setF() {
        f = g + h;
    }

    int getG() {
        return g;
    }

    int checkG(Node node) {
         if (node.y > y || node.y < y) return 2;
         else return 1;
    }

    void setG(int g) {
        this.g = g;
    }

    boolean isComplete() {
        return x == finder.toX && y == finder.toY && z == finder.toZ;
    }

    void setParentNode(Node node) {
        this.parentNode = node;
    }

     Node getParentNode() {
         return parentNode;
     }

     int getX() {
         return x;
     }

     int getY() {
         return y;
     }

     int getZ() {
         return z;
     }

     void setNeighbours() {
        for (int i = -1; i < 2; i++) {
            for (int j = -1; j < 2; j++) {
                for (int k = -1; k < 2; k++) {
                    int cX = x + i;
                    int cY = y + k;
                    int cZ = z + j;

                    if (!nodeExistsAt(cX,cY,cZ) && !isObstacle(cX,cY,cZ)) {
                        Node node = new Node(cX,cY,cZ,finder);
                        neighbours.add(node);
                    }
                }
            }
        }
    }

    boolean isClosed() {
         return isClosed;
    }

    void setClosed() {
         isClosed = true;
    }

     @Override
    public int compareTo(@Nonnull Node node) {
         return Integer.compare(f, node.f);
    }
}

RouteFinder:

public class RouteFinder implements RouteObserver {
    private static final long TRAVEL_TIMEOUT = 10000;
    private static final long RETRY_TIMEOUT = 500;
    private static final long TELEPORT_TIMEOUT = 8000;
    private static final int BLOCK_ACCURACY = 2;

    ArrayList<Node> nodes = new ArrayList<>();
    private ArrayList<Node> openSet = new ArrayList<>();

    private ArrayList<Location> route = new ArrayList<>();

    private boolean isEntityLoaded;

    private EntityInsentient entity;

    private RouteSubject subject;

    private int fromX;
    private int fromY;
    private int fromZ;

    int toX;
    int toY;
    int toZ;

    World world;

    public RouteFinder(EntityInsentient entity, World world) {
        this.entity = entity;
        isEntityLoaded = world.getChunkAt((int)entity.locX,(int)entity.locZ).isLoaded();
        this.subject = WolfPack.getChunkLoadListener();
        this.world = world;
    }

    private void setRoute() {
        long startTime = System.currentTimeMillis();
        long endTime;
        long totalTime;
        Node node = new Node(fromX,fromY,fromZ,this);
        node.setG(0);
        openSet.add(node);

        while (!openSet.isEmpty()) {

            Collections.sort(openSet);
            Node current = openSet.get(0);
            current.setNeighbours();

            openSet.remove(current);
            current.setClosed();

            if (current.isComplete()) {

                //DEBUG
                endTime = System.currentTimeMillis();
                totalTime = endTime - startTime;
                Debugger.debug("Execution took: " + totalTime + "ms");
                //DEBUG

                //Route found
                buildRoute(current);
                return;
            }
            for (Node neighbour : current.neighbours) {
                if (neighbour.isClosed()) continue;
                if (!openSet.contains(neighbour)) openSet.add(neighbour);
                int tempG = current.getG() + neighbour.checkG(current);
                if (tempG >= neighbour.getG()) continue;
                neighbour.setParentNode(current);
                neighbour.setG(tempG);
                neighbour.setF();
            }
        }
        //No route found
    }

    private void buildRoute(Node node) {
        Node current = node;
        while (current.getParentNode() != null) {
            route.add(new Location(world,current.getX(),current.getY(),current.getZ()));
            current = current.getParentNode();
        }
        nodes.clear();
        openSet.clear();
        Collections.reverse(route);
    }

    public void moveEntity(Location to) {
        subject.addObserver(this);

        this.fromX = (int) entity.locX;
        this.fromY = (int) entity.locY;
        this.fromZ = (int) entity.locZ;

        this.toX = to.getBlockX();
        this.toY = to.getBlockY();
        this.toZ = to.getBlockZ();

        setRoute();

        if (isEntityLoaded) moveEntityLoaded();
        else moveEntityUnloaded();
    }

    private void moveEntityLoaded() {
        new BukkitRunnable() {
            public void run() {

                //DEBUG
                int debugRetryAttempt = 0;
                //DEBUG

                Iterator<Location> iterator = route.iterator();

                while (iterator.hasNext()) {
                    Location go = iterator.next();

                    long timeout = System.currentTimeMillis() + TRAVEL_TIMEOUT;
                    long retryTravel = System.currentTimeMillis() + RETRY_TIMEOUT;
                    long teleportTravel = System.currentTimeMillis() + TELEPORT_TIMEOUT;

                    int entityX;
                    int entityY;
                    int entityZ;

                    int goToX = go.getBlockX();
                    int goToY = go.getBlockY();
                    int goToZ = go.getBlockZ();

                    boolean arrived = false;

                    entity.getNavigation().a(goToX, goToY, goToZ, 1D);

                    while (!arrived) {
                        if (!isEntityLoaded) {
                            moveEntityUnloaded();
                            cancel();
                        }

                        entityX = (int) entity.locX;
                        entityY = (int) entity.locY;
                        entityZ = (int) entity.locZ;

                        //Check to see if the entity has arrived at its destination
                        if (entityX >= goToX - BLOCK_ACCURACY && entityX <= goToX + BLOCK_ACCURACY
                                && entityY >= goToY - BLOCK_ACCURACY && entityY <= goToY + BLOCK_ACCURACY
                                && entityZ >= goToZ - BLOCK_ACCURACY && entityZ <= goToZ + BLOCK_ACCURACY) {
                            arrived = true;
                            iterator.remove();
                        }

                        //Probably won't occur with the teleport aspect added in, but just for safety's sake
                        if (System.currentTimeMillis() >= timeout) {

                            //DEBUG
                            Debugger.debug("Cancelling route.");
                            //DEBUG

                            cancel();
                            return;
                        }

                        //Retry the next step of the route, they get distracted a lot
                        if (System.currentTimeMillis() >= retryTravel) {
                            entity.getNavigation().a(goToX, goToY, goToZ, 1D);
                            retryTravel = System.currentTimeMillis() + RETRY_TIMEOUT;
                            debugRetryAttempt++;
                        }

                        //Teleport the entity if it is stuck
                        if (System.currentTimeMillis() >= teleportTravel) {
                            //DEBUG
                            Debugger.debug("Teleporting...");
                            //DEBUG

                            entity.setPosition(go.getX(),go.getY(),go.getZ());

                            teleportTravel = System.currentTimeMillis() + TELEPORT_TIMEOUT;
                        }

                        try {
                            Thread.sleep(10);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
                //DEBUG
                Debugger.debug("Route COMPLETE! Retries: " + debugRetryAttempt);
                //DEBUG

                cancel();
                //Route complete
            }
        }.runTaskAsynchronously(WolfPack.getPlugin());
        subject.removeObserver(this);
    }

    private void moveEntityUnloaded() {
        //Speed Formula
        //y = 43.178x-0.02141
        double blocksPerMillisecond = (43.178 *
                entity.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).getValue() - 0.02141)/1000;

        new BukkitRunnable() {
            public void run() {

                Iterator<Location> iterator = route.iterator();
                double blockTravelled = 10/blocksPerMillisecond;
                int counter = 0;

                Location go = iterator.next();

                //Emulate the entity travelling the route through the unloaded chunk
                while (iterator.hasNext()) {

                    while (counter <= blockTravelled) {
                        entity.setPosition(go.getX(),go.getY(),go.getZ());

                        if (isEntityLoaded) {
                            moveEntityLoaded();
                            cancel();
                        }

                        counter = counter + 10;
                        iterator.remove();

                        iterator.next();

                        try {
                            Thread.sleep(10);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
                cancel();
            }
        }.runTaskAsynchronously(WolfPack.getPlugin());
        subject.removeObserver(this);
    }

    @Override
    public void update(boolean loaded) {
        this.isEntityLoaded = loaded;
    }

    @Override
    public EntityInsentient getEntity() {
        return entity;
    }
}

Tried to implement the Observer pattern to check if an Entity happened to be unloaded from the server so it could continue its route:

public interface RouteObserver {
    void update(boolean loaded);

    EntityInsentient getEntity();
}
public interface RouteSubject {
    void addObserver(RouteObserver observer);

    void removeObserver(RouteObserver observer);

    void notifyObservers(ArrayList<EntityInsentient> entities, boolean loaded);
}
public class ChunkLoadListener implements Listener, RouteSubject {
    private ArrayList<RouteObserver> observers = new ArrayList<>();

    @EventHandler
    public void onChunkUnload(ChunkUnloadEvent event) {
        notifyObservers(buildEntityList(event.getChunk()),false);
    }

    @EventHandler
    public void onChunkLoad(ChunkLoadEvent event) {
        notifyObservers(buildEntityList(event.getChunk()),true);
    }

    @Override
    public void addObserver(RouteObserver observer) {
        observers.add(observer);
    }

    @Override
    public void removeObserver(RouteObserver observer) {
        observers.remove(observer);
    }

    private ArrayList<EntityInsentient> buildEntityList(Chunk chunk) {
        ArrayList<EntityInsentient> entities = new ArrayList<>();
        for (Entity entity : chunk.getEntities()) {
            if (((CraftEntity) entity).getHandle() instanceof  EntityInsentient) {
                entities.add((EntityInsentient) ((CraftEntity) entity).getHandle());
            }
        }
        return entities;
    }

    @Override
    public void notifyObservers(ArrayList<EntityInsentient> entities, boolean loaded) {
        for (RouteObserver observer : observers) {
            for (EntityInsentient entity : entities) {
                if (observer.getEntity() == entity) observer.update(loaded);
            }
        }
    }
}

 

That was a while ago, and I have no idea what observers is doing in ChunkLoadListener after reading it in this post, but I do remember it being a problem with it waiting for it to say that there's an Entity that should be emulated to move instead of actually moved... Fuck me, it might not be moving at all after unload maybe with nothing looking to see where it's at? Been a while since I've dealt with Spigot or looked at this code lol.

Erm, fuck it, what have you written that does exactly what you want it to or at least close enough that you're happy with where you're at in you goals?

Share this post


Link to post
Share on other sites

It's hard to have a 'favourite' piece of code, really, but the one I've used, updated and adapted most often is this one. It's nothing big, fancy and glamorous, it just provides a simple way to generate a random integer within a given range in a single line of code. Language is Swift 4.

public func randomNumber<T : SignedInteger>(inRange range: ClosedRange<T> = 1...6) -> T { 
	let length = Int64(range.upperBound - range.lowerBound + 1) 
	let value = Int64(arc4random()) % length + Int64(range.lowerBound) 
	return T(value) 
}

- F

Share this post


Link to post
Share on other sites
On 2/21/2020 at 4:32 AM, Socketosis said:

You must use the strict comparison operator, baka!

That's exactly the point of my favorite bit of code. :3

PHP is one of the exceptionally few languages where a separate strict comparison operator exists, let alone is so critically necessary that success and failure return codes test equal without it.  This behavior is not intuitive at all to programmers coming to PHP from other C-like languages, and it's a failure on the language designer's part, not on the programmer's part.

Horrifyingly, this language design flaw also allows things like phantom hash collisions when the hashes don't actually collide.  Have a look at my runner-up favorite piece of code.  They test equal because PHP sees strings starting with "0e#," thinks they're numbers in scientific notation, and silently and unexpectedly casts the non-numerical strings to floating point numbers identical to 0.0 (edit: integers identical to 0) for the comparison test.  (Phantom hash collisions have also been found for SHA3-256 and SHA3-512 inputs.)

1571684415_Screenshot_2020-02-21PHPTESTER-TestPHPcodeonline.png.ad33be0661a492d3ccbd879687cbc48b.png

  • Like 1

Share this post


Link to post
Share on other sites
On 2/21/2020 at 1:38 PM, ArielMT said:

That's exactly the point of my favorite bit of code. :3

PHP is one of the exceptionally few languages where a separate strict comparison operator exists, let alone is so critically necessary that success and failure return codes test equal without it.  This behavior is not intuitive at all to programmers coming to PHP from other C-like languages, and it's a failure on the language designer's part, not on the programmer's part.

Horrifyingly, this language design flaw also allows things like phantom hash collisions when the hashes don't actually collide.  Have a look at my runner-up favorite piece of code.  They test equal because PHP sees strings starting with "0e#," thinks they're numbers in scientific notation, and silently and unexpectedly casts the non-numerical strings to floating point numbers identical to 0.0 for the comparison test.  (Phantom hash collisions have also been found for SHA3-256 and SHA3-512 inputs.)

1571684415_Screenshot_2020-02-21PHPTESTER-TestPHPcodeonline.png.ad33be0661a492d3ccbd879687cbc48b.png

Yeah, it's similar to Javascript in that regard. I just make it a habit to always use the strict comparison operators unless there's a reason why I want it to be non-strict.

  • Like 1

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...