Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

Bejewelled 2 autoplay.


  • Please log in to reply
5 replies to this topic
Dewi Morgan
  • Members
  • 191 posts
  • Last active: Jun 07 2015 04:02 AM
  • Joined: 03 Oct 2005
First script, because hello world is passé.

The company "Pop cap games" have a popular Flash game called "Bejewelled".

It's simple: a grid of coloured pieces. Each move, you swap two adjacent pieces to form a line of three or more of the same colour. The line disappears, tetris style, the pieces shift downwards, and the empty grid spaces are filled from the top.

I wanted to try out different strategies in a controlled environment, to see which was best. I decided to make something that would play Pop Cap's Bejewelled or any of the mass of similar clones that have come out, and record scores for different strategies.

The graphics in the latest version, Bejewelled 2 Deluxe 1.0 are significantly "improved" from the earlier version, in ways that I suspected would prevent ImageSearch (animated pieces, with flashy sparkles on) or PixelSearch (pieces are different shapes, have arbitrary photographic backgrounds which could contain any colour). But that version of the game has a really handy-for-testing-strategies "play forever" mode.

So I pick a pixel within each piece, and use the r/g/b proportions to try to guess what colour it's meant to be.

So far, I think I'm successful. It plays on three different versions of the game, though obviously once they use colours it can't differentiate (cream and white), it fails.

The version below is configured for, and the colour guessing function is optimised for, the locally-installed demo version of Bejewelled 2 Deluxe 1.0, though it will with tweaking work with others too.

Now I have to get some RL work done, but if I can be bothered, next I'll: 1) Make it prompt for grid size,
2) Ask for mouseclicks on the top left and bottom right of the grid
3) Ask for mouseclick on a piece in the centre of the board to get the sample pixel position within the gridsquares.
4) Grab the window title from the active window after those clicks
5) Grab the score after a fixed time (probably as an image).

For the moment, I've run it through a large number of moves using two algorithms: bottommost valid move, and topmost valid move. It doesn't rank moves by potential gain at all, and does no planning ahead (for that, I'll throw the board layout at a language that I'm more comfortable in). But already, a pattern is emerging.

Going for the lowest move on the board gives the highest average per-move score, so would be good for timed games.

Going for the highest move on the board gives lower per-move scores, but gives longer games, and hence higher overall scores, since you're less likely to run out of valid moves. Good for versions of the game where running out of moves ends the game.

This strategy difference seems to be the case on every version of the game, though the smaller the board, the less the difference is.

I'd really be interested in whether people can think of a better way of differentiating between different pieces. At the moment, it sometimes has a hard time differentiating between cyan/blue (so I removed cyan, since there were no cyan pieces), and red/orange/yellow.

Mostly, though, it's the white sparkles that cause problems: it thinks they are white pieces. I guess one option there would be to retest "white" pieces at a couple of other points, just in case.

I'd also be interested in any other feedback whatsoever, from code style to optimisation suggestions to telling me to STFU. I'm new to this language, and anything you can say will be muchly appreciated.


; Script to play a board game.
; 
; Proof of concept thang to recognise pieces by colour.
; 
; Assumes that all pieces are primary (rgb) or secondary (cmy)
; colors, orange, or grey. Doesn't use PixelSearch, as the
; background may be any colour. Instead, checks the color of a
; single pixel offset within each square to build a map of the
; board layout each turn, then operates on that.
; 
; Detection of red/orange/yellow and cyan/blue is really a bit dodgy
; and often wrong.
;
; Copyright is explicitly released upon the public domain.
; This means you may do as you wish with it, without credit.

^!r::Reload ; pause/reload the script.

^!d:: toggleDebug()

toggleDebug()
{
  global SG_DEBUG
  if ( SG_DEBUG == 1)
    SG_DEBUG=0
  else
    SG_DEBUG=1
}


^!z::
; Globals.
; These should be gathered on the fly rather than predefined.

; Offset to measure within the square.
SG_OFFSET_X=6
SG_OFFSET_Y=12
; Coords within the window for the top left of the board.
SG_ORIGIN_X=168
SG_ORIGIN_Y=15
; Size of a single square.
SG_SIZE_X=52
SG_SIZE_Y=52
; Number of squares on board.
SG_SQUARES_X=8
SG_SQUARES_Y=8
; Remember last move so we're less likely to get stuck trying the same wrong move forever.
; But if there are TWO wrong moves, then...
SG_LAST_X=0
SG_LAST_Y=0
; Whether this version allows piece dragging rather than click/click.
SG_DRAG_OK=1
; Debugging level
SG_DEBUG=0

