autohotkey how to cycle through array - autohotkey

I'm mainly a javascript developer and I'm starting to play with authotkey. I'm guessing if there is a better way to loop through an array than the way I'm using. Basically is like this:
cycle(value,maxValue){
value += 1
if value not between 1 and %maxValue%
value :=1
return value
}
Then I use it like this:
variable := cycle(variable,array.MaxIndex())
Seems a bit rudimentary. Is any other way?
EDIT:
I saw that my description was not clear. What I want is to get variables from the array in a circular way: when you ask for the next value and you are already at the last one, start again from the beginning.

Sounds like what you need is a for-loop
Example:
colors := Object("red", 0xFF0000, "blue", 0x0000FF, "green", 0x00FF00)
for key, value in colors
s .= key "=" value "`n"
MsgBox % s
Edit:
As per your comment, this may be more towards your need
index := 0
maxValue := 10
f3::
tooltip % index := cycle(index, maxValue)
return
cycle(index, maxValue)
{
return index := mod(index + 1, maxvalue)
}
Hope it helps

Since what blackholyman did is correct, it is not aimed on arrays.
Here is an approach that now I know it works:
i:=0,somearr:= ["bla","morebla","bla bla"]
;-- do whatever stuff here
somearr[i:=i>1?--i:somearr.MaxIndex()]

Related

Build an array in AHK from mouse clicks

I am looking to build an array of mouse click coordinates. I'm struggling to understand how you build out a two-dimensional array. Or if I'm going about this wrong and should build two arrays. One with X-coordinates and one with Y-coordinates.
I can easily build out an array with just X coordinates or just Y-coordinates. Can I somehow build out X and Y into the same 2-D array?
ArrayX := []
ArrayY := []
counter:=1
^Lbutton::
MouseGetPos CurX, CurY
ArrayX[counter] := CurX
ArrayY[counter] := CurY
counter++
return
^q::
loop % ArrayX.MaxIndex()
items.= ArrayX[A_Index] ","
StringLeft, items, items, Strlen(items)-1
Clipboard := items
msgbox % items
items:=""
loop % ArrayY.MaxIndex()
items.= ArrayY[A_Index] ","
StringLeft, items, items, Strlen(items)-1
Clipboard := items
msgbox % items
exitapp
You'd create a multidimensional or a jagged array in AHK by just assigning the element at the desired index an array.
Array[3] := [481, 529]
And you'd access it by just accessing the elements in order, e.g:
Array[3][2] would result in 529.
Other things to improve on your script:
You might want to use the .Push() method to append values to your array.
A for loop might be a desirable alternative to normal looping and worrying about the index:
for each, element in Array
StringLeft is a deprecated legacy command and shouldn't be used anyone. SubStr() replaces it.
However, for what you were doing with it, RTrim() would be better/easier:
RTrim(items, ",")
Revised script:
Array := []
^LButton::
MouseGetPos, CurX, CurY
Array.Push([CurX, CurY])
return
^q::
For each, element in Array
items .= element[1] ", " element[2] "`n"
MsgBox, % RTrim(items, "`n")
ExitApp
return

autohotkey use a return value imagesearch and click

I have a function:
NormalRand(x,y,int=1) {
Loop 12
{
Random, var,0.0,1
Num+=var
}
norm := (int) ? Round((y+x)/2+((Num-6)*(y-x))/6) : (y+x)/2+((Num-6)*(y-x))/6
Return norm < x ? x : norm > y ? y : norm
}
I have an imagesearch:
ImageSearch, FoundX, FoundY, 0, 0, A_ScreenWidth, A_ScreenWidth, *50 Okay.jpg
If ErrorLevel = 0
{
xCord = NormalRand(%FoundX%-10,%FoundX%+10)
yCord = NormalRand(%FoundY%-10,%FoundY%+10)
MsgBox, 4,, Found the image at %xCord% %yCord%
Click, %xCord%, %yCord% Left, 1
Sleep, 2000
}
I am trying to use the NormalRand function to distribute my clicks around the buttons so they are harder to detect within the program I will use this with. However, when i try to sent the cords to the click it doens't work. When i test it with msgbox i get this output Found the image at NormalRand(391-10,391+10) NormalRand(676-10,676+10)
I can't seem to figure out how to get it to send the numbers instead of the text.
What we have here, is misuse, and probably also unintentional use, of the legacy syntax.
Lets look at these two lines:
xCord = NormalRand(%FoundX%-10,%FoundX%+10)
yCord = NormalRand(%FoundY%-10,%FoundY%+10)
You're actually assigning text to those variables, not calling a function.
See this as an example:
xCord = NormalRand(%FoundX%-10,%FoundX%+10)
yCord = NormalRand(%FoundY%-10,%FoundY%+10)
MsgBox, % xCord "`n" yCord
For legacy syntax you're referencing the FoundX and FoundY variables correctly by wrapping them around %, but you're not doing that for the function name.
So in legacy syntax you'd do this:
xCord = %NormalRand%(%FoundX%-10,%FoundX%+10)
yCord = %NormalRand%(%FoundY%-10,%FoundY%+10)
However, please stop using legacy syntax. It's so ancient, bad and very different compared to other programming languages you maybe have experienced.
Expression syntax is what you want to use, so instead of legacy =, we're using := to assign an expression to our variables. (= is never ever used!)
In expression syntax your function calls look normal and nice:
xCord := NormalRand(FoundX-10, FoundX+10)
yCord := NormalRand(FoundY-10, FoundY+10)
And to preach even more about legacy syntax, you're also using it on the if-statement. To not use the legacy if-statement, use if ().

