Auto-click the sequence The following pascal script needs a bit of context for it's purpose to be understood. For those who don't give about purpose, feel free to skip ahead.

Background

A game I'm terribly fond of has a couple of levels which have nothing to do with the rest of the game. One of those is a puzzle: Alpha Sector::virgilw::Brain

When you open the game, it looks like this:


Turns out you need to connect all the 8 red dots to the emitter like this:



All dots must be clicked in a specific sequence. If a dot is clicked and allowed to switch, it will light up if not already lit up. If it was already lit up it will darken again. The sequence to light up the 8th dot looks like this:

1 2 1 3 1 2 1 4
1 2 1 3 1 2 1 5
1 2 1 3 1 2 1 4
1 2 1 3 1 2 1 6
1 2 1 3 1 2 1 4
1 2 1 3 1 2 1 5
1 2 1 3 1 2 1 4
1 2 1 3 1 2 1 7
1 2 1 3 1 2 1 4
1 2 1 3 1 2 1 5
1 2 1 3 1 2 1 4
1 2 1 3 1 2 1 6
1 2 1 3 1 2 1 4
1 2 1 3 1 2 1 5
1 2 1 3 1 2 1 4
1 2 1 3 1 2 1 8


The mathematically inclined can probably tell us what such a sequence is called, but it should be obvious to the rest of us that the first 7 characters are always the same and only the 8th varies.

Now, there's an achievement for lighting up all 8 dots within 60 seconds. Since it's already 128 clicks for lighting up the 8th one alone, I decided to automate the process using SCAR Divi (manual), an environment running Pascal scripts which allows you to automate keyboard presses and mouse movements.

Code

The resulting code is ugly, but I was mainly looking for fast code. This means it may not be up to par with Pascal-style and I took a short-cut wherever I thought it would influence the execution speed.

I'm looking for improvements on readability wherever this does not hurt the performance in any way. I've written a fair bit of Pascal but I've always been a little fuzzy on what did and didn't impact performance. General tips about this are welcome.

The most used procedure is MnC(x, y), short for Move'n'Click. It moves the mouse to whatever coordinate you give it, clicks once with the left mouse-button and halts in between to give the rest of the computer time to catch up. Or whatever it does in that time, but it breaks terribly if it doesn't wait long enough.

All values are hardcoded. I'm aware I can put them at the front of the program as global variables, but I wasn't sure whether the additional look-up would penalize performance. Current script manages to win in 21.86 seconds :-)

virgilwBrain.scar

Procedure MnC(x,y : integer);
begin
  MoveMouse(x,y);
  Wait(30);
  ClickMouse(x,y,false);
  Wait(29);
end;

Procedure hit1();
begin
  MnC(421,298);
end;

Procedure hit2();
begin
  MnC(470,311);
end;

Procedure hit3();
begin
  MnC(500,318);
end;

Procedure hit4();
begin
  MnC(539,325);
end;

Procedure hit5();
begin
  MnC(577,325);
end;

Procedure hit6();
begin
  MnC(611,318);
end;

Procedure hit7();
begin
  MnC(655,311);
end;

Procedure hit8();
begin
  MnC(694,298);
end;

Procedure line();
begin
  hit1;
  hit2;
  hit1;
  hit3;
  hit1;
  hit2;
  hit1;
end;

begin
  line;
  hit4;
  line;
  hit5;
  line;
  hit4;
  line;
  hit6;
  line;
  hit4;
  line;
  hit5;
  line;
  hit4;
  line;
  hit7;
  line;
  hit4;
  line;
  hit5;
  line;
  hit4;
  line;
  hit6;
  line;
  hit4;
  line;
  hit5;
  line;
  hit4;
  line;
  hit8;

  line;
  hit4;
  line;
  hit5;
  line;
  hit4;
  line;
  hit6;
  line;
  hit4;
  hit1;
  hit2; 
end.
I've done similar things in the past, although never with a scripting language and I haven't used Pascal in a really long time, so this is more of an approach review than a code review.

The action happens in MnC:

Procedure MnC(x,y : integer);
begin
  MoveMouse(x,y);
  Wait(30);
  ClickMouse(x,y,false);
  Wait(29);
end;


Moving the Mouse

The first thing you do is MouseMove.  The question that immediately springs to mind is do you need to move the mouse?  Unless the program your are interacting with actually tracks mouse movements in an attempt to detect bots, you probably don't need to move it.  Most of the times in the programs I have written I have been able to just send mouse click events at the relevant coordinates.  The library you're using supports a SetMousePos method that will simply set the coordinates of the mouse.  However, as ClickMouse calls this under the hood, this might not work for you.  I'd try running the script with just two clicks and a longer wait between them to detect if the program really requires the movements or if it's only concerned by the clicks.

ClickMouse(421,298,false);
Wait(100);
ClickMouse(470,311,false);
Wait(100);


If it is only concerned by the clicks, then great, you only need to worry about clicking the mouse.  If the program does care about the movement then you might want to think about using the MoveMouseEx function, which supports an additional parameter MouseSpeed.  The default speed is 20, with larger numbers being faster so increasing the number should either make the function return faster, or allow you to wait for less time before clicking.  Depending on whether the movement is async, you may also find that you need to wait less time for shorter movements (i.e. the mouse will get from 8 to 7 faster than it will get from 8 to 1).

Clicking the mouse

After you click the mouse, you wait before doing the next thing.  You may be able to make it so that you don't need to wait as long by using playing with MouseClickEx and passing in different values for Interval which is how long the mouse button is held down for.  Alternately, depending on how your application works you may be able to detect that the mouse click has been recognised (because some pixels have changes from red to green for example).  The library appears to have a few functions that might support this FindColors or FindBitmap would appear to be good starting points.  You could then loop until the click has been recognised, rather than waiting.