Loop
{
  getWindow()
  ; Loop Y then X: move across every square, down only once per line.
  sg_line := ("")
  Loop, %SG_SQUARES_Y%
  {
    sg_y := A_Index
    sg_py := yCoordToPixel(A_Index)
    sg_line = %sg_line% `n# %A_Index% : 
    Loop, %SG_SQUARES_X%
    {
      sg_x := A_Index
      sg_px := xCoordToPixel(A_Index)
      PixelGetColor, sg_color, %sg_px%, %sg_py%, RGB
      sg_c := hex2Color(sg_color)
      SG_ARR_%sg_x%_%sg_y% := sg_c
      col := SG_ARR_%sg_x%_%sg_y% 
      ;MsgBox, SG_ARR_%sg_x%_%sg_y%  = %col%
      sg_line = %sg_line% %sg_c% ( %sg_x% , %sg_y% = %sg_px% , %sg_py% )
    }
    ;MsgBox, %sg_line%
  }
  getMove()
  if ( SG_DEBUG == 1 )
  {
    MsgBox, DEBUG %SG_DEBUG% %sg_line%
  }
  ;return
}


getWindow()
{
; Window management. This shouldn't care about the window name.
WinWait, Bejeweled 2 Deluxe 1.0, 
IfWinNotActive, Bejeweled 2 Deluxe 1.0, , WinActivate, Bejeweled 2 Deluxe 1.0, 
WinWaitActive, Bejeweled 2 Deluxe 1.0, 
}

; Convert a board y square coord to a screen y coord.
; This means our y coords are, internally, all "backwards" - but who cares?
yCoordToPixel(y)
{
  global SG_SIZE_Y
  global SG_ORIGIN_Y
  global SG_OFFSET_Y
  global SG_SQUARES_Y
  ; SG_SQUARES_Y - y as we're working from the bottom for highest scores.
  return ( ( SG_SQUARES_Y - y + 1 ) * SG_SIZE_Y ) + SG_ORIGIN_Y + SG_OFFSET_Y
}

; Convert a board square x coord to a screen x coord.
xCoordToPixel(x)
{
  global SG_SIZE_X
  global SG_ORIGIN_X
  global SG_OFFSET_X
  return ( x * SG_SIZE_X ) + SG_ORIGIN_X + SG_OFFSET_X
}

; Make the mouse move a piece.
movePiece(x1, y1, x2, y2)
{
  global SG_DRAG_OK
  global SG_ORIGIN_X
  global SG_ORIGIN_Y
  global SG_LAST_X
  global SG_LAST_Y
  SG_LAST_X := x1
  SG_LAST_Y := y1

  ;MsgBox, Moving from %x1% , %y1% to %x2% , %y2%, set last = %SG_LAST_X% , %SG_LAST_Y%
  getWindow()
  if (SG_DRAG_OK == 1)
  {
    MouseClickDrag, L, xCoordToPixel(x1), yCoordToPixel(y1), xCoordToPixel(x2), yCoordToPixel(y2), 2
  }
  else
  {
    MouseClick, L, xCoordToPixel(x1), yCoordToPixel(y1)
    Sleep, 100
    MouseClick, L, xCoordToPixel(x2), yCoordToPixel(y2)
    Sleep, 100
  }
  ; Move to home point to avoid hilighting pieces.
  MouseMove, SG_ORIGIN_X, SG_ORIGIN_Y
  ; Wait for pieces to fall.
  Sleep, 600
}


; Check that the piece in an array coord is not out of bounds,
; and is the same color.
isSameAt(col, x, y)
{
  ; Generic global to include array.
  global 
  if (x < 1 || x > SG_SQUARES_X || y < 1 || y > SG_SQUARES_Y )
  {
  ;MsgBox, returning false as OOB ( %x% < 1 || %x% > %SG_SQUARES_X% || %y% < 1 || %y% > %SG_SQUARES_Y% )
    return false
  }
  ax := SG_ARR_%x%_%y%
  if ( SG_ARR_%x%_%y% == col )
  {
    ;MsgBox, returning true as %ax% == %col% and none of ( %x% < 1 || %x% > %SG_SQUARES_X% || %y% < 1 || %y% > %SG_SQUARES_Y% )
    return true
  }
  ;MsgBox, returning false as %ax% != %col% though none of ( %x% < 1 || %x% > %SG_SQUARES_X% || %y% < 1 || %y% > %SG_SQUARES_Y% )
  return false
}


; An extremely nasty bruteforcism: tries each possible move in turn.
; If it finds one, it does it.
; Unfortunately, for the SG_ARR stuff, need all globals defined.
getMove()
{
  global
  Loop, %SG_SQUARES_Y%
  {
    y = %A_Index%
    py := xCoordToPixel(y)
    y1 := ( y + 1 )
    y2 := ( y + 2 )
    y3 := ( y + 3 )
    y_1 := ( y - 1 )
    y_2 := ( y - 2 )
    y_3 := ( y - 3 )
    Loop, %SG_SQUARES_X%
    {
      x = %A_Index%
      ; Anti-stick: don't play the same piece twice in a row
      ; If it's the only playable square, we'll get it next time round.
      if ( SG_LAST_X == x && SG_LAST_Y == y )
      {
        ; Zero them so we can get this square next time if it's the only one.
        SG_LAST_X = 0
        SG_LAST_Y = 0
        Continue
      }
      px := xCoordToPixel(px)
      x1 := ( x + 1 )
      x2 := ( x + 2 )
      x3 := ( x + 3 )
      x_1 := ( x - 1 )
      x_2 := ( x - 2 )
      x_3 := ( x - 3 )
      col := SG_ARR_%x%_%y%

      ;MsgBox, At %x%, %y% comparing %col% , %x2% , %y% And %col%, %x3%, %y%

      ; X_xx
      if ( isSameAt(col, x2, y) && isSameAt(col, x3, y) )
      {
        movePiece( x, y, x1, y)
        return
      }
      ; xx_X
      else if ( isSameAt(col, x_2, y) && isSameAt(col, x_3, y) )
      {
        movePiece( x, y, x_1, y)
        return
      }
      ; X
      ; _
      ; x
      ; x
      else if ( isSameAt(col, x, y2) && isSameAt(col, x, y3) )
      {
        movePiece( x, y, x, y1)
        return
      }
      ; x
      ; x
      ; _
      ; X
      else if ( isSameAt(col, x, y_2) && isSameAt(col, x, y_3) )
      {
        movePiece( x, y, x, y_1)
        return
      }
      ; x_
      ; x_
      ; _X
      else if ( isSameAt(col, x_1, y_1) && isSameAt(col, x_1, y_2) )
      {
        movePiece( x, y, x_1, y)
        return
      }
      ; _x
      ; _x
      ; X_
      else if ( isSameAt(col, x1, y_1) && isSameAt(col, x1, y_2) )
      {
        movePiece( x, y, x1, y)
        return
      }
      ; _X
      ; x_
      ; x_
      else if ( isSameAt(col, x_1, y1) && isSameAt(col, x_1, y2) )
      {
        movePiece( x, y, x_1, y)
        return
      }
      ; X_
      ; _x
      ; _x
      else if ( isSameAt(col, x1, y1) && isSameAt(col, x1, y2) )
      {
        movePiece( x, y, x1, y)
        return
      }
      ; X__
      ; _xx
      else if ( isSameAt(col, x1, y1) && isSameAt(col, x2, y1) )
      {
        movePiece( x, y, x, y1)
        return
      }
      ; _xx
      ; X__
      else if ( isSameAt(col, x1, y_1) && isSameAt(col, x2, y_1) )
      {
        movePiece( x, y, x, y_1)
        return
      }
      ; __X
      ; xx_
      else if ( isSameAt(col, x_1, y1) && isSameAt(col, x_2, y1) )
      {
        movePiece( x, y, x, y1)
        return
      }
      ; xx_
      ; __X
      else if ( isSameAt(col, x_1, y_1) && isSameAt(col, x_2, y_1) )
      {
        movePiece( x, y, x, y_1)
        return
      }
      ; x_
      ; _X
      ; x_
      else if ( isSameAt(col, x_1, y_1) && isSameAt(col, x_1, y1) )
      {
        movePiece( x, y, x_1, y)
        return
      }
      ; _x
      ; X_
      ; _x
      else if ( isSameAt(col, x1, y_1) && isSameAt(col, x1, y1) )
      {
        movePiece( x, y, x1, y)
        return
      }
      ; _X_
      ; x_x
      else if ( isSameAt(col, x_1, y1) && isSameAt(col, x1, y1) )
      {
        movePiece( x, y, x, y1)
        return
      }
      ; x_x
      ; _X_
      else if ( isSameAt(col, x_1, y_1) && isSameAt(col, x1, y_1) )
      {
        movePiece( x, y, x, y_1)
        return
      }
    }
  }
  ; MsgBox, Unable to find a move
}


; Convert a hex value to a colour letter, from:
; r(ed)c(yan)g(reen)y(ellow)o(range)b(lue)m(agenta)w(hite).
; Per: r rg=o gr=y g gb=c bg=c b rb=m br=m
hex2Color(color)
{
  r := ( ( color >> 16 ) & 0xFF )
  g := ( ( color >> 8 ) & 0xFF )
  b := ( color & 0xFF )
  if (r > g && r > b )
  {
    col = r
    min := ( r * 0.5 )
    if (min <= g && min <= b )
      col=w
    if (min <= g && min > b ) ; dodgy yellow test.
      if (r - g > 10)
        col=o
      else ; numbers too similar, still yellow.
        col=y
    if (min > g && min <= b )
      col=m
  }
  if (g >= r && g >= b )
  {
    col = g
    min := ( g * 0.6 )
    if (min <= r && min <= b )
      col=w
    if (min <= r && min > b )
      col=y
    if (min > r && min <= b )
;      col=c ; commented since there ARE no cyan pieces in the test game
      col=b
  }
  if (b >= r && b >= g )
  {
    col = b
    min := ( b * 0.6 )
    if (min <= r && min <= g )
      col=w
    if (min <= r && min > g )
      col=m
    if (min > r && min <= g )
;      col=c ; commented since there ARE no cyan pieces in the test game
      col=b
  }
  ;MsgBox, Colour %color%=%col% r:%r% g:%g% b:%b%
  return col
}

Yet another hotkeyer.

Chris
  • Administrators
  • 10727 posts
  • Last active:
  • Joined: 02 Mar 2004
Even if someone doesn't use it for this particular game, your script is sophisticated enough to be a great example of screen-searching in its own right. Thanks for posting it.

eef
  • Guests
  • Last active:
  • Joined: --
Nice idea, Autoplay!

I really like bejeweled and play it quite often.
As I see this has been posted some time ago.
Nonetheless heres my proposal for a solution of
your "color guessing problem":

The idea is to take average color of a given gem.
In the following pseudocode x=0,y=0 is the upper left
coordinate of the gem. dx/dy its size.


c = 0;  // color averaging variable

for x = 0 to dx
{   
    for y = 0 to dy
    {
        c += getcolor(x,y);
    }
}

c /= dx * dy;  // devide by number of pixels in the area

if ( c > somecolor - tolerance && c < somecolor + tolerance)
{
     print "gem is most probably of color 'somecolor' ";
     // do sth here...
}
else if ( c > someothercolor - tolerance ....


One would need to tweak the constants 'somecolor' and 'tolerance'
a littlebit. The algorithm should be quite robust.
Even better would be to take the median of the pixel colors,
this would be immune to isolated color values from the sparkling.

This is classical problem in the field of image processing. If you're interested in the topic just google for "image processing"

Cheers
eef

  • Guests
  • Last active:
  • Joined: --
Very good point.

Have to average the r, g, and b separately, though. Otherwise, the "average" of 0x030000 ("black, negligible red") and 0x0000ff ("100% blue") is 0x01807f ("50% cyan") - clearly not a sensible average colour: the green value isn't in either of the initial two colours.

Averaging the bytes individually, it's 0x01007f (50% blue, negligible red), which is a more sensible average.

Another thing to take into account is that sparkles and other graphical wossnames will generally be greater than one pixel in size, so you can save processing time (or use the same processing time to scan a larger area) by skipping pixels.

Since stuff tends to be in straight lines, my guess at the best pixel-pattern would be the tightest one in which no pixels are in a straight line for eachother.

For example in an 8x8 array, you could scan only 8 pixels and get a pretty good guess, if you selected them carefully: http://en.wikipedia....t_queens_puzzle

o o o X o o o o
o o o o o o X o
o o X o o o o o
o o o o o o o X
o X o o o o o o
o o o o X o o o
X o o o o o o o
o o o o o X o o

This might be moot, though: my gut feeling is that pixel-checking is "expensive", and needs to be minimised, but perhaps it's really fast.

For an 8x8 board, the number of checks at:
1 pixel/boardsquare = 8x8 = 64
8 pixels/boardsquare = 8x8x8 = 512
8*8(=64) pixels/square = 8x8x64 = 4096

Could then use a fn like (untested):
SG_NUM_SCANNED_PIXELS = 8
SG_SCAN_X_0 = 4
SG_SCAN_X_1 = 7
SG_SCAN_X_2 = 3
SG_SCAN_X_3 = 8
SG_SCAN_X_4 = 2
SG_SCAN_X_5 = 5
SG_SCAN_X_6 = 1
SG_SCAN_X_7 = 6

; Like pixelGetColor, but for board squares.
; Finds the average color for an 8x8 area in the given board square.
boardSquareGetColor(sg_x, sg_y)
{
  global SG_NUM_SCANNED_PIXELS
  ; I think the following includes the whole array, not sure.
  global SG_SCAN_X_

  sg_px := xCoordToPixel(sg_x)
  sg_py := yCoordToPixel(sg_y)
  r = 0
  g = 0
  b = 0
  ; Get the pixel values
  Loop %SG_NUM_SCANNED_PIXELS%
  {
    new_px := sg_px + SG_SCAN_X_%A_Index%
    new_py := sg_py + %A_Index%
    PixelGetColor, sg_color, %new_px%, %new_py%, RGB
    r := r + ( ( sg_color >> 16 ) & 0xFF )
    g := g + ( ( sg_color >> 8 ) & 0xFF )
    b := b + ( sg_color & 0xFF )
  }
  ; Average the values.
  r := ( r / SG_NUM_SCANNED_PIXELS ) & 0xFF
  g := ( g / SG_NUM_SCANNED_PIXELS ) & 0xFF
  b := ( b / SG_NUM_SCANNED_PIXELS ) & 0xFF
  ; return the colour vaue for the square.
  ret_val = ( r << 16 ) + ( g << 8 ) + b
  return %ret_val%
}


Vixay Xavier
  • Guests
  • Last active:
  • Joined: --
I've adapted the above script for use with Puzzle Quest!
Great Script Btw!!

Now I am just hunting for a good algorithm to match 4 of a kind
set priorities and some basic GUI

Try it out, and let me know!

; ==UserScript==
; @name           AutoPuzzleQuest
; @namespace      http://userscripts.org/users/49912
; @description    Script to play a board game (Puzzle Quest)
; @source         http://www.autohotkey.com/forum/topic5663.html
; @identifier     http://www.autohotkey.com/forum/topic5663.html&highlight=bejeweled
; @version        1.0
; @date           2008-06-12
; @creator        ViXaY XaVieR
; @include        Puzzle Quest
; ==/UserScript==
; Original Source: see @source
;
; Proof of concept thang to recognise pieces by colour.
;
; Assumes that all pieces are primary (rgb) or secondary (cmy)
; colors, orange, or grey. Doesn't use PixelSearch, as the
; background may be any colour. Instead, checks the color of a
; single pixel offset within each square to build a map of the
; board layout each turn, then operates on that.
;
; Detection of red/orange/yellow and cyan/blue is really a bit dodgy
; and often wrong.
;
; Copyright is explicitly released upon the public domain.
; This means you may do as you wish with it, without credit.


; ==TO DO==
; trigger to start combat
; autodetection of end
; preferences for 4 in ones and skulls, and for scrolls, or anvils  depending on game mode (BATTLE, TRAIN MOUNT, RESEARCH SPELL, FORGE ITEM)
; GUI for options (type of combat, delay, wait for AI turn ...etc)
;DONE (need to test) check for defeat banner (save it)
; check colors for wild cards and black skulls, scrolls & anvils
; disable wait for my turn in different game modes
; detect game modes by checking on screen
; output grid (in memory) in a neat way
; fix color detection (sometimes it still makes illegal moves)
; scan grid only after our turn indicator has come


^!r::Reload ; pause/reload the script. stop scripts execution

^!t::toggleDelay()

^!d::toggleDebug()

toggleDelay()
{
  global SG_PAUSE
  if ( SG_PAUSE == 1)
    SG_PAUSE=0
  else
    SG_PAUSE=1
}

toggleDebug()
{
  global SG_DEBUG
  if ( SG_DEBUG == 1)
    SG_DEBUG=0
  else
    SG_DEBUG=1
}


^!z::
; Globals.
; These should be gathered on the fly rather than predefined.

; Offset to measure within the square.
SG_OFFSET_X=37
SG_OFFSET_Y=37
; Coords within the window for the top left of the board.
SG_ORIGIN_X=218
SG_ORIGIN_Y=156
; Size of a single square.
SG_SIZE_X=74
SG_SIZE_Y=74
; Number of squares on board.
SG_SQUARES_X=8
SG_SQUARES_Y=8
; Remember last move so we're less likely to get stuck trying the same wrong move forever.
; But if there are TWO wrong moves, then...
SG_LAST_X=0
SG_LAST_Y=0
; Whether this version allows piece dragging rather than click/click.
SG_DRAG_OK=1
; Debugging level
SG_DEBUG=1
; Whether to Pause (for AI turn) or not
SG_PAUSE=1
; Start at top of board or bottom
SG_TOP=1

Loop
{
  getWindow()
  ; Loop Y then X: move across every square, down only once per line.
  sg_line := ("")
  Loop, %SG_SQUARES_Y%
  {
    sg_y := A_Index
    sg_py := yCoordToPixel(A_Index)
    sg_line = %sg_line% `n# %A_Index% :
    Loop, %SG_SQUARES_X%
    {
      sg_x := A_Index
      sg_px := xCoordToPixel(A_Index)
      PixelGetColor, sg_color, %sg_px%, %sg_py%, RGB
      sg_c := hex2Color(sg_color)
      SG_ARR_%sg_x%_%sg_y% := sg_c
      col := SG_ARR_%sg_x%_%sg_y%
      ;MsgBox, SG_ARR_%sg_x%_%sg_y%  = %col%
      sg_line = %sg_line% %sg_c% ( %sg_x% , %sg_y% = %sg_px% , %sg_py% )
    }
    ;MsgBox, %sg_line%
  }
  getMove()
  if ( SG_PAUSE == 1 ) sleep, 1000
  if ( SG_DEBUG == 1 )
  {
    MsgBox, DEBUG %SG_DEBUG% %sg_line%
	clipboard = %sg_line%
	return
  }
  if (gameOver() == true) {
	return
  }
  waitForMyTurn()
}