Series of Variables by name

In AHK script:
Code for finding a value between great numbers of variables above, for one variable:
If (Variable1 = "sin (90°)")
MsgBox Value is reached
How searching by this method between series of variables with different value of number in their names? From Variable5 to Variable15, Variable51 to Variable105, etc.
How modify this code if number from 5 to 15, 51 to 105, or 74 to 117 etc?
number = 5
If (Variable%number% = "sin (90°)")
.............
Is %Variable%number%% acceptable and will works surely?
And here may also be useful Associative Arrays. What is it by simple examples?
Best practice here would probably be to use an array in the first place.
myArray := []
myArray[1] := "bla"
myArray.Push("bla2") ;by using this you don't need to worry about the index number
myArray[3] := "sin (90°)"
myArray[4] := 63456
Loop % myArray.MaxIndex()
{
If (myArray[A_Index] = "sin (90°)")
{
MsgBox Value is reached
}
}
... another example
anotherArray := []
Loop, read, C:\Files\prog.txt
{
If (A_LoopReadLine = "FileRead, OutputVar, C:\Files\prog1.txt")
{
anotherArray.Push(A_LoopReadLine)
MsgBox, An interesting code line was found. And was added to the array.
}
Else If (A_LoopReadLine = "blablabla")
{
anotherArray.Push(A_LoopReadLine)
MsgBox, An interesting code line was found. And was added to the array.
}
Else If (A_LoopReadLine = "some other text line")
{
anotherArray.Push(A_LoopReadLine)
MsgBox, An interesting code line was found. And was added to the array.
}
;Else
;{
; MsgBox, Nothing important was found.
;}
}
Loop % anotherArray.MaxIndex()
{
currentArrayEntry := anotherArray[A_Index]
MsgBox, %currentArrayEntry%
}
In an expression you can use variable expansion to modify the name of the variable to use:
number = 5
If (Variable%number% = "sin (90°)")
.............
But consider using arrays instead.
Is %Variable%number%% acceptable and will works surely?
Not. More right way
% Variable%number%
but it may have a problem.
Possible way is use Var := expression
Variable:= number
may be.

Create a CoffeeScript range with a length instead an endpoint?

I want to create a CoffeeScript range (like [4...496]) but using a length instead of an end range. This can be done with a loop like
myNum = getBigNumber()
newArray = ( n + myNum for n in [0...50] )
but I'm wondering if there is range-related shortcut that I'm missing. Is there something like
[getBigNumber()...].length(50) available in CoffeeScript?
You can just do
range = [myNum...myNum + 50]
Edit: As mu points out in the comments, CoffeeScript will add some complexity whether you use the snippet above or the original code. If performance is an issue, it might be better to drop down to plain JS for the loop (using backticks in the CoffeeScript code).
Assuming you want an ascending (i.e. low to high) range, you can do:
myNum = getBigNumber()
length = 50
range = new Array length
i = 0
`for(; i < length ; i++) { range[i] = i + myNum }` # raw, escaped JS
It's a lot faster than CoffeeScript's way of doing things, but note that CoffeeScript's range syntax also supports creating descending ranges by just flipping the boundary values. So CoffeeScript is (as always) easier on the eyes and simpler to work with, but raw JS is 3.5x faster in my test.

Error, (in addList) invalid subscript selector

I have a list of numbers and I want to add them and then multiply them by a constant.
addList := proc(a::list,b::integer)::integer;
local x,i,s;
description "add a list of numbers and multiply by a constant";
x:=b;
s:=0;
for i in a do
s:=s+a[i];
end do;
s:=s*x;
end proc;
sumList := addList([2, 2, 3], 2) works fine but at the same time sumList := addList([20, 20, 30], 2) gives an error.
Can somebody point out the error ?
In the for loop you do s:=s+a[i] but i is set to one of the values in a already - not the index of a value. A first pass fix would be to just change the statement above to s:=s+i.
You could also write the function as
proc(a::list,b::integer)::integer;
convert(a,`+`)*b;
end;
Even shorter, there is
addList:= (a,b)-> `+`(a[])*b;