1
#Requires AutoHotkey v2.0
; ============================================================
; FUNCTION: SegmentsMatch
; ============================================================
SegmentsMatch(seg1, seg2, trim := 2, threshold := 20, varThreshold := 15) {
s1 := [], s2 := []
Loop seg1.Length - trim * 2
s1.Push(seg1[A_Index + trim])
Loop seg2.Length - trim * 2
s2.Push(seg2[A_Index + trim])
HexToRGB(hex) {
hex := StrReplace(StrReplace(hex, "0x", ""), "#", "")
n := Integer("0x" hex)
return [(n >> 16) & 255, (n >> 8) & 255, n & 255]
}
AvgRGB(seg) {
r := 0, g := 0, b := 0
for c in seg {
rgb := HexToRGB(c)
r += rgb[1], g += rgb[2], b += rgb[3]
}
n := seg.Length
return [r / n, g / n, b / n]
}
RGBDist(a, b) {
return Sqrt((a[1]-b[1])**2 + (a[2]-b[2])**2 + (a[3]-b[3])**2)
}
StdLuma(seg) {
lumas := []
for c in seg {
rgb := HexToRGB(c)
lumas.Push(0.299 * rgb[1] + 0.587 * rgb[2] + 0.114 * rgb[3])
}
mean := 0
for l in lumas
mean += l
mean /= lumas.Length
variance := 0
for l in lumas
variance += (l - mean)**2
return Sqrt(variance / lumas.Length)
}
if RGBDist(AvgRGB(s1), AvgRGB(s2)) >= threshold
return false
if Abs(StdLuma(s1) - StdLuma(s2)) > varThreshold
return false
return true
}
; ============================================================
; FUNCTION: ScanPixelArrays
; Scans a 7x6 grid, logs each array to scan_log.txt
; Samples pixels in a cross/plus pattern (horizontal arm + vertical arm)
; centered on each cell. Uses a single GDI BitBlt capture.
;
; Cross layout (armLen=20, total=81 pixels per cell):
; horizontal: center + armLen left + armLen right (41 px, left→right)
; vertical: center - armLen above + armLen below (40 px, top→bottom, center excluded)
; Array order: [horiz left→right | vert top→bottom]
; ============================================================
ScanPixelArrays(baseX := 670, baseY := 630, xStep := 64, yStep := 64,
xCount := 7, yCount := 6, armLen := 20) {
arrays := []
logFile := A_ScriptDir "\scan_log.txt"
try FileDelete(logFile)
FileAppend("Scan Log — " FormatTime(, "yyyy-MM-dd HH:mm:ss") "`n", logFile)
FileAppend("========================================`n", logFile)
; --- single GDI capture of the entire grid region (plus arm overhang) ---
captureX := baseX - armLen
captureY := baseY - armLen
captureW := (xCount - 1) * xStep + armLen * 2 + 1
captureH := (yCount - 1) * yStep + armLen * 2 + 1
hScreen := DllCall("GetDC", "Ptr", 0, "Ptr")
hMemDC := DllCall("CreateCompatibleDC", "Ptr", hScreen, "Ptr")
hBitmap := DllCall("CreateCompatibleBitmap", "Ptr", hScreen, "Int", captureW, "Int", captureH, "Ptr")
DllCall("SelectObject", "Ptr", hMemDC, "Ptr", hBitmap)
DllCall("BitBlt", "Ptr", hMemDC, "Int", 0, "Int", 0,
"Int", captureW, "Int", captureH,
"Ptr", hScreen, "Int", captureX, "Int", captureY, "UInt", 0x00CC0020) ; SRCCOPY
ReadPixel(hDC, bx, by) {
raw := DllCall("GetPixel", "Ptr", hDC, "Int", bx, "Int", by, "UInt")
r := (raw & 0xFF)
g := (raw >> 8) & 0xFF
b := (raw >> 16) & 0xFF
return Format("0x{:06X}", (r << 16) | (g << 8) | b)
}
Loop yCount {
yIndex := A_Index - 1
y := baseY + (yIndex * yStep)
; bitmap coords — offset by the top-left of our capture rect
bmpCY := armLen + (yIndex * yStep) ; center row in bitmap
Loop xCount {
xIndex := A_Index - 1
x := baseX + (xIndex * xStep)
bmpCX := armLen + (xIndex * xStep) ; center col in bitmap
index := (yIndex * xCount) + xIndex + 1
colors := []
; horizontal arm: left → right across center row
Loop armLen * 2 + 1
colors.Push(ReadPixel(hMemDC, bmpCX - armLen + (A_Index - 1), bmpCY))
; vertical arm: top → bottom, skip center (already in horizontal)
Loop armLen {
colors.Push(ReadPixel(hMemDC, bmpCX, bmpCY - armLen + (A_Index - 1)))
}
Loop armLen {
colors.Push(ReadPixel(hMemDC, bmpCX, bmpCY + A_Index))
}
arrays.Push(colors)
colorStr := ""
for c in colors
colorStr .= c " "
uniform := IsUniform(colors) ? "UNIFORM" : "ok"
logLine := "[" index "]`t"
. "col=" (xIndex+1) " row=" (yIndex+1) "`t"
. "x=" x " y=" y "`t"
. uniform "`t"
. Trim(colorStr) "`n"
FileAppend(logLine, logFile)
}
FileAppend("`n", logFile) ; blank line between rows
}
; --- clean up GDI resources ---
DllCall("DeleteObject", "Ptr", hBitmap)
DllCall("DeleteDC", "Ptr", hMemDC)
DllCall("ReleaseDC", "Ptr", 0, "Ptr", hScreen)
FileAppend("========================================`n", logFile)
FileAppend("Total arrays: " arrays.Length "`n", logFile)
return arrays
}
; ============================================================
; FUNCTION: IsUniform
; ============================================================
IsUniform(arr, lumaThreshold := 10) {
HexToRGB(hex) {
hex := StrReplace(StrReplace(hex, "0x", ""), "#", "")
n := Integer("0x" hex)
return [(n >> 16) & 255, (n >> 8) & 255, n & 255]
}
lumas := []
for c in arr {
rgb := HexToRGB(c)
lumas.Push(0.299 * rgb[1] + 0.587 * rgb[2] + 0.114 * rgb[3])
}
mean := 0
for l in lumas
mean += l
mean /= lumas.Length
variance := 0
for l in lumas
variance += (l - mean)**2
return Sqrt(variance / lumas.Length) < lumaThreshold
}
; ============================================================
; FUNCTION: FilterUniform
; ============================================================
FilterUniform(arrays) {
filtered := []
removed := 0
for arr in arrays {
if IsUniform(arr)
removed++
else
filtered.Push(arr)
}
return { filtered: filtered, removed: removed }
}
; ============================================================
; FUNCTION: FindAllMatches
; ============================================================
FindAllMatches(arrays, trim := 2, threshold := 20, varThreshold := 15) {
matches := []
total := arrays.Length
used := Map()
Loop total - 1 {
i := A_Index
if used.Has(i) || IsUniform(arrays[i])
continue
Loop total - i {
j := i + A_Index
if used.Has(j) || IsUniform(arrays[j])
continue
if SegmentsMatch(arrays[i], arrays[j], trim, threshold, varThreshold) {
x1 := 670 + Mod(i - 1, 7) * 64
y1 := 625 + Floor((i - 1) / 7) * 64
x2 := 670 + Mod(j - 1, 7) * 64
y2 := 625 + Floor((j - 1) / 7) * 64
; MsgBox("A: " x1 "," y1 " | B: " x2 "," y2)
MouseClickDrag("Left", x1, y1, x2, y2)
Sleep(100)
used.Set(i, true)
used.Set(j, true)
break
}
}
}
;MsgBox("Done")
return matches
}
; ============================================================
; MAIN — press F12 to run
; ============================================================
F12:: {
arrays := ScanPixelArrays()
;Tooltip("Scan complete")
matches := FindAllMatches(arrays)
Tooltip("Done")
SetTimer(() => ToolTip(), -2000)
}
For immediate assistance, please email our customer support: [email protected]