getWindow()
{
; Window management. This shouldn't care about the window name.
WinWait, Puzzle Quest,
IfWinNotActive, Puzzle Quest, , WinActivate, Puzzle Quest,
WinWaitActive, Puzzle Quest,
}

; Convert a board y square coord to a screen y coord.
; This means our y coords are, internally, all "backwards" - but who cares?
yCoordToPixel(y)
{
  global SG_SIZE_Y
  global SG_ORIGIN_Y
  global SG_OFFSET_Y
  global SG_SQUARES_Y
  ; SG_SQUARES_Y - y as we're working from the bottom for highest scores.
  ;return ( ( SG_SQUARES_Y - y + 1 ) * SG_SIZE_Y ) + SG_ORIGIN_Y - SG_OFFSET_Y
  ; normal screen pixels - to conserve moves (lower risk of no moves)
  if ( SG_TOP == 1 ) 
	return ( y * SG_SIZE_Y ) + SG_ORIGIN_Y - SG_OFFSET_Y
  else 
	return ( ( SG_SQUARES_Y - y + 1 ) * SG_SIZE_Y ) + SG_ORIGIN_Y - SG_OFFSET_Y
}

; Convert a board square x coord to a screen x coord.
xCoordToPixel(x)
{
  global SG_SIZE_X
  global SG_ORIGIN_X
  global SG_OFFSET_X
  return ( x * SG_SIZE_X ) + SG_ORIGIN_X - SG_OFFSET_X
}

