Jump to content

Recommended Posts

Posted (edited)

Hello all,

I'm trying to come up with a bot that will play a game called Collapse. I'm doing this as a challenge for myself because I figured it would be pretty difficult to come up with an app with the logic needed to play it and also to be very fast.

What I've done so far:

Figured out a way to find the game on the screen.

Decipher the different color boxes and bombs for the game. *

Store the position and color of each box for later usage.

Destroy bombs before boxes.

* I'm currently using pixelchecksum() for simplicity, but I'll most likely change that to a 4 or 5 point pixelgetcolor().

What I need help/suggestions to do:

Come up with a way to find all boxes of the same color that are connected (3 or more).

Come up with a faster screen searching method for boxes/bombs.

Here's my code so far:

#include <Array.au3>

#region Globals
Const $Title = "Collapse - Microsoft Internet Explorer"
Global $FirstRun = 1
Global $StartX = 337
Global $StartY = 393
Const $BoxRed   = 3183926199
Const $BoxGreen     = 426970639
Const $BoxBlue  = 1410215416
Const $BoxWhite     = 3294800457
Const $BoxGrey  = 3059036688
Const $BoxEmpty     = 50331649
Const $BombRed  = 1990514232
Const $BombGreen    = 1332283690
Const $BombBlue     = 904996846
Const $BombBlack    = 3050855186
Const $BombWhite    = 3039844320
Global $Boxes[1]
Global $PosX[1]
Global $PosY[1]
#endregion Globals

#region Main Loop
While 1
    If (WinActive($Title)) Then
        If ($FirstRun) Then
            $FirstRun = _FindFirstBox()
            MouseMove($StartX,$StartY,0)
        Else
            $time = TimerInit()
            _FindBomb()
            _UpdateBoxes()
        EndIf
    EndIf
    Sleep(10)
WEnd
#endregion Main Loop

Func _GetBoxColor($X,$Y = -1)
    Local $Checksum = 0
    If ($X > -1 And $Y > -1) Then
;Each box/bomb is 15x15 pixels (well, what we want anyways)
        $Checksum = PixelChecksum($X,$Y,$X+15,$Y+15)
        
        Select
            Case ($Checksum == $BoxRed)
                Return "Red Box"
            Case ($Checksum == $BoxGreen)
                Return "Green Box"
            Case ($Checksum == $BoxBlue)
                Return "Blue Box"
            Case ($Checksum == $BoxWhite)
                Return "White Box"
            Case ($Checksum == $BoxGrey)
                Return "Grey Box"
            Case ($Checksum == $BoxEmpty)
                Return "Empty Box"
            Case ($Checksum == $BombRed)
                Return "Red Bomb"
            Case ($Checksum == $BombGreen)
                Return "Green Bomb"
            Case ($Checksum == $BombBlue)
                Return "Blue Bomb"
            Case ($Checksum == $BombBlack)
                Return "Black Bomb"
            Case ($Checksum == $BombWhite)
                Return "White Bomb"
            Case Else
                MouseMove($X,$Y,0)
                MsgBox(0,"GetBoxColor","Unknown Color: " & $Checksum)
                Return "Unknown Color: " & $Checksum
        EndSelect
    ElseIf ($X > -1 And $Y == -1) Then
        Return $Boxes[$X]
    Else
        Return 0
    EndIf
EndFunc;==>_GetBoxColor(X,Y)

Func _UpdateBoxes()
    Local $iterX = 0
    Local $iterY = 0
    
    For $iterX = $StartX To ($StartX-264) Step -24
        For $iterY = $StartY to ($StartY-336) Step -24
            _ArrayAdd($Boxes,_GetBoxColor($iterX,$iterY));Store the color of the box
            _ArrayAdd($PosX,$iterX)             ;Store the X position of the box
            _ArrayAdd($PosY,$iterY)             ;Store the Y position of the box
        Next
    Next
EndFunc;==>_UpdateBoxes()

Func _FindAdjacentBox($X,$Y,$Color)
    Local $Information[3]
    Local $Found
;Find a box that is next to the current position (X,Y) and is
;the same color (color checksum) and return it as an array
    If ($Found) Then
        $Information[0] = $X
        $Information[1] = $Y
        $Information[2] = $Color
    Else
        $Information[0] = -1
        $Information[1] = -1
        $Information[2] = -1
    EndIf
    Return $Information
EndFunc;==>_FindAdjacentBox(X,Y)

Func _FindFirstBox()
    $WinInfo = WinGetPos($Title)
    If (Not @error) Then
        $pos = PixelSearch($WinInfo[0],$WinInfo[1],($WinInfo[0] + $WinInfo[2]),($WinInfo[1] + $WinInfo[3]),0xCC962C)
        If (Not @error) Then
            $StartX = $pos[0] - 26
            $StartY = $pos[1] - 42
            Return 1
        EndIf
    EndIf
EndFunc;==>_FindFirstBox()

Func _FindBomb()
    Local $iter = 1
    For $iter = 1 to UBound($Boxes)
        If (StringInStr($Boxes[$iter],"Bomb")) Then
            MouseClick("left",$PosX[$iter],$PosY[$iter])
            Sleep(1000)
            ExitLoop
        EndIf
    Next
EndFunc;==>_FindBomb()

Note that _GetBoxColor() will poll the storage array or the current screen position depending on the agruments supplied (so I can use the same function for simplicity). The unfinished function _FindAdjacentBox() is going to be the logic heavy portion of the code. The _FindFirstBox() function finds a single unique pixel on the screen and calculates the position of the bottom-right box. The update function reads all boxes from the bottom up and right to left.

