BombSniffer

Klasik Mayın Tarlası oyununu kendi başına başarılı olana kadar çözen bir yapay zeka botu.


Python pyAutoGUI
projectDetails.md

# Proje Özeti

Bir gün Youtube'da gezinirken 'Code Bullet' (https://www.youtube.com/@CodeBullet) kanalında gördüğüm, Mayın Tarlası çözme rekoru kırmak için bir bot geliştirdiği bir video gördüm. Bende oradan esinlenerek kendi botumu kendi algoritmamla geliştirmek istedim.

Proje Ekran Görüntüsü Video 1: Uygulamanın aktif çalışma görüntüsü.

Bot şu şekilde çalışıyor. İlk önce 16x16'lık bir mayın tarlası olduğunu yazılıma anlatmak için 16x16'lık bir matris oluşturup her hücrenin içeriğini (sayı veya bayrak gibi) bu matrise kaydediyoruz. Daha sonrasında PyAutoGUI (https://pyautogui.readthedocs.io/) kütüphanesi ile ekrandaki fare imlecimizi güvenli hücrelere götüttürüp bastırıyoruz. Mayın tespit edilen hücrelere ise Bayrak koydurup oyunu kazanmaya çalışıyoruz.

Tabii ki bu bot her oyunda %100 kazanma vadetmiyor. Hiç bir mayın tarlası oyunu %100 olarak çözülecek diye bir şey yoktur çünkü. Belirli oyunlarda oyununuz açılmaz ve rastgele tıklamak zorunda kalırsınız. O rastgele tıkladığınız hücrenin mayın çıkma olasılığı 16x16'lık oyunda 40/216'dır. Yani hiç bir şekilde %100 olarak kazanamazsınız. Bu bot ise bunun birazcık önüne geçiyor ve her hücre komşuları için en güvenli hücreyi seçerek oyunda hızla ilerliyor.

Çalışma mantığı ise şöyle: İlk olarak rastgele bir hücre seçip oyuna başlıyor. Daha sonrasında eğer açılmış ise güvenli hücreleri tespit ederek klasik yöntemle mayın tarlasını çözüyor. Ama eğer açılmaz ve tek hücre açık olursa rastgele bir hücreye tıklıyor ve açılıp açılmama durumuna göre bu döngü devam ediyor.

Eğer yanarsa oyunu otomatik olarak yeniden başlatıyor ve kazanana kadar devam ediyor.

# Kullanımı

Bu botu kullanmak için ilk önce bilgisayarınızda Python 3+ ve PyAutoGUI kütüphanesi kurulu olmalı. Daha sonrasında Windows görüntü ayarlarınızda 1920x1080 çözünürlük ve %125 ölçeğe getirmeniz gerekiyor. Sonrasında ise tarayıcınızdan https://minesweeper.online/ sitesine gidip 'Intermediate' seçeneğini seçiyoruz. Sadece sağ üstte bulunan ayarlar çarkına girip, 'Skin' seçeneğini 'Low Resolution' seçip altındaki 'Pixelated' seçeneğini seçiyoruz. Bu ayarlar botun çalışma mantığındaki pixel bulma fonksiyonlarının doğru çalışabilmesi için çok önemli. Bu ayarları yaptıktan sonra sayfanın altında bulunan 'Return to the game' seçeneği ile oyuna dönüyoruz. Artık botu kullanmaya hazırız. Oyun ekranda kalacak şekilde scripti çalıştırmanız yeterli olacaktır. Bot bazen bug'a girip hata yapmasına rağmen oyunu yeniden başlatıp tıklamalar yapmaya devam edecektir. Bu durumda farenizi ekranınızın sol üst köşesine götürürseniz bot duracaktır. Sonrasında yeniden botu başlatabilirsiniz.

Kullanamadığınız veya anlamadığınız durumda bana ulaşmaktan lütfen çekinmeyin. İyi çalışmalar.

# Scriptler

bombsniffer.py python
import pyautogui
import time
import random

pyautogui.PAUSE = 0
pyautogui.MINIMUM_DURATION = 0

# Hücre içi sayılar için renk → sayı eşlemesi
RENKLER = {
    (0, 0, 247): 1,
    (0, 119, 0): 2,
    (236, 0, 0): 3,
    (0, 0, 128): 4,
    (128, 0, 0): 5,
    (0, 128, 128): 6,
    (0, 0, 0): 7,
    (128, 128, 128): 8
}

# Sabit ayarlar
check_offset_x = 601
check_offset_y = 427
cell_offset = 30
probe_dx = 15
probe_dy = 20
GRID_SIZE = 16

# Kazanma ve kaybetme pikselleri
win_pixel = (838, 367)
lose_pixel = (831, 378)
restart_click = (839, 368)

# İlk tıklama koordinatları
first_x = check_offset_x + 7 * cell_offset
first_y = check_offset_y + 7 * cell_offset

# Oyun başlatıcı
def oyunu_yeniden_baslat():
    pyautogui.click(*restart_click)
    time.sleep(0.2)
    pyautogui.click(first_x, first_y)
    time.sleep(0.2)

# Oyun durumu kontrolü
def oyun_durumu():
    win = pyautogui.pixel(*win_pixel)
    lose = pyautogui.pixel(*lose_pixel)
    if win == (0, 0, 0):
        return "win"
    elif lose == (0, 0, 0):
        return "lose"
    return "ongoing"

# Komşuları döndür
def komsular(x, y):
    return [
        (x + dx, y + dy)
        for dx in [-1, 0, 1]
        for dy in [-1, 0, 1]
        if not (dx == 0 and dy == 0) and 0 <= x + dx < GRID_SIZE and 0 <= y + dy < GRID_SIZE
    ]

# Hücre tıklama
def tıkla(x, y, sağ=False):
    cx = check_offset_x + x * cell_offset
    cy = check_offset_y + y * cell_offset
    if sağ:
        pyautogui.rightClick(cx, cy)
    else:
        pyautogui.click(cx, cy)

# Ana döngü
while True:
    oyunu_yeniden_baslat()

    while True:
        durum = oyun_durumu()
        if durum == "win":
            exit()
        elif durum == "lose":
            break

        screenshot = pyautogui.screenshot()
        matris = []

        for y in range(GRID_SIZE):
            satir = []
            for x in range(GRID_SIZE):
                cx = check_offset_x + x * cell_offset
                cy = check_offset_y + y * cell_offset
                renk = screenshot.getpixel((cx, cy))

                if renk == (255, 255, 255):  # Kapalı hücre
                    probe = screenshot.getpixel((cx + probe_dx, cy + probe_dy))
                    if probe == (0, 0, 0):  # Bayrak
                        satir.append('F')
                    else:
                        satir.append('-')
                else:
                    probe = screenshot.getpixel((cx + probe_dx, cy + probe_dy))
                    sayi = RENKLER.get(probe, 0)
                    satir.append(str(sayi))
            matris.append(satir)

        # Açık hücre çok azsa, rastgele tıkla
        acik_sayisi = sum(1 for row in matris for h in row if h not in ('-', 'F'))
        if acik_sayisi < 2:
            kapali = [(x, y) for y in range(GRID_SIZE) for x in range(GRID_SIZE) if matris[y][x] == '-']
            if kapali:
                random.shuffle(kapali)
                tıkla(*kapali[0])
                time.sleep(0.01)
            continue

        sag_tikla = set()
        sol_tikla = set()

        for y in range(GRID_SIZE):
            for x in range(GRID_SIZE):
                merkez = matris[y][x]
                if merkez in '12345678':
                    m = int(merkez)
                    kapali_komsu = []
                    bayrak_say = 0

                    for nx, ny in komsular(x, y):
                        val = matris[ny][nx]
                        if val == '-':
                            kapali_komsu.append((nx, ny))
                        elif val == 'F':
                            bayrak_say += 1

                    if bayrak_say == m:
                        sol_tikla.update(kapali_komsu)
                    elif len(kapali_komsu) + bayrak_say == m:
                        sag_tikla.update(kapali_komsu)

        for (x, y) in sag_tikla:
            tıkla(x, y, sağ=True)
            time.sleep(0.003)

        for (x, y) in sol_tikla:
            tıkla(x, y)
            time.sleep(0.003)

        # Eğer tıklanacak hücre kalmadıysa, rastgele seç
        if not sag_tikla and not sol_tikla:
            kapali = [(x, y) for y in range(GRID_SIZE) for x in range(GRID_SIZE) if matris[y][x] == '-']
            if kapali:
                random.shuffle(kapali)
                tıkla(*kapali[0])
                time.sleep(0.005)