; Make the mouse move a piece.
movePiece(x1, y1, x2, y2)
{
  global SG_DRAG_OK
  global SG_ORIGIN_X
  global SG_ORIGIN_Y
  global SG_LAST_X
  global SG_LAST_Y
  SG_LAST_X := x1
  SG_LAST_Y := y1

  ;MsgBox, Moving from %x1% , %y1% to %x2% , %y2%, set last = %SG_LAST_X% , %SG_LAST_Y%
  getWindow()
  if (SG_DRAG_OK == 1)
  {
    MouseClickDrag, L, xCoordToPixel(x1), yCoordToPixel(y1), xCoordToPixel(x2), yCoordToPixel(y2), 2
  }
  else
  {
    MouseClick, L, xCoordToPixel(x1), yCoordToPixel(y1)
    Sleep, 100
    MouseClick, L, xCoordToPixel(x2), yCoordToPixel(y2)
    Sleep, 100
  }
  ; Move to home point to avoid hilighting pieces.
  MouseMove, SG_ORIGIN_X, SG_ORIGIN_Y
  ; Wait for pieces to fall.
  Sleep, 1000
}


; Check that the piece in an array coord is not out of bounds,
; and is the same color.
isSameAt(col, x, y)
{
  ; Generic global to include array.
  global
  if (x < 1 || x > SG_SQUARES_X || y < 1 || y > SG_SQUARES_Y )
  {
  ;MsgBox, returning false as OOB ( %x% < 1 || %x% > %SG_SQUARES_X% || %y% < 1 || %y% > %SG_SQUARES_Y% )
    return false
  }
  ax := SG_ARR_%x%_%y%
  if ( SG_ARR_%x%_%y% == col )
  {
    ;MsgBox, returning true as %ax% == %col% and none of ( %x% < 1 || %x% > %SG_SQUARES_X% || %y% < 1 || %y% > %SG_SQUARES_Y% )
    return true
  }
  ;MsgBox, returning false as %ax% != %col% though none of ( %x% < 1 || %x% > %SG_SQUARES_X% || %y% < 1 || %y% > %SG_SQUARES_Y% )
  return false
}


