Minister Oak Minister Oak - 13 days ago 5
Python Question

Sikuli - How to cycle through findAll Matches

I am working on a bot for an online game. When logging in there are multiple servers that I must select from. Each server has an "online"/"offline" identifier next to the server names. Even when there are multiple online servers the bot will only select one of them, the same one every time. And what's strange is that the other one should be found first therefor it should be the one getting clicked.

I want to tweak my code so that I can find all the "online" instances and select one. If that one fails to connect then I want it to go back through the menus and try the next potential online server. When a server fails to connect or server connection is lost it goes back to the main menu with a Server Connection Lost popup. Below is the code that I'm working with. The logic is basically checking for any issues such as server connection lost or death and fixing those issues before actually trying to fight.

BotOn = True
FighterBot = False

def runHotkey(event):
global BotOn
BotOn = False
Env.addHotkey(Key.ESC, KeyModifier.SHIFT, runHotkey)

def BotFighter(event):
global FighterBot
FighterBot = True
Env.addHotkey(Key.PAGE_UP,0, BotFighter)

def BotFighterOff(event):
global FighterBot
FighterBot = False
Env.addHotkey(Key.PAGE_DOWN,0, BotFighterOff)

while BotOn == True:
while FighterBot == True:
if exists("LostServerConnection.png"):
click("LostServerConnection.png")
wait(.5)
if exists("LoginFailed.png"):
click("LoginFailed.png")
wait(.5)
if exists("Login.png"):
click("Login.png")
wait(.5)
if exists("Play.png"):
click("Play.png")
wait(.5)
if exists("OnlineServer.png"):
click("OnlineServer.png")
wait(("AttackButton.png")or("LostServerConnection.png"),FOREVER)
if exists("AttackButton.png"):
#commands for moving to specific location
elif exists("LostServerConnection.png"):
click("LostServerConnection.png")
wait(.5)
if exists("Revive.png"):
click("Revive.png")
waitVanish("Revive.png")
wait("AttackButton.png")
#commands for moving to specific location
else:
#commands for fighting


I apologize that it's crudely written. I am still learning and trying to figure this issue out before I clean it up.

My ideal result is having a function that will check how many servers are online, create an index for them, and be able to cycle through that index as it attempts to log into one that is actually working.

(Sikuli Check multiple of the same images on screen)
I tried what Eugene S said in this link however when I input the image in the "image" pieces of his code and Sikuli errored out at the first line:

Edit1: I reread some important info I missed and fixed the code. It does not error out now, however it is not actually clicking on anything. I'm sure I'm missing something so I will continue to look into this in the meantime.
(thank you EugeneS)

def returnImageCount(image):
count = 0
for i in findAll(image):
count += 1
return count
imageCount = returnImageCount("OnlineServer.png")

if imageCount == 1:
click(buttonX.image)
elif imageCount == 2:
click(buttonY.image)
else:
pass


Edit2: I have updated the below to the current code I am working with. It seems to be much closer to what I am looking for. But right now it finds all matches, then clicks on each one even after it disappears from loading the first one it clicks. I'd like to be able to do what I added in it's comments.
(thank you EugeneS)

wait(5)
OnlineServers = findAll(Pattern("OnlineServer.png").exact())
for Server in OnlineServers:
Server.click()#(only click the first match)
#wait for game to load OR server connection failed(wait as long as it takes)
#if game load
#proceed to standard actions
#elif server connection failed
#log in
#retry logic with next match in list
#(after it finishes going through all possible matches and fails each one, I want it to try again from the first match and go through the cycle again. So if there is only 1 match I want it to just keep trying that one.)

Answer

I am not aware of your game logic but if you need to work with multiple patterns, you can do this as follows:

Find them and store in some list:

resultsList = findAll("YOUR_FILE_NAME.png")

Then for each found pattern do what you need with it

for result in resultsList:
    result.highlight(1)
    #DO WHAT YOU NEED

As an alternative you can just use the first item in list and remove it each time you require next item:

NEXT_PATTERN = results.pop(0)

You will have to think about a strategy that makes sense for you and implement it using the concepts I mentioned. IMHO you should split all your main tasks into blocks and then build your main logic. For example you could implement functions like these:

def waitForGameToLoad():
    # Wait sufficienttime and verify that the game has been actually successfully started

def isGameLoaded():
    # Just and exmaple of another function that can be used internally inside waitForGameToLoad

def getNextAvailableServer():
    # Get next available server from a list that you have previously created

As a result you can build the main logic block in a way that is much easier to understand, for exmaple:

availableServersList = getAvailableServers() # getAvailableServers is a function that you will have to implement yourself

while True:
    connectServer(availableServersList)

    if connected():
        break

NOTE

What I described above is just a general suggestion of how you can implement your main logic. You do not need to follow that directly. Try to create functions that will make sense for you and then use them together to build a logical flow.