I'm having a real mental block trying to come up with a way to find connected boxes efficiently. I will also need to decide if/when to click bombs. Black bombs destroy everything within 5-6 boxes of itself. Every other color bomb destroys all boxes of that color currently on the screen (I.E. A Red bomb destroys all red boxes).

Here is a screenshot of the game with some highlighting I've included to show how the logic should work for finding connected boxes (3 or more). Once it finds a set of boxes, it needs to click on it and then refresh the list of boxes.

Posted Image

So, the basic loop rundown of this bot is:

1.) Search for a bomb, click on it, go to step 1

2.) Update the list of boxes

3.) Find a set of 3 or more same colored boxes that are connected and click on it

4.) Repeat

Any suggestions would be greatly appreciated,

CMR

Edited by CodeMaster Rapture
Posted

I think it might be easier (but not as bottish) to just have it click really fast nonstop and let you move around to wherever you want it to click.

A little unrelated - what's your high score? I just played for the first time and got to level 9 with a score of 210,436.

Posted

That's impressive. It really speeds up as the level progresses.

I just did a test with a super-fast clicking script (hold space to click at ultra-speed), and I didn't even get past level 10. So if a bot can manage to do that, I'm really impressed.

Posted (edited)

Hello again,

I've been working on the logic part of my bot and I think I've got it down, I'm just not too sure where I should call it. Here's the code I've got:

Global $Tracked[1]

Func _FindAdjacentBox($X,$Y,$Color)
    Local $Found
    Local $iter
;Find a box that is next to the current position (X,Y) and is the same color (color checksum)
    While ($Found)
        For $iter = 1 To (UBound($Boxes)-1) Step 1
            If ($Boxes[$iter] == $Color) Then
                If   ($PosX[$iter] == ($X-15) And $PosY[$iter] == $Y) Then
                    _ArrayAdd($Tracked,$iter)
                    $X -= 15
                    ExitLoop
                ElseIf ($PosX[$iter] == ($X-15) And $PosY[$iter] == ($Y-15)) Then
                    _ArrayAdd($Tracked,$iter)
                    $X -= 15
                    $Y -= 15
                    ExitLoop
                ElseIf ($PosX[$iter] == ($X-15) And $PosY[$iter] == ($Y+15)) Then
                    _ArrayAdd($Tracked,$iter)
                    $X -= 15
                    $Y += 15
                    ExitLoop
                ElseIf ($PosX[$iter] == $X And $PosY[$iter] == ($Y-15)) Then
                    _ArrayAdd($Tracked,$iter)
                    $Y -= 15
                    ExitLoop
                ElseIf ($PosX[$iter] == $X And $PosY[$iter] == ($Y+15)) Then
                    _ArrayAdd($Tracked,$iter)
                    $Y += 15
                    ExitLoop
                ElseIf ($PosX[$iter] == ($X+15) And $PosY[$iter] == $Y) Then
                    _ArrayAdd($Tracked,$iter)
                    $X += 15
                    ExitLoop
                ElseIf ($PosX[$iter] == ($X+15) And $PosY[$iter] == ($Y+15)) Then
                    _ArrayAdd($Tracked,$iter)
                    $X += 15
                    $Y += 15
                    ExitLoop
                ElseIf ($PosX[$iter] == ($X+15) And $PosY[$iter] == ($Y-15)) Then
                    _ArrayAdd($Tracked,$iter)
                    $X += 15
                    $Y -= 15
                    ExitLoop
                Else
                    $Found = Not $Found
                EndIf
            EndIf
        Next
    WEnd

;Ok, we found 3 or more adjacent boxes, let's destroy them.
    If ((UBound($Tracked)-1) > 2) Then
        MouseClick("left",$PosX[$Tracked[1]],$PosY[$Tracked[1]])
        ReDim $Tracked[1]
        Return
    EndIf
EndFunc ;==>_FindAdjacentBox(X,Y)

I figure this is the best method to find connected boxes (8 points around the current box), but I need a little help to exclude the previos box(es) I've tracked. Any ideas on how to go about that? I also am unsure of where to call this function for the best performance.

Thanx,

CMR

Edited by CodeMaster Rapture
Posted

I was thinking a little about this, and I decided on a very few things.

1. You don't actually need to check all 8 spaces around a block - only the vertical and horizontal 4 (up, right, down, left, not diagonal). Since blocks aren't considered connected if they are attached diagonally, there's no point in checking.

2. Since the blocks shift when you click a group, you shouldn't check all of the blocks for possible groups - just go through each row checking for neighbors, and if there is a group, click it and start over. Otherwise, you'll be checking stuff that doesn't exist. Although, now that I think about it, checking for neighbors as you go will take care of shifts in blocks....

This is a fun challenge... hopefully the code will be quick enough.

Posted

I was thinking a little about this, and I decided on a very few things.

1. You don't actually need to check all 8 spaces around a block - only the vertical and horizontal 4 (up, right, down, left, not diagonal). Since blocks aren't considered connected if they are attached diagonally, there's no point in checking.

2. Since the blocks shift when you click a group, you shouldn't check all of the blocks for possible groups - just go through each row checking for neighbors, and if there is a group, click it and start over. Otherwise, you'll be checking stuff that doesn't exist. Although, now that I think about it, checking for neighbors as you go will take care of shifts in blocks....

This is a fun challenge... hopefully the code will be quick enough.

Ya know... that's a good point. I didn't even think of the diagonals. Thank you. As for the "shifting", that won't really be a problem. Since it would take far too much code to track how the blocks "shift", I decided to just click and then update my list of boxes. Well, there goes 4 less boxes to check per cycle!

Thanx so much,

CMR

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...