Jump to content

Better Clicking Algorithm


Virtue

Recommended Posts

Hey guys - I wanted to share a bit of pseudocode that I've successfully used on bots in the past and resulted in a ~<2% ban rate, even on highly populated areas and pretty easily detected scripts. This method does take a bit of processing time, and isn't always recommended for every click needed. Also, it's late and I've been drinking, so there are likely a ton of pseudocoding mistakes.

 

First, let's pretend we have a 3x3 grid we want to click on in the format (x,y) where (1,1) is the top left corner and (3,3) is the bottom right corner.

 

As far as I know, almost every clicking algorithm will click randomly on the 3x3 grid, resulting in a distribution over time of roughly 1/9 clicks on each square. This is not desirable, as human clicking will not provide a perfect distribution and will tend to fall closer to where the cursor is initially - that is, if my mouse is at -2,-2 (above and to the left of our 3x3 click target), I will tend to click more towards (1,1) than (3,3). The opposite is true if I my cursor is at (5,5) - I will tend to click more towards (3,3) than (1,1). At a location such as (2, 5), I may tend to click more towards (2,3), and often more towards the middle row than anything else.

 

In order to follow these better click patterns, we need to first identify which direction our mouse is relative to the target. Because I'm lazy, I use the 8 cardinal directions - North (2,1), NorthEast (3,1), East (2,3), etc. to identify the path of the movement.

 

So the first part of our script becomes:

GetCursorLocation(){
dX = CursorX - TargetX (note that there should be logic to find the closest X and Y in the target grid)
dY = CursorY - TargetY
}

Now that we know which direction we're going to move, we should figure out which "region" of clicking we should fall into:

FindCardinalDirection(dx, dy){
// logic to define which click regime you're in
// I like to use if dX or dY is greater than 3-4x the other to use NSEW instead of NE, SE, etc.
return "North", "South", etc.
}

The next part is a little bit more difficult - How do we get a better click distribution? The answer is in statistics - namely Gaussian (for the most part). I'll offer a simple example for the North Case.

If (FindCardinalDirection = North) then{
// for this code block, we're going to try to target the middle y row (x,2) the most, with the x row (1,y) the most likely vertical click location.
LocationToActuallyClick(){
while (not 1<= Xclick <= 3){
Xclick = 1
Xclick = Xclick + rand(0,1)-rand(0,1) //where rand(0,1) is a random choice between 0 and 1
Xclick = Xclick+ rand(0,1)-rand(0,1)
}
 
Yclick = 2
Yclick = Yclick + rand(0,1) - rand (0,1)
 
return (Xclick, Yclick)
}
}
ClickMouseOn(Xclick,Yclick)

The resulting Y distribution should be ~25% row 1, ~50% row 2, and ~50% row 3. The resulting X distribution should be about 57% row 1, ~29% row 2, and ~14% row 3. This should be a lot closer to "normal" human clicking.

 

I prefer to use normal distributions with sigma between .2 and 1.5, depending on the application. Note that this does take a bit of runtime and can slow down computers a decent amount if running multiple bots or on older machines.

Edited by Virtue
  • Like 4
Link to comment
Share on other sites

Great read. I actually cover this a little bit in my OmniPocket thread except I call it "bias deviation"

I'll post some code here in a bit, hang in there tongue.png

 

EDIT: untested, however should work:

import java.awt.Point;
import java.awt.Rectangle;
import java.util.Random;

public class BetterMouse {
	
	
	public static Point getBiasedPoint(Point src, Point dst) {
		Rectangle dstRect = new Rectangle((int) dst.getX() - 2, (int) dst.getY() - 2, 5, 5);
		
		if (dstRect.contains(src)) return src;
		
		int dstStartX = (int) dstRect.getX();
		int dstStartY = (int) dstRect.getY();
		int dstEndX = dstStartX + (int) dstRect.getWidth();
		int dstEndY =  dstStartY + (int) dstRect.getHeight();
		
		int srcX = (int) src.getX();
		int srcY = (int) src.getY();
		
		boolean north = (srcY <= dstStartY);
		boolean west = (srcX <= dstStartX);
		
		int biasedX = (north ? positiveSkewedRandom(1, 3) : negativeSkewedRandom(1, 3));
		int biasedY = (west ? positiveSkewedRandom(1, 3) : negativeSkewedRandom(1, 3));
		
		return new Point(dstStartX + biasedX, dstStartY + biasedY);
	}
	
	public static int positiveSkewedRandom(int min, int max) {
		return skewedRandom((double) min, (double) max, 2.55, -1.68);
	}

	public static int negativeSkewedRandom(int min, int max) {
		return skewedRandom((double) min, (double) max, 2.55, 1.68);
	}
	
	//http://stackoverflow.com/a/13548135
	public static int skewedRandom(double min, double max, double skew, double bias) {
		Random r = new Random(System.currentTimeMillis());
		double range = max - min;
		double mid = min + range / 2.0;
		double unitGaussian = r.nextGaussian();
		double biasFactor = Math.exp(bias);
		double retval = mid + (range * (biasFactor / (biasFactor + Math.exp(-unitGaussian / skew)) - 0.5));
		return (int) retval;
	}
}

Skewed random functions from here, enjoy smile.png

Edited by Bobrocket
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.
Note: Your post will require moderator approval before it will be visible.

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