; An extremely nasty bruteforcism: tries each possible move in turn.
; If it finds one, it does it.
; Unfortunately, for the SG_ARR stuff, need all globals defined.
getMove()
{
  global
  Loop, %SG_SQUARES_Y%
  {
    y = %A_Index%
    py := xCoordToPixel(y)
    y1 := ( y + 1 )
    y2 := ( y + 2 )
    y3 := ( y + 3 )
    y_1 := ( y - 1 )
    y_2 := ( y - 2 )
    y_3 := ( y - 3 )
    Loop, %SG_SQUARES_X%
    {
      x = %A_Index%
      ; Anti-stick: don't play the same piece twice in a row
      ; If it's the only playable square, we'll get it next time round.
      if ( SG_LAST_X == x && SG_LAST_Y == y )
      {
        ; Zero them so we can get this square next time if it's the only one.
        SG_LAST_X = 0
        SG_LAST_Y = 0
        Continue
      }
      px := xCoordToPixel(px)
      x1 := ( x + 1 )
      x2 := ( x + 2 )
      x3 := ( x + 3 )
      x_1 := ( x - 1 )
      x_2 := ( x - 2 )
      x_3 := ( x - 3 )
      col := SG_ARR_%x%_%y%

      ;MsgBox, At %x%, %y% comparing %col% , %x2% , %y% And %col%, %x3%, %y%

      ; X_xx
      if ( isSameAt(col, x2, y) && isSameAt(col, x3, y) )
      {
        movePiece( x, y, x1, y)
        return
      }
      ; xx_X
      else if ( isSameAt(col, x_2, y) && isSameAt(col, x_3, y) )
      {
        movePiece( x, y, x_1, y)
        return
      }
      ; X
      ; _
      ; x
      ; x
      else if ( isSameAt(col, x, y2) && isSameAt(col, x, y3) )
      {
        movePiece( x, y, x, y1)
        return
      }
      ; x
      ; x
      ; _
      ; X
      else if ( isSameAt(col, x, y_2) && isSameAt(col, x, y_3) )
      {
        movePiece( x, y, x, y_1)
        return
      }
      ; x_
      ; x_
      ; _X
      else if ( isSameAt(col, x_1, y_1) && isSameAt(col, x_1, y_2) )
      {
        movePiece( x, y, x_1, y)
        return
      }
      ; _x
      ; _x
      ; X_
      else if ( isSameAt(col, x1, y_1) && isSameAt(col, x1, y_2) )
      {
        movePiece( x, y, x1, y)
        return
      }
      ; _X
      ; x_
      ; x_
      else if ( isSameAt(col, x_1, y1) && isSameAt(col, x_1, y2) )
      {
        movePiece( x, y, x_1, y)
        return
      }
      ; X_
      ; _x
      ; _x
      else if ( isSameAt(col, x1, y1) && isSameAt(col, x1, y2) )
      {
        movePiece( x, y, x1, y)
        return
      }
      ; X__
      ; _xx
      else if ( isSameAt(col, x1, y1) && isSameAt(col, x2, y1) )
      {
        movePiece( x, y, x, y1)
        return
      }
      ; _xx
      ; X__
      else if ( isSameAt(col, x1, y_1) && isSameAt(col, x2, y_1) )
      {
        movePiece( x, y, x, y_1)
        return
      }
      ; __X
      ; xx_
      else if ( isSameAt(col, x_1, y1) && isSameAt(col, x_2, y1) )
      {
        movePiece( x, y, x, y1)
        return
      }
      ; xx_
      ; __X
      else if ( isSameAt(col, x_1, y_1) && isSameAt(col, x_2, y_1) )
      {
        movePiece( x, y, x, y_1)
        return
      }
      ; x_
      ; _X
      ; x_
      else if ( isSameAt(col, x_1, y_1) && isSameAt(col, x_1, y1) )
      {
        movePiece( x, y, x_1, y)
        return
      }
      ; _x
      ; X_
      ; _x
      else if ( isSameAt(col, x1, y_1) && isSameAt(col, x1, y1) )
      {
        movePiece( x, y, x1, y)
        return
      }
      ; _X_
      ; x_x
      else if ( isSameAt(col, x_1, y1) && isSameAt(col, x1, y1) )
      {
        movePiece( x, y, x, y1)
        return
      }
      ; x_x
      ; _X_
      else if ( isSameAt(col, x_1, y_1) && isSameAt(col, x1, y_1) )
      {
        movePiece( x, y, x, y_1)
        return
      }
    }
  }
  ; MsgBox, Unable to find a move
}


