Загрузка данных


import sys, time, math, os, ctypes
import tkinter as tk
from typing import Dict, Tuple, Optional
from PIL import Image
import mss

VK_F7 = 0x76
VK_ESCAPE = 0x1B

def is_key_pressed(vk_code: int) -> bool:
    try:
        return bool(ctypes.windll.user32.GetAsyncKeyState(vk_code) & 0x8000)
    except Exception:
        return False

class MinesweeperBitboard:
    def __init__(self, rows: int, cols: int):
        self.rows = rows
        self.cols = cols
        self.unopened_mask = 0
        self.flags_mask = 0
        self.opened_empty_mask = 0
        self.numbers_masks: Dict[int, int] = {num: 0 for num in range(1, 9)}

    def cell_to_index(self, row: int, col: int) -> int:
        return row * self.cols + col

    def set_bit(self, mask: int, row: int, col: int) -> int:
        return mask | (1 << self.cell_to_index(row, col))

    def get_bit(self, mask: int, row: int, col: int) -> bool:
        return bool((mask >> self.cell_to_index(row, col)) & 1)

    def print_mask(self, mask: int, title: str = "Bitmask") -> None:
        print(f"\n--- {title} ({self.rows}x{self.cols}) ---")
        col_header = "    " + "".join([str(c % 10) for c in range(self.cols)])
        print(col_header)
        print("   +" + "-" * self.cols + "+")

        for r in range(self.rows):
            row_str = f"{r:2d} |"
            for c in range(self.cols):
                row_str += "1" if self.get_bit(mask, r, c) else "0"
            print(row_str + "|")

        print("   +" + "-" * self.cols + "+")
        print(f"Dec: {mask}\nHex: 0x{mask:X}")

    def print_board_summary(self) -> None:
        print(f"\nBOARD ({self.rows}x{self.cols}):")
        if self.cols <= 10:
            col_header = "     " + " ".join([str(c) for c in range(self.cols)])
        else:
            col_header = "     " + " ".join([str(c // 10) if c >= 10 else " " for c in range(self.cols)]) + "\n     " + " ".join([str(c % 10) for c in range(self.cols)])

        print(col_header)
        border = "   +" + "-" * (self.cols * 2 + 1) + "+"
        print(border)

        for r in range(self.rows):
            row_cells = []
            for c in range(self.cols):
                if self.get_bit(self.flags_mask, r, c):
                    row_cells.append("!")
                elif self.get_bit(self.unopened_mask, r, c):
                    row_cells.append("?")
                elif self.get_bit(self.opened_empty_mask, r, c):
                    row_cells.append("0")
                else:
                    found = None
                    for num in range(1, 9):
                        if self.get_bit(self.numbers_masks[num], r, c):
                            found = str(num)
                            break
                    row_cells.append(found if found else "E")

            print(f"{r:2d} | " + " ".join(row_cells) + f" | {r:2d}")

        print(border)
        print(col_header)

    def get_fingerprint(self) -> Tuple:
        return (self.unopened_mask, self.flags_mask, self.opened_empty_mask, tuple(self.numbers_masks[n] for n in range(1, 9)))

class ScreenSelector:
    def __init__(self):
        self.selected_region: Optional[Tuple[int, int, int, int]] = None

    def select_area(self) -> Optional[Tuple[int, int, int, int]]:
        root = tk.Tk()
        root.attributes("-fullscreen", True)
        root.attributes("-topmost", True)
        try:
            root.attributes("-alpha", 0.35)
        except Exception:
            pass

        canvas = tk.Canvas(root, highlightthickness=0, bg="#000000", cursor="cross")
        canvas.pack(fill=tk.BOTH, expand=True)

        rect_id = canvas.create_rectangle(0, 0, 0, 0, outline="#00FF00", width=2)
        start_x = start_y = 0

        def on_down(e):
            nonlocal start_x, start_y
            start_x, start_y = e.x, e.y

        def on_drag(e):
            canvas.coords(rect_id, start_x, start_y, e.x, e.y)

        def on_up(e):
            x1, x2 = min(start_x, e.x), max(start_x, e.x)
            y1, y2 = min(start_y, e.y), max(start_y, e.y)
            if (x2 - x1) > 10 and (y2 - y1) > 10:
                self.selected_region = (x1, y1, x2 - x1, y2 - y1)

        canvas.bind("<ButtonPress-1>", on_down)
        canvas.bind("<B1-Motion>", on_drag)
        canvas.bind("<ButtonRelease-1>", on_up)
        root.bind("<Escape>", lambda e: root.destroy())
        root.bind("<Return>", lambda e: root.destroy())
        root.bind("<Double-Button-1>", lambda e: root.destroy())

        root.mainloop()
        return self.selected_region

class CellAnalyzer:
    COLOR_MAP = {
        1: (0, 0, 255), 2: (0, 128, 0), 3: (255, 0, 0), 4: (0, 0, 128),
        5: (128, 0, 0), 6: (0, 128, 128), 7: (0, 0, 0), 8: (128, 128, 128)
    }

    @classmethod
    def classify_cell(cls, cell_img: Image.Image) -> str:
        w, h = cell_img.size
        if w < 4 or h < 4:
            return 'UNOPENED'

        img = cell_img.convert("RGB")
        pixels = img.load()

        tl = [pixels[x, y] for x in range(1, min(w, 5)) for y in range(1, min(h, 5))]
        br = [pixels[x, y] for x in range(max(0, w - 5), w - 1) for y in range(max(0, h - 5), h - 1)]

        tl_avg = sum(sum(p) / 3.0 for p in tl) / max(len(tl), 1)
        br_avg = sum(sum(p) / 3.0 for p in br) / max(len(br), 1)

        is_beveled = (tl_avg - br_avg) > 35.0

        mx, my = max(1, int(w * 0.2)), max(1, int(h * 0.2))
        inner = [pixels[x, y] for x in range(mx, w - mx) for y in range(my, h - my)]

        if not inner:
            return 'UNOPENED' if is_beveled else 'EMPTY'

        flags = sum(1 for (r, g, b) in inner if r > 160 and g < 70 and b < 70)
        if flags > len(inner) * 0.05:
            return 'FLAG'

        if is_beveled:
            return 'UNOPENED'

        counts = {n: 0 for n in range(1, 9)}
        for r, g, b in inner:
            if abs(r - g) < 15 and abs(g - b) < 15 and abs(r - b) < 15 and r > 150:
                continue
            best_num, min_dist = None, 80.0
            for num, ref in cls.COLOR_MAP.items():
                dist = math.sqrt((r - ref[0])**2 + (g - ref[1])**2 + (b - ref[2])**2)
                if dist < min_dist:
                    min_dist, best_num = dist, num
            if best_num:
                counts[best_num] += 1

        req = max(3, int(len(inner) * 0.025))
        top_num = max(counts, key=counts.get)
        return str(top_num) if counts[top_num] >= req else 'EMPTY'

def analyze_screen(rows: int, cols: int, region: Tuple[int, int, int, int]) -> MinesweeperBitboard:
    rx, ry, rw, rh = region
    with mss.mss() as sct:
        sct_img = sct.grab({"top": ry, "left": rx, "width": rw, "height": rh})
        img = Image.frombytes("RGB", sct_img.size, sct_img.bgra, "raw", "BGRX")

    board = MinesweeperBitboard(rows, cols)
    cw, ch = rw / cols, rh / rows

    for r in range(rows):
        for c in range(cols):
            crop = img.crop((int(c * cw), int(r * ch), int((c + 1) * cw), int((r + 1) * ch)))
            st = CellAnalyzer.classify_cell(crop)
            if st == 'UNOPENED':
                board.unopened_mask = board.set_bit(board.unopened_mask, r, c)
            elif st == 'FLAG':
                board.flags_mask = board.set_bit(board.flags_mask, r, c)
            elif st == 'EMPTY':
                board.opened_empty_mask = board.set_bit(board.opened_empty_mask, r, c)
            elif st.isdigit():
                num = int(st)
                if 1 <= num <= 8:
                    board.numbers_masks[num] = board.set_bit(board.numbers_masks[num], r, c)
    return board

def get_grid_size() -> Tuple[int, int]:
    print("1) 9x9 | 2) 16x16 | 3) 16x30 | 4) Custom")
    ch = input("Выбор [1-4] (def: 1): ").strip()
    if ch == "2": return 16, 16
    if ch == "3": return 16, 30
    if ch == "4":
        try: return max(1, int(input("N: "))), max(1, int(input("M: ")))
        except Exception: pass
    return 9, 9

if __name__ == '__main__':
    rows, cols = get_grid_size()
    print("Выделите зону мышкой. [F7] - Новая зона | [ESC] - Выход")
    region = ScreenSelector().select_area()

    last_fp = None
    f7_prev = False

    while True:
        if is_key_pressed(VK_ESCAPE):
            break

        f7_now = is_key_pressed(VK_F7)
        if f7_now and not f7_prev:
            time.sleep(0.2)
            new_reg = ScreenSelector().select_area()
            if new_reg:
                region, last_fp = new_reg, None
        f7_prev = f7_now

        if region:
            board = analyze_screen(rows, cols, region)
            fp = board.get_fingerprint()
            if fp != last_fp:
                last_fp = fp
                os.system('cls' if os.name == 'nt' else 'clear')
                print(f"[F7] Новая зона | [ESC] Выход | {rows}x{cols} | {time.strftime('%H:%M:%S')}")
                board.print_board_summary()
                board.print_mask(board.unopened_mask, "UNOPENED (?)")
                board.print_mask(board.flags_mask, "FLAGS (!)")
                board.print_mask(board.opened_empty_mask, "EMPTY (0)")
                for n in range(1, 9):
                    if board.numbers_masks[n]:
                        board.print_mask(board.numbers_masks[n], f"NUMBER ({n})")

        time.sleep(0.33)