The following Pascal script needs a bit of context for its 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: Puzzle at start
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.
-
4\$\begingroup\$ The most used procedure is MnC(x, y), short for Move'n'Click. - then why don't you call it that? \$\endgroup\$Simon Forsberg– Simon Forsberg2016年02月08日 19:55:57 +00:00Commented Feb 8, 2016 at 19:55
-
\$\begingroup\$ @SimonForsberg That's what you get for re-using old snippets. It's from a script written in a time before I got enlightened about code quality. Also, I remember Pascal slowing down with basically everything you add. My methods to test that are sketchy at best though, so I'm happily proven wrong. \$\endgroup\$Mast– Mast ♦2016年02月08日 20:09:41 +00:00Commented Feb 8, 2016 at 20:09
1 Answer 1
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 changed 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.