Jump to content
scriptersteve

best way to loot stackables

Recommended Posts

Non-stackables I loot like this:

private void loot(GroundItem a) {

    if (a != null) { //need to add distance checking so doesnt run around liek bot for a steel longsword
        if (getInventory().isFull()) {
            eat();
        }
        int lastCount = getInventory().getEmptySlotCount();
        if (a.interact("Take")) {
            new ConditionalSleep(3000, 600) {
                @Override
                public boolean condition() throws InterruptedException {
                    return getInventory().getEmptySlotCount() < lastCount;
                }
            }.sleep();
        }
    }
}

when I only have one stackable I loot like this:

private void lootTokkul() {
    if (getTokkul() != null) {
        long lastTokkul = getInventory().getAmount("Tokkul");
        if (getTokkul().interact("Take")) {
            new ConditionalSleep(3000, 600) {
                @Override
                public boolean condition() throws InterruptedException {
                    return getInventory().getAmount("Tokkul") > lastTokkul;
                }
            }.sleep();
        }
    }
}

However when I have a huge ground item array of stackables. What's the best way to loot them

Link to comment
Share on other sites

11 minutes ago, Apaec said:

Pass the groundItem name to your loot method, save the number you currently have, (if you have 0, make sure you have an empty slot), then go ahead with the interaction 

But I have like 25 grounditems in my list, how do I know which one it will be looting? I am just testing if any of the items in the list are present and then wondering looting ( how can i detect which one of the list i am looting).

alchables = new String[]{"Adamant hasta","Adamant spear","Rune longsword","Dragon longsword","Dragon dagger","Mithril full helm","Adamant platelegs","Blue d'hide vamb","Blue d'hide body","Rune full helm","Rune platebody","Dragon med helm"};
stackables = new String[] {"Law rune","Death rune","Chaos rune","Rune arrow","Adamant dart","Rune javelin","Rune knife","Rune thrownaxe","Coins","Dragon dart tip","Dragon arrowtips","Dragon javelin heads"};
profitables = new String[] {"Ensouled dragon head","Dragon bones","Blue dragonhide","Draconic visage","Ancient shard","Dark totem base","Dark totem middle","Dark totem top","Clue scroll (hard)", "Clue scroll (elite)"};
Edited by scriptersteve
Link to comment
Share on other sites

 

:edit: I've massively overcomplicated it. Something better would be:

		if (lootExists()) {
			if (lootIsStackable()) {
				if (inventoryContainsLoot()) {
					lootItem();
				} else if (inventoryIsFull()) {
					makeSpace();
				}
			} else {
				if (inventoryIsFull()) {
					makeSpace();
				} else {
					lootItem();
				}
			}
		}

The problem with looting algorithms is that it's nearly impossible to figure out what items you'd dispose of in place for a more valuable item. Imagine if you're inventory's full of an item your bots is programmed to value, such as abyssal whips, but then a wild elysian sigil appears. If your bot isn't programmed to forcibly make space for that item, you could lose out on a very valuable item. You could try to use GE prices, but they're volatile, subjective, and sometimes miscellaneous items can have their prices purposfully manipulated to trick your bot into dropping valuables.

 

 

I've never written a looting algorithm before, because I haven't needed to. So this is certainly not the best and is untested. But, here's a 5-minute ad-hoc example of something I'd probably do:


private boolean loot(GroundItem groundItem, Callable<Boolean> executeIfInventoryFull) throws Exception {

		boolean looted = false;
		ConditionalSleep conditionalSleep;
		long invAmount;

		/* do we have the item in our inventory already (and is it stackable), or do we have space for it, OR can we make space for it? */
		if ((groundItem.getDefinition().isNoted() && inventory.contains(groundItem.getName())) || !inventory.isFull() || executeIfInventoryFull.call()) {

			/* Get current amount in the inventory */
			invAmount = inventory.getAmount(groundItem.getName());

			/* Create conditional sleep */
			conditionalSleep = new ConditionalSleep(3000, 600) {
				@Override
				public boolean condition() throws InterruptedException {
					/* Stop sleeping once the ground item ceases to exist */
					return !groundItem.exists();
				}
			};

			/* If the sleep was interrupted, then lets check to see if we looted item */
			if (conditionalSleep.sleep()) {
				looted = (inventory.getAmount(groundItem.getName()) > invAmount);
			}
		}

		return looted;
	}