; Convert a hex value to a colour letter, from:
; r(ed)c(yan)g(reen)y(ellow)o(range)b(lue)m(agenta)w(hite).
; Per: r rg=o gr=y g gb=c bg=c b rb=m br=m
hex2Color(color)
{
  r := ( ( color >> 16 ) & 0xFF )
  g := ( ( color >> 8 ) & 0xFF )
  b := ( color & 0xFF )
  if (r > g && r > b )
  {
    col = r
    min := ( r * 0.5 )
    if (min <= g && min <= b )
      col=w
    if (min <= g && min > b ) ; dodgy yellow test.
      if (r - g > 10)
        col=o
      else ; numbers too similar, still yellow.
        col=y
    if (min > g && min <= b )
      col=m
  }
  if (g >= r && g >= b )
  {
    col = g
    min := ( g * 0.6 )
    if (min <= r && min <= b )
      col=w
    if (min <= r && min > b )
      col=y
    if (min > r && min <= b )
;      col=c ; commented since there ARE no cyan pieces in the test game
      col=b
  }
  if (b >= r && b >= g )
  {
    col = b
    min := ( b * 0.6 )
    if (min <= r && min <= g )
      col=w
    if (min <= r && min > g )
      col=m
    if (min > r && min <= g )
;      col=c ; commented since there ARE no cyan pieces in the test game
      col=b
  }
   if ( SG_DEBUG == 1 ) {
	MsgBox, Colour %color%=%col% r:%r% g:%g% b:%b%
   }
  return col
}

