AutoHotkey Community

It is currently May 27th, 2012, 6:36 am

All times are UTC [ DST ]




Post new topic Reply to topic  [ 6 posts ] 
Author Message
 Post subject: Bejewelled 2 autoplay.
PostPosted: October 4th, 2005, 5:17 am 
Offline

Joined: October 3rd, 2005, 2:42 am
Posts: 186
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.


Code:
; 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.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: October 4th, 2005, 11:56 am 
Offline

Joined: March 2nd, 2004, 3:36 pm
Posts: 10720
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.


Report this post
Top
 Profile  
Reply with quote  
 Post subject: averaging
PostPosted: January 13th, 2006, 2:09 pm 
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.

Code:

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


Report this post
Top
  
Reply with quote  
 Post subject:
PostPosted: January 13th, 2006, 6:08 pm 
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.org/wiki/Eight_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):
Code:
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%
}


Report this post
Top
  
Reply with quote  
PostPosted: June 12th, 2008, 10:27 am 
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!

Code:
; ==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
   }
}


Report this post
Top
  
Reply with quote  
 Post subject: Attachments
PostPosted: June 12th, 2008, 10:33 am 
Offline

Joined: June 12th, 2008, 10:29 am
Posts: 52
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.


Report this post
Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 6 posts ] 

All times are UTC [ DST ]


Who is online

Users browsing this forum: Apollo and 20 guests


You can post new topics in this forum
You can reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Powered by phpBB® Forum Software © phpBB Group