This is the third part of Building a Poker Bot series where I describe my experience developing bot software to play in online poker rooms. I'm building the bot with .NET framework and F# language which makes the task relatively easy and very enjoyable. Here are the previous parts:

In this short post I write about the last step of the poker bot flow: clicking the buttons. So, the screen is already recognized, the hand is understood, the decisions are made and now the bot needs to execute the actions. Except for the bet sizing, this simply means clicking the right button at the poker table.

The stealthiness of such clicks is a valid concern here. Ideally, we want all the mouse movements to look as similar as possible to the movements produced by a human being. For this post, I will simplify the task to the following steps:

  • Identify where the mouse cursor is right now
  • Decide where the mouse should be moved to
  • Gradually move the mouse cursor
  • Click the button

Cursor Position

It's really easy to understand where the mouse cursor currently is: just use Control.MousePosition property from the standard library:

let currentPosition () = 
  let mp = System.Windows.Forms.Control.MousePosition
  (mp.X, mp.Y)

Note that your application doesn't have to be based on WinForms, just reference the required assembly.

Move the Cursor

I use the third party WindowsInput library to control the mouse and the keyboard programmatically. It uses some weird coordinate system, so the function to move the mouse cursor looks like this:

let simulator = new InputSimulator()

let moveTo x y =
  let toX = 65535. * x / (Screen.PrimaryScreen.Bounds.Width |> float)
  let toY = 65535. * y / (Screen.PrimaryScreen.Bounds.Height |> float)
  simulator.Mouse.MoveMouseTo(toX, toY)

The input parameters x and y are the pixel location starting at the top-left corner of the screen.

Move It Smoothly

Now we want to simulate the human-like movements. It won't be perfect, but at least it should look decent. For this gradual movement function I used a nice F# feature called asynchronous workflows. Effectively, it looks like a loop with async sleep statements inside.

let moveToWorkflow step (toX, toY) = async {
  let (fromX, fromY) = currentPosition()
  let count = Math.Max(10, (Math.Abs (toX - fromX) + Math.Abs (toY - fromY)) / 20)
  for i = 0 to count do
    let x = step fromX toX count i |> float
    let y = step fromY toY count i |> float
    moveTo x y
    do! Async.Sleep 3

The key parameter here is the step function of obscure type int -> int -> int -> int -> int. Basically, it calculates a coordinate for n-th step of the movement. We can plug different implementations of this function to find the right balance of the movement style. Here is the simplest linear implementation:

let linearStep from until max i =
  from + (until - from) * i / max

The sinus-based implementation is a bit more verbose because of float-int conversions:

let sinStep (from:int) (until:int) (max:int) (index:int) =
  let fromf = from |> float
  let untilf = until |> float
  let maxf = max |> float
  let indexf = index |> float
  fromf + (untilf - fromf) * Math.Sin(Math.PI / 2. * indexf / maxf) |> int

The following animation illustrates the concept:

The top mouse cursor just jumps from left to right and back (no animation). The middle cursor moves with linear speed (linearStep function above). The bottom cursor moves based on the sinStep function derived from sinus of time.

Click the Button

A button is a rectangle and we want to click a random point inside it. So, all we need is to pick random coordinates, move the mouse there and send a click event via the simulator:

let clickButton (minX, minY, maxX, maxY) =
  let r = new Random()
  let p = (r.Next(minX, maxX), r.Next(minY, maxY))
  moveToWorkflow sinStep p |> Async.RunSynchronously

Demo Time

Here is the demo of the mouse movements:

Mouse clicking the button

It looks fun, doesn't it? The full code for the mouse movements can be found in my github repo.