; Search if combat is over
gameOver()
{
	ImageSearch, FoundX, FoundY, 270, 190, A_ScreenWidth, A_ScreenHeight, victory.bmp
	if (ErrorLevel = 2) {
		;MsgBox Could not conduct the search.
		;return false
	}
	else if (ErrorLevel = 1) {
		;MsgBox Icon could not be found on the screen.
		;return false
	}
	else {
		MsgBox The victory image was found at %FoundX%x%FoundY%.
		return true
	}
	ImageSearch, FoundX, FoundY, 270, 190, A_ScreenWidth, A_ScreenHeight, defeat.bmp
	if (ErrorLevel = 2) {
		;MsgBox Could not conduct the search.
		;return false
	}
	else if (ErrorLevel = 1) {
		;MsgBox Icon could not be found on the screen.
		;return false
	}
	else {
		MsgBox The defeat image was found at %FoundX%x%FoundY%.
		return true
	}
	return false
}

;Return true if indicator is over our head
myTurn()
{
	PixelGetColor, turn_color, 101, 107, RGB
	turn_c := hex2Color(turn_color)
	;MsgBox %turn_color% %turn_c%
	if (turn_c == "o") ;orange = my turn, white = not my turn
		return true
	sleep 100
	return false
}

; To reduce illegal moves
waitForMyTurn()
{
	Loop
	{
		if ((a_index > 100) || (myTurn()))
			break  ; Terminate the loop
		;MsgBox, a_index = %a_index% ; This will display only the numbers 20 through 25
	}
}


vixay
  • Members
  • 59 posts
  • Last active: Nov 06 2014 07:57 AM
  • Joined: 12 Jun 2008
I can't attach the bitmaps here, but basically they are the copped screen cap of the word Victory/Defeat after you play the game.

Wrote this primarily to beat the easy opponents/boring puzzles.