This function checks to see if an item is noted and whether we have it in our inventory already. However, I'm not sure if this works for untradable stackable items. You could always adjust the routine to suite your needs.

If the item is not noted, then we check to see if the inventory is full.

If the inventory is full, then we call a retroactive function that tries to clear a space in the inventory. If that function returns true, then presumably, we have space to loot the item.

We return true if the amount of the item we've looted is greater than what we had before. I know this'll work for stackable items, and I'm guessing unstackable items are grouped together too. For instance, if you have 12 lobsters, then the getAmount for lobsters would return 12 also.

The function we'll use to clear a space in the inventory is as followes:


private boolean eatFoodToMakeSpace() {
		
		String[] foods = { "Lobster", "Trout", "Salmon" };
		
		ConditionalSleep conditionalSleep = new ConditionalSleep(3000) {
			@Override
			public boolean condition() throws InterruptedException {
				return !inventory.isFull();
			}
		};
		
		return inventory.interact("Eat", foods) && conditionalSleep.sleep();
	}

You give it a list of foods you want to eat, then eat one of them. We only need to check whether the inventory's not full to proceed to loot the item. Bare in mind, this function is called each time an item is attempted to be looted. Also note that there's no fail-safe mechanism in place if your inventory's full, but there's no food to eat. You'd need to extend this routine to include items you're willing to drop. You should also make sure you can dispose of an item before you even begin looting. Otherwise, go and bank.

Okay, so you said multiple items, so we can write a small routine to deal with that:


	private void loot(List<GroundItem> groundItems, Callable<Boolean> executeIfInventoryFull) throws Exception {
		
		for (GroundItem groundItem : groundItems) {
			
			if (!loot(groundItem, executeIfInventoryFull)) {
				
				throw new InterruptedException("Failed to loot: " + groundItem.getName());
			}
		}
	}

This is just a wrapper function. All we're doing is calling the main loot function multiple times. However, it makes our code look cleaner and more manageable. Also, take note of the exception we throw if - and only if - we fail to loot the item. If there's 10 items to loot, but we fail to loot the 5th item, then we stop looting the remaining 6 items. This is good, because it means stop, figure out what went wrong, adjust, and resume. If you'd rather just ignore the failed loot attempt, then you could tweak this code.

Then we need to run the entire thing:


		List<GroundItem> itemsToLoot = groundItems.filter(gi -> gi.getDefinition().isNoted());
		
		try {
			
			loot(itemsToLoot, this::eatFoodToMakeSpace);
			
		} catch (Exception e) {
			
			logger.error(e);
		}

Notice the try/catch? Callable#call throws an exception, so we need to catch it. It's originally there to catch any errors that occur in our eatFoodToMakeSpace function, because that's the function we call. However, there's no sense letting a try/catch go to waste. If you look at the previous block of code, we throw a custom exception if we fail to loot an item, so we can use the catch block to write in code that tries to find what went wrong in-game (e.g. Did the item despawn? Did somebody else take it? Is it out of reach? etc. ).

Edited by liverare
  • Like 2
Link to comment
Share on other sites

Thank's livere, I saw your boolean identifier way when reading through  your glassblowing script but couldn't quite get my head around it. the this::eatFoodToMAkeSpace, is this basically saying if we can loot it loot if, if we can't this is the method we are running to 'fix' the problem?

Regarding the unchecked, value for if we don't have any food left in inventory, I was thinking of running a forloop over every inventory position returning the value of this position and getting the lowest valued position and dropping it to make space for 'new' loot. but this might be inefficient if i run it each time but think it's the best method

14 minutes ago, liverare said:

I've never written a looting algorithm before, because I haven't needed to. So this is certainly not the best and is untested. But, here's a 5-minute ad-hoc example of something I'd probably do:


private boolean loot(GroundItem groundItem, Callable<Boolean> executeIfInventoryFull) throws Exception {

		boolean looted = false;
		ConditionalSleep conditionalSleep;
		long invAmount;

		/* do we have the item in our inventory already (and is it stackable), or do we have space for it, OR can we make space for it? */
		if ((groundItem.getDefinition().isNoted() && inventory.contains(groundItem.getName())) || !inventory.isFull() || executeIfInventoryFull.call()) {

			/* Get current amount in the inventory */
			invAmount = inventory.getAmount(groundItem.getName());

			/* Create conditional sleep */
			conditionalSleep = new ConditionalSleep(3000, 600) {
				@Override
				public boolean condition() throws InterruptedException {
					/* Stop sleeping once the ground item ceases to exist */
					return !groundItem.exists();
				}
			};

			/* If the sleep was interrupted, then lets check to see if we looted item */
			if (conditionalSleep.sleep()) {
				looted = (inventory.getAmount(groundItem.getName()) > invAmount);
			}
		}

		return looted;
	}

This function checks to see if an item is noted and whether we have it in our inventory already. However, I'm not sure if this works for untradable stackable items. You could always adjust the routine to suite your needs.

If the item is not noted, then we check to see if the inventory is full.

If the inventory is full, then we call a retroactive function that tries to clear a space in the inventory. If that function returns true, then presumably, we have space to loot the item.

We return true if the amount of the item we've looted is greater than what we had before. I know this'll work for stackable items, and I'm guessing unstackable items are grouped together too. For instance, if you have 12 lobsters, then the getAmount for lobsters would return 12 also.

The function we'll use to clear a space in the inventory is as followes:


private boolean eatFoodToMakeSpace() {
		
		String[] foods = { "Lobster", "Trout", "Salmon" };
		
		ConditionalSleep conditionalSleep = new ConditionalSleep(3000) {
			@Override
			public boolean condition() throws InterruptedException {
				return !inventory.isFull();
			}
		};
		
		return inventory.interact("Eat", foods) && conditionalSleep.sleep();
	}

You give it a list of foods you want to eat, then eat one of them. We only need to check whether the inventory's not full to proceed to loot the item. Bare in mind, this function is called each time an item is attempted to be looted. Also note that there's no fail-safe mechanism in place if your inventory's full, but there's no food to eat. You'd need to extend this routine to include items you're willing to drop. You should also make sure you can dispose of an item before you even begin looting. Otherwise, go and bank.

Okay, so you said multiple items, so we can write a small routine to deal with that:


	private void loot(List<GroundItem> groundItems, Callable<Boolean> executeIfInventoryFull) throws Exception {
		
		for (GroundItem groundItem : groundItems) {
			
			if (!loot(groundItem, executeIfInventoryFull)) {
				
				throw new InterruptedException("Failed to loot: " + groundItem.getName());
			}
		}
	}

This is just a wrapper function. All we're doing is calling the main loot function multiple times. However, it makes our code look cleaner and more manageable. Also, take note of the exception we throw if - and only if - we fail to loot the item. If there's 10 items to loot, but we fail to loot the 5th item, then we stop looting the remaining 6 items. This is good, because it means stop, figure out what went wrong, adjust, and resume. If you'd rather just ignore the failed loot attempt, then you could tweak this code.

Then we need to run the entire thing:


		List<GroundItem> itemsToLoot = groundItems.filter(gi -> gi.getDefinition().isNoted());
		
		try {
			
			loot(itemsToLoot, this::eatFoodToMakeSpace);
			
		} catch (Exception e) {
			
			logger.error(e);
		}

Notice the try/catch? Callable#call throws an exception, so we need to catch it. It's originally there to catch any errors that occur in our eatFoodToMakeSpace function, because that's the function we call. However, there's no sense letting a try/catch go to waste. If you look at the previous block of code, we throw a custom exception if we fail to loot an item, so we can use the catch block to write in code that tries to find what went wrong in-game (e.g. Did the item despawn? Did somebody else take it? Is it out of reach? etc. ).

 

Link to comment
Share on other sites

1 minute ago, scriptersteve said:

this::eatFoodToMAkeSpace

That's just referencing the function that will be ran to fix the problem.

7 minutes ago, scriptersteve said:

Regarding the unchecked, value for if we don't have any food left in inventory, I was thinking of running a forloop over every inventory position returning the value of this position and getting the lowest valued position and dropping it to make space for 'new' loot. but this might be inefficient if i run it each time but think it's the best method

There's not really a more efficient way of doing this. However, stackable items have a stackable worth, so you need to take that into account. You're going to need some sort of table/enum/data structure to define those items and their perceived values. Then, you can evaluate the items you have in your inventory to find the least valuable item(s) and drop them, then loot the more valuable item.

Link to comment
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.   Paste as plain text instead

  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...