🔴🟡 Connect4 Oyunu

Bir kodun hikayesi: Sıfırdan profesyonel bir oyuna...

25+ Adım Algoritma Odaklı OOP & Animasyon
Giriş

Ne Yapıyoruz?

Connect4 (Dörtlü Bağla), iki oyuncunun sırayla taş düşürdüğü klasik bir strateji oyunudur. Amaç: Yatay, dikey veya çapraz olarak 4 taşı yan yana getirmek!

Bu tutorial'da p5.js kullanarak adım adım tam fonksiyonel bir Connect4 oyunu yapacağız:

📐
Bölüm 1: Grid çizimi ve temel yapı
→
🖱️
Bölüm 2: Tıklama ve taş düşürme
→
🧮
Bölüm 3: Kazanma algoritması
→
⚙️
Bölüm 4: Parametrik yapı
→
🎨
Bölüm 5: Animasyon ve görsel efektler
→
🏆
Final: Skor sistemi ve OOP

🎯 Hedef: 7x6 Connect4 Oyunu

Oyuncu 1 (Kırmızı)
Oyuncu 2 (Sarı)
Adım 1

Canvas Oluşturma

Her p5.js projesinde olduğu gibi, önce canvas'ımızı oluşturuyoruz. Connect4 için 7 sütun ve 6 satır gerekiyor.

📐 Boyut Hesabı

  • Her hücre: 80x80 piksel
  • 7 sütun × 80 = 560 piksel genişlik
  • 6 satır × 80 = 480 piksel yükseklik
ADIM 1 sketch.js
function setup() {
    createCanvas(560, 480);
}

function draw() {
    background(0, 100, 200); // Mavi arka plan
}
💡 Neden Mavi?

Gerçek Connect4 oyun tahtaları genellikle mavi plastikten yapılır. Bu renk seçimi oyuna otantik bir görünüm kazandırır.

📂 Adım 1 Sonrası Tam Kod
sketch.js - Tam Kod (Adım 1)
// Adım 1: Temel Canvas
function setup() {
    createCanvas(560, 480);
}

function draw() {
    background(0, 100, 200);
}
Adım 2

Grid Çizimi - İç İçe Döngüler

Şimdi 7x6'lık grid'imizi çizelim. Her hücre için bir daire çizeceğiz.

🔄 İç İçe Döngü Mantığı

2D grid çizmek için iki döngü kullanıyoruz:

Dış Döngü (satır) satır: 0, 1, 2, 3, 4, 5
↓
İç Döngü (sütun) Her satır için: sütun: 0, 1, 2, 3, 4, 5, 6
ADIM 2 sketch.js
function setup() {
    createCanvas(560, 480);
}

function draw() {
    background(0, 100, 200);
    
    // Grid çizimi
    for (let satir = 0; satir < 6; satir++) {
        for (let sutun = 0; sutun < 7; sutun++) {
            fill(255); // Beyaz
            let x = sutun * 80 + 40;
            let y = satir * 80 + 40;
            ellipse(x, y, 70, 70);
        }
    }
}
📐 Koordinat Hesabı
0
0
x = sütun × 80 + 40 = 40
y = satır × 80 + 40 = 40

+40 ekleyerek dairenin merkezini hücrenin ortasına getiriyoruz (80/2 = 40)

📂 Adım 2 Sonrası Tam Kod
sketch.js - Tam Kod (Adım 2)
// Adım 2: Grid Çizimi
function setup() {
    createCanvas(560, 480);
}

function draw() {
    background(0, 100, 200);
    drawBoard();
}

function drawBoard() {
    for (let satir = 0; satir &lt; 6; satir++) {
        for (let sutun = 0; sutun &lt; 7; sutun++) {
            let x = sutun * 80 + 40;
            let y = satir * 80 + 40;
            fill(255);
            noStroke();
            ellipse(x, y, 70, 70);
        }
    }
}
Adım 3

Oyun Tahtası Dizisi (2D Array)

Görsel grid'i çizdik, ama oyun durumunu nerede tutacağız? Bunun için 2 boyutlu bir dizi (2D array) kullanacağız.

🎲 gameBoard Dizisi

Her hücre 3 durumdan birinde olabilir:

  • 0 = Boş hücre
  • 1 = Oyuncu 1 (Kırmızı)
  • 2 = Oyuncu 2 (Sarı)

📦 gameBoard Yapısı

gameBoard = [
[0, 0, 0, 0, 0, 0, 0], // Satır 0 (en üst)
[0, 0, 0, 0, 0, 0, 0], // Satır 1
[0, 0, 0, 0, 0, 0, 0], // Satır 2
[0, 0, 0, 0, 0, 0, 0], // Satır 3
[0, 0, 0, 0, 0, 0, 0], // Satır 4
[0, 0, 0, 0, 0, 0, 0] // Satır 5 (en alt)
]
ADIM 3 sketch.js
let gameBoard = [];

function setup() {
    createCanvas(560, 480);
    
    // 6 satır, 7 sütunluk boş tahta oluştur
    for (let satir = 0; satir < 6; satir++) {
        gameBoard[satir] = [];
        for (let sutun = 0; sutun < 7; sutun++) {
            gameBoard[satir][sutun] = 0; // Başlangıçta boş
        }
    }
    
    print(gameBoard);
}

function draw() {
    background(0, 100, 200);
    drawBoard();
}

function drawBoard() {
    for (let satir = 0; satir < 6; satir++) {
        for (let sutun = 0; sutun < 7; sutun++) {
            let x = sutun * 80 + 40;
            let y = satir * 80 + 40;
            
            // Hücre durumuna göre renk seç
            if (gameBoard[satir][sutun] == 0) {
                fill(255); // Beyaz (boş)
            } else if (gameBoard[satir][sutun] == 1) {
                fill(255, 0, 0); // Kırmızı
            } else {
                fill(255, 255, 0); // Sarı
            }
            
            ellipse(x, y, 70, 70);
        }
    }
}
📂 Adım 3 Sonrası Tam Kod
sketch.js - Tam Kod (Adım 3)
// Adım 3: Oyun Tahtası Dizisi
let gameBoard = [];

function setup() {
    createCanvas(560, 480);

    // 6 satır, 7 sütunluk boş tahta oluştur
    for (let satir = 0; satir &lt; 6; satir++) {
        gameBoard[satir] = [];
        for (let sutun = 0; sutun &lt; 7; sutun++) {
            gameBoard[satir][sutun] = 0;
        }
    }
}

function draw() {
    background(0, 100, 200);
    drawBoard();
}

function drawBoard() {
    for (let satir = 0; satir &lt; 6; satir++) {
        for (let sutun = 0; sutun &lt; 7; sutun++) {
            let x = sutun * 80 + 40;
            let y = satir * 80 + 40;

            if (gameBoard[satir][sutun] == 0) {
                fill(255);            // Boş = Beyaz
            } else if (gameBoard[satir][sutun] == 1) {
                fill(255, 0, 0);      // Oyuncu 1 = Kırmızı
            } else {
                fill(255, 255, 0);    // Oyuncu 2 = Sarı
            }

            noStroke();
            ellipse(x, y, 70, 70);
        }
    }
}
Adım 4

Tıklama Algılama

Oyuncunun hangi sütuna tıkladığını algılamamız gerekiyor. mousePressed() fonksiyonu ve mouseX değişkeni bunu sağlar.

🖱️ Sütun Hesaplama

Mouse X koordinatından sütun numarasını bulmak için:

sutun = floor(mouseX / 80)

Örnek: mouseX = 250
sutun = floor(250 / 80) = floor(3.125) = 3
ADIM 4 sketch.js
// ... önceki kod ...

function mousePressed() {
    let sutun = floor(mouseX / 80);
    print("Tıklanan sütun:", sutun);
}
🖱️ Sütun Algılama Demo
Bir sütuna tıklayın!
📂 Adım 4 Sonrası Tam Kod
sketch.js - Tam Kod (Adım 4)
// Adım 4: Tıklama Algılama
let gameBoard = [];

function setup() {
    createCanvas(560, 480);

    for (let satir = 0; satir &lt; 6; satir++) {
        gameBoard[satir] = [];
        for (let sutun = 0; sutun &lt; 7; sutun++) {
            gameBoard[satir][sutun] = 0;
        }
    }
}

function draw() {
    background(0, 100, 200);
    drawBoard();
}

function drawBoard() {
    for (let satir = 0; satir &lt; 6; satir++) {
        for (let sutun = 0; sutun &lt; 7; sutun++) {
            let x = sutun * 80 + 40;
            let y = satir * 80 + 40;

            if (gameBoard[satir][sutun] == 0) fill(255);
            else if (gameBoard[satir][sutun] == 1) fill(255, 0, 0);
            else fill(255, 255, 0);

            noStroke();
            ellipse(x, y, 70, 70);
        }
    }
}

function mousePressed() {
    let sutun = floor(mouseX / 80);
    print("Tıklanan sütun:", sutun);
}
Adım 5

Taş Düşürme Mantığı

Connect4'te taşlar yukarıdan düşer ve en alttaki boş hücreye yerleşir. Bu mantığı kodlamamız gerekiyor.

⬇️ Düşürme Algoritması

  1. Seçilen sütunun en altından başla (satır 5)
  2. Yukarı doğru ilerle (satır 4, 3, 2, 1, 0)
  3. İlk boş hücreyi (değeri 0 olan) bul
  4. O hücreye taşı yerleştir
Satır 5 (en alt) Boş mu? → Hayır (dolu)
Satır 4 Boş mu? → Hayır (dolu)
Satır 3 Boş mu? → EVET! Buraya koy
ADIM 5 sketch.js
let gameBoard = [];
let currentPlayer = 1; // 1 veya 2

function setup() {
    createCanvas(560, 480);
    initBoard();
}

function initBoard() {
    for (let satir = 0; satir < 6; satir++) {
        gameBoard[satir] = [];
        for (let sutun = 0; sutun < 7; sutun++) {
            gameBoard[satir][sutun] = 0;
        }
    }
}

function draw() {
    background(0, 100, 200);
    drawBoard();
}

function mousePressed() {
    let sutun = floor(mouseX / 80);
    
    // Sütun geçerli mi kontrol et
    if (sutun >= 0 && sutun < 7) {
        dropPiece(sutun);
    }
}

function dropPiece(sutun) {
    // En alttan başlayarak yukarı doğru boş hücre ara
    for (let satir = 5; satir >= 0; satir--) {
        if (gameBoard[satir][sutun] == 0) {
            gameBoard[satir][sutun] = currentPlayer;
            
            // Sırayı değiştir
            if (currentPlayer == 1) {
                currentPlayer = 2;
            } else {
                currentPlayer = 1;
            }
            
            return; // Taş yerleşti, fonksiyondan çık
        }
    }
    // Buraya gelirse sütun dolu demektir
    print("Sütun dolu!");
}

// drawBoard fonksiyonu aynı...
💡 return Kullanımı

return; komutu fonksiyonu hemen sonlandırır. Boş hücre bulunduğunda taşı yerleştirip çıkıyoruz - böylece döngü devam etmiyor ve taş sadece bir yere konuyor.

📂 Adım 5 Sonrası Tam Kod
sketch.js - Tam Kod (Adım 5)
// Adım 5: Taş Bırakma
let gameBoard = [];
let currentPlayer = 1;  // 1 = Kırmızı, 2 = Sarı

function setup() {
    createCanvas(560, 480);

    for (let satir = 0; satir &lt; 6; satir++) {
        gameBoard[satir] = [];
        for (let sutun = 0; sutun &lt; 7; sutun++) {
            gameBoard[satir][sutun] = 0;
        }
    }
}

function draw() {
    background(0, 100, 200);
    drawBoard();
}

function drawBoard() {
    for (let satir = 0; satir &lt; 6; satir++) {
        for (let sutun = 0; sutun &lt; 7; sutun++) {
            let x = sutun * 80 + 40;
            let y = satir * 80 + 40;

            if (gameBoard[satir][sutun] == 0) fill(255);
            else if (gameBoard[satir][sutun] == 1) fill(255, 0, 0);
            else fill(255, 255, 0);

            noStroke();
            ellipse(x, y, 70, 70);
        }
    }
}

function mousePressed() {
    let sutun = floor(mouseX / 80);
    if (sutun &gt;= 0 &amp;&amp; sutun &lt; 7) {
        dropPiece(sutun);
    }
}

function dropPiece(sutun) {
    for (let satir = 5; satir &gt;= 0; satir--) {
        if (gameBoard[satir][sutun] == 0) {
            gameBoard[satir][sutun] = currentPlayer;
            currentPlayer = (currentPlayer == 1) ? 2 : 1;
            return;
        }
    }
    print("Sütun dolu!");
}
Adım 6

Sıra Göstergesi

Oyuncuların sıranın kimde olduğunu bilmesi gerekiyor. Ekrana bir metin ekleyelim.

ADIM 6 sketch.js - draw() güncellendi
function draw() {
    background(0, 100, 200);
    drawBoard();
    
    // Sıra göstergesi
    fill(255);
    textSize(20);
    textAlign(LEFT);
    if (currentPlayer == 1) {
        text("Sıra: Kırmızı", 10, 470);
    } else {
        text("Sıra: Sarı", 10, 470);
    }
}
📂 Adım 6 Sonrası Tam Kod
sketch.js - Tam Kod (Adım 6)
// Adım 6: Sıra Göstergesi
let gameBoard = [];
let currentPlayer = 1;

function setup() {
    createCanvas(560, 480);
    for (let r = 0; r &lt; 6; r++) {
        gameBoard[r] = [];
        for (let c = 0; c &lt; 7; c++) {
            gameBoard[r][c] = 0;
        }
    }
}

function draw() {
    background(0, 100, 200);
    drawBoard();

    // Sıra göstergesi
    fill(255);
    textSize(20);
    textAlign(LEFT);
    if (currentPlayer == 1) {
        text("Sıra: Kırmızı", 10, 470);
    } else {
        text("Sıra: Sarı", 10, 470);
    }
}

function drawBoard() {
    for (let r = 0; r &lt; 6; r++) {
        for (let c = 0; c &lt; 7; c++) {
            let x = c * 80 + 40;
            let y = r * 80 + 40;

            if (gameBoard[r][c] == 0) fill(255);
            else if (gameBoard[r][c] == 1) fill(255, 0, 0);
            else fill(255, 255, 0);

            noStroke();
            ellipse(x, y, 70, 70);
        }
    }
}

function mousePressed() {
    let sutun = floor(mouseX / 80);
    if (sutun &gt;= 0 &amp;&amp; sutun &lt; 7) {
        dropPiece(sutun);
    }
}

function dropPiece(sutun) {
    for (let satir = 5; satir &gt;= 0; satir--) {
        if (gameBoard[satir][sutun] == 0) {
            gameBoard[satir][sutun] = currentPlayer;
            currentPlayer = (currentPlayer == 1) ? 2 : 1;
            return;
        }
    }
}
Adım 7

Kazanma Kontrolü - Akıllı Algoritma

Şimdi en kritik kısma geldik: 4 taşın yan yana olup olmadığını kontrol etmek.

🧠 Kritik Düşünce: Neden Her Yeri Kontrol Etmeyelim?

Bir düşünelim: Her hamleden önce oyunda Connect4 var mıydı? Hayır! Eğer olsaydı oyun çoktan bitmiş olurdu.

Bu demek oluyor ki: Kazanma ancak YENİ konulan taş sayesinde oluşabilir!

❌ Naif Yaklaşım

Her hamlede TÜM tahtayı tara:

  • 42 hücrenin hepsini kontrol et
  • Her hücre için 4 yönü kontrol et
  • Gereksiz işlem = yavaş kod
O(n × m × 4) = ~168 kontrol

✅ Akıllı Yaklaşım

Sadece SON TAŞIN etrafını kontrol et:

  • Son taşın pozisyonunu biliyoruz
  • Sadece 4 yönde say
  • Maksimum ~24 hücre kontrolü
O(4 × 6) = ~24 kontrol
💡 Anahtar Fikir

Son konulan taşın pozisyonunu biliyoruz: (satır, sütun). Kazanma varsa bu taş mutlaka o 4'lünün parçasıdır. Bu yüzden sadece bu taştan geçen 4 çizgiyi kontrol etmemiz yeterli!

🎯 4 Yön Kontrolü

Kazanma 4 farklı yönde olabilir. Her yön için son taştan iki tarafa bakıp aynı renk taşları sayacağız:

↔️
Yatay
← → sütun değişir
↕️
Dikey
↑ ↓ satır değişir
↘️
Çapraz ↘
satır+1, sütun+1
↙️
Çapraz ↙
satır+1, sütun-1

📐 Yön Vektörleri

Her yön için hareket miktarını vektör olarak ifade edebiliriz. Vektör = (satır değişimi, sütun değişimi)

← →
Yatay
(0, -1) ve (0, +1)
↑ ↓
Dikey
(-1, 0) ve (+1, 0)
↖ ↘
Çapraz 1
(-1, -1) ve (+1, +1)
↗ ↙
Çapraz 2
(-1, +1) ve (+1, -1)

📚 Kavram: Fonksiyon Nedir?

Fonksiyon, belirli bir görevi yapan kod parçasıdır. Bir kez yazıp, istediğimiz kadar çağırabiliriz.

function selamVer(isim) { print("Merhaba " + isim); } selamVer("Ali"); // → "Merhaba Ali" selamVer("Ayşe"); // → "Merhaba Ayşe"

Neden kullanırız? Aynı kodu tekrar tekrar yazmak yerine bir fonksiyon oluşturur, ihtiyaç duyduğumuzda çağırırız. Kod daha kısa, daha okunabilir ve hata düzeltmesi daha kolay olur.

🔧 countInDirection() Fonksiyonu

Bir yönde kaç tane aynı renk taş olduğunu sayan yardımcı fonksiyon yazacağız:

AKILLI ALGORİTMA countInDirection fonksiyonu
// Belirli bir yönde aynı renk taş sayısını döndürür
// dRow: satır değişimi (-1, 0, veya +1)
// dCol: sütun değişimi (-1, 0, veya +1)
function countInDirection(row, col, dRow, dCol, player) {
    let count = 0;
    
    // Başlangıç pozisyonundan bir adım ilerle
    let r = row + dRow;  // Yeni satır = eski satır + değişim
    let c = col + dCol;  // Yeni sütun = eski sütun + değişim
    
    // Tahta sınırları içinde VE aynı oyuncu taşıysa devam et
    while (r >= 0 && r < 6 && c >= 0 && c < 7 && 
           gameBoard[r][c] == player) {
        count++;         // Sayacı artır
        r += dRow;      // Aynı yönde devam et
        c += dCol;
    }
    
    return count;  // Bulunan taş sayısını döndür
}
🎮 Akıllı Algoritma Demo
> Son taş konulduktan sonra 4 yön kontrol edilecek
Kontrol edilen hücre sayısı: 0 (Naif yöntemde 168 olurdu!)
📂 Adım 7 Sonrası Tam Kod
sketch.js - Tam Kod (Adım 7)
// Adım 7: countInDirection Fonksiyonu
let gameBoard = [];
let currentPlayer = 1;
let gameOver = false;
let winner = 0;

function setup() {
    createCanvas(560, 480);
    initBoard();
}

function initBoard() {
    for (let r = 0; r &lt; 6; r++) {
        gameBoard[r] = [];
        for (let c = 0; c &lt; 7; c++) {
            gameBoard[r][c] = 0;
        }
    }
}

// ★ Yön vektörleri ile sayma fonksiyonu ★
function countInDirection(row, col, dRow, dCol, player) {
    let count = 0;
    let r = row + dRow;
    let c = col + dCol;

    while (r &gt;= 0 &amp;&amp; r &lt; 6 &amp;&amp; c &gt;= 0 &amp;&amp; c &lt; 7 &amp;&amp;
        gameBoard[r][c] == player) {
        count++;
        r += dRow;
        c += dCol;
    }
    return count;
}

function draw() {
    background(0, 100, 200);
    drawBoard();

    fill(255);
    textSize(20);
    textAlign(LEFT);
    let t = (currentPlayer == 1) ? "Sıra: Kırmızı" : "Sıra: Sarı";
    text(t, 10, 470);
}

function drawBoard() {
    for (let r = 0; r &lt; 6; r++) {
        for (let c = 0; c &lt; 7; c++) {
            let x = c * 80 + 40;
            let y = r * 80 + 40;

            if (gameBoard[r][c] == 0) fill(255);
            else if (gameBoard[r][c] == 1) fill(255, 0, 0);
            else fill(255, 255, 0);

            noStroke();
            ellipse(x, y, 70, 70);
        }
    }
}

function mousePressed() {
    let sutun = floor(mouseX / 80);
    if (sutun &gt;= 0 &amp;&amp; sutun &lt; 7) {
        dropPiece(sutun);
    }
}

function dropPiece(sutun) {
    for (let satir = 5; satir &gt;= 0; satir--) {
        if (gameBoard[satir][sutun] == 0) {
            gameBoard[satir][sutun] = currentPlayer;
            currentPlayer = (currentPlayer == 1) ? 2 : 1;
            return;
        }
    }
}
Adım 8

Akıllı checkWin() Fonksiyonu

Artık countInDirection() fonksiyonumuz var. Şimdi 4 yön için bu fonksiyonu kullanarak kazanma kontrolü yapacağız.

📝 Mantık

Her yön için:

  1. Bir yöne doğru say (örn: sağa)
  2. Ters yöne doğru say (örn: sola)
  3. Topla + 1 (merkez taş) = Toplam
  4. Toplam ≥ 4 ise kazandı!

📚 Kavram: return Nedir?

return komutu iki şey yapar:

  1. Fonksiyonu hemen sonlandırır
  2. Belirtilen değeri geri döndürür
function topla(a, b) { return a + b; // Sonucu döndür } let sonuc = topla(3, 5); // sonuc = 8

Kazanma kontrolünde return true "evet kazandı", return false "hayır kazanmadı" anlamına gelir.

ADIM 8 - TAM KOD sketch.js - Akıllı Kazanma Kontrolü
// ============================================
// AKILLI KAZANMA ALGORİTMASI
// Son taşın pozisyonundan 4 yöne bakarak kontrol
// ============================================

// Yön vektörleri: [satır değişimi, sütun değişimi]
const DIRECTIONS = [
    [[0, 1], [0, -1]],   // Yatay: sağ ve sol
    [[1, 0], [-1, 0]],   // Dikey: aşağı ve yukarı
    [[1, 1], [-1, -1]], // Çapraz ↘: sağ-aşağı ve sol-yukarı
    [[1, -1], [-1, 1]]  // Çapraz ↙: sol-aşağı ve sağ-yukarı
];

// Belirli bir yönde aynı renk taş sayısını döndürür
function countInDirection(row, col, dRow, dCol, player) {
    let count = 0;
    let r = row + dRow;
    let c = col + dCol;
    
    // Sınırlar içinde ve aynı oyuncu taşıysa devam
    while (r >= 0 && r < 6 && c >= 0 && c < 7 && 
           gameBoard[r][c] == player) {
        count++;
        r += dRow;
        c += dCol;
    }
    
    return count;
}

// Ana kazanma kontrol fonksiyonu
function checkWin(row, col, player) {
    // Her yön için kontrol et
    for (let dir of DIRECTIONS) {
        // Bir yöne say
        let count1 = countInDirection(row, col, dir[0][0], dir[0][1], player);
        // Ters yöne say
        let count2 = countInDirection(row, col, dir[1][0], dir[1][1], player);
        
        // Toplam = iki yön + merkez taş (1)
        let total = count1 + count2 + 1;
        
        if (total >= 4) {
            return true;  // Kazandı!
        }
    }
    
    return false;  // Kazanma yok
}
✅ Bu Yaklaşımın Avantajları
  • Verimli: Sadece ilgili hücreleri kontrol eder
  • Okunabilir: Yön vektörleri ile temiz kod
  • Genişletilebilir: Connect5, Connect6 için sadece 4'ü değiştir
📂 Adım 8 Sonrası Tam Kod
sketch.js - Tam Kod (Adım 8)
// Adım 8: Akıllı checkWin Fonksiyonu
let gameBoard = [];
let currentPlayer = 1;
let gameOver = false;
let winner = 0;

const DIRECTIONS = [
    [[0, 1], [0, -1]],   // Yatay
    [[1, 0], [-1, 0]],   // Dikey
    [[1, 1], [-1, -1]],  // Çapraz ↘
    [[1, -1], [-1, 1]]   // Çapraz ↙
];

function setup() {
    createCanvas(560, 480);
    initBoard();
}

function initBoard() {
    for (let r = 0; r &lt; 6; r++) {
        gameBoard[r] = [];
        for (let c = 0; c &lt; 7; c++) {
            gameBoard[r][c] = 0;
        }
    }
}

function countInDirection(row, col, dRow, dCol, player) {
    let count = 0;
    let r = row + dRow;
    let c = col + dCol;
    while (r &gt;= 0 &amp;&amp; r &lt; 6 &amp;&amp; c &gt;= 0 &amp;&amp; c &lt; 7 &amp;&amp;
        gameBoard[r][c] == player) {
        count++;
        r += dRow;
        c += dCol;
    }
    return count;
}

// ★ Akıllı checkWin fonksiyonu ★
function checkWin(row, col, player) {
    for (let [dir1, dir2] of DIRECTIONS) {
        let count = 1;
        count += countInDirection(row, col, dir1[0], dir1[1], player);
        count += countInDirection(row, col, dir2[0], dir2[1], player);
        if (count &gt;= 4) return true;
    }
    return false;
}

function draw() {
    background(0, 100, 200);
    drawBoard();
    fill(255);
    textSize(20);
    textAlign(LEFT);
    let t = (currentPlayer == 1) ? "Sıra: Kırmızı" : "Sıra: Sarı";
    text(t, 10, 470);
}

function drawBoard() {
    for (let r = 0; r &lt; 6; r++) {
        for (let c = 0; c &lt; 7; c++) {
            let x = c * 80 + 40;
            let y = r * 80 + 40;
            if (gameBoard[r][c] == 0) fill(255);
            else if (gameBoard[r][c] == 1) fill(255, 0, 0);
            else fill(255, 255, 0);
            noStroke();
            ellipse(x, y, 70, 70);
        }
    }
}

function mousePressed() {
    let sutun = floor(mouseX / 80);
    if (sutun &gt;= 0 &amp;&amp; sutun &lt; 7) {
        dropPiece(sutun);
    }
}

function dropPiece(sutun) {
    for (let satir = 5; satir &gt;= 0; satir--) {
        if (gameBoard[satir][sutun] == 0) {
            gameBoard[satir][sutun] = currentPlayer;
            currentPlayer = (currentPlayer == 1) ? 2 : 1;
            return;
        }
    }
}
Adım 9

dropPiece ile Entegrasyon

Şimdi dropPiece() fonksiyonunda taş konulduktan sonra kazanma kontrolü yapalım.

🔄 Akış

  1. Taşı yerleştir
  2. checkWin() çağır
  3. Kazandıysa → Oyunu bitir
  4. Kazanmadıysa → Sırayı değiştir

📚 Kavram: 2D Dizi (2D Array)

Normal dizi tek boyutlu bir listedir. 2D dizi ise "satır ve sütun" şeklinde organize edilmiş bir tablodur.

// 1D Dizi (tek satır): let liste = [5, 3, 8, 1]; liste[2] // → 8 // 2D Dizi (tablo): let tablo = [ [1, 2, 3], // satır 0 [4, 5, 6], // satır 1 [7, 8, 9] // satır 2 ]; tablo[1][2] // → 6 (satır 1, sütun 2)

gameBoard[satir][sutun] şeklinde erişiyoruz: önce satır, sonra sütun!

ADIM 9 - TAM KOD sketch.js - dropPiece Entegrasyonu
// Oyun durumu değişkenleri
let gameBoard = [];
let currentPlayer = 1;
let gameOver = false;
let winner = 0;

function setup() {
    createCanvas(560, 480);
    initBoard();
}

function initBoard() {
    gameBoard = [];
    for (let r = 0; r < 6; r++) {
        gameBoard[r] = [];
        for (let c = 0; c < 7; c++) {
            gameBoard[r][c] = 0;  // 0 = boş
        }
    }
}

function mousePressed() {
    if (gameOver) {
        resetGame();  // Oyun bittiyse yeniden başlat
        return;
    }
    
    let sutun = floor(mouseX / 80);
    if (sutun >= 0 && sutun < 7) {
        dropPiece(sutun);
    }
}

function dropPiece(sutun) {
    // En alttan başla, yukarı doğru boş hücre ara
    for (let satir = 5; satir >= 0; satir--) {
        if (gameBoard[satir][sutun] == 0) {
            // Taşı yerleştir
            gameBoard[satir][sutun] = currentPlayer;
            
            // ★ KAZANMA KONTROLÜ ★
            if (checkWin(satir, sutun, currentPlayer)) {
                gameOver = true;
                winner = currentPlayer;
            } else {
                // Sırayı değiştir (1 → 2, 2 → 1)
                currentPlayer = (currentPlayer == 1) ? 2 : 1;
            }
            
            return;  // Taş kondu, fonksiyondan çık
        }
    }
    // Buraya gelirse sütun dolu
}

function resetGame() {
    initBoard();
    currentPlayer = 1;
    gameOver = false;
    winner = 0;
}
📂 Adım 9 Sonrası Tam Kod
sketch.js - Tam Kod (Adım 9)
// Adım 9: dropPiece ile Kazanma Kontrolü Entegrasyonu
let gameBoard = [];
let currentPlayer = 1;
let gameOver = false;
let winner = 0;

const DIRECTIONS = [
    [[0, 1], [0, -1]],
    [[1, 0], [-1, 0]],
    [[1, 1], [-1, -1]],
    [[1, -1], [-1, 1]]
];

function setup() {
    createCanvas(560, 480);
    initBoard();
}

function initBoard() {
    for (let r = 0; r &lt; 6; r++) {
        gameBoard[r] = [];
        for (let c = 0; c &lt; 7; c++) {
            gameBoard[r][c] = 0;
        }
    }
}

function countInDirection(row, col, dRow, dCol, player) {
    let count = 0;
    let r = row + dRow;
    let c = col + dCol;
    while (r &gt;= 0 &amp;&amp; r &lt; 6 &amp;&amp; c &gt;= 0 &amp;&amp; c &lt; 7 &amp;&amp; gameBoard[r][c] == player) {
        count++;
        r += dRow;
        c += dCol;
    }
    return count;
}

function checkWin(row, col, player) {
    for (let [dir1, dir2] of DIRECTIONS) {
        let count = 1 + countInDirection(row, col, dir1[0], dir1[1], player)
            + countInDirection(row, col, dir2[0], dir2[1], player);
        if (count &gt;= 4) return true;
    }
    return false;
}

function draw() {
    background(0, 100, 200);
    drawBoard();
    fill(255);
    textSize(20);
    textAlign(LEFT);
    text((currentPlayer == 1) ? "Sıra: Kırmızı" : "Sıra: Sarı", 10, 470);
}

function drawBoard() {
    for (let r = 0; r &lt; 6; r++) {
        for (let c = 0; c &lt; 7; c++) {
            let x = c * 80 + 40;
            let y = r * 80 + 40;
            if (gameBoard[r][c] == 0) fill(255);
            else if (gameBoard[r][c] == 1) fill(255, 0, 0);
            else fill(255, 255, 0);
            noStroke();
            ellipse(x, y, 70, 70);
        }
    }
}

function mousePressed() {
    if (gameOver) {
        resetGame();
        return;
    }
    let sutun = floor(mouseX / 80);
    if (sutun &gt;= 0 &amp;&amp; sutun &lt; 7) {
        dropPiece(sutun);
    }
}

// ★ Kazanma kontrolü ile dropPiece ★
function dropPiece(sutun) {
    for (let satir = 5; satir &gt;= 0; satir--) {
        if (gameBoard[satir][sutun] == 0) {
            gameBoard[satir][sutun] = currentPlayer;

            if (checkWin(satir, sutun, currentPlayer)) {
                gameOver = true;
                winner = currentPlayer;
            } else {
                currentPlayer = (currentPlayer == 1) ? 2 : 1;
            }
            return;
        }
    }
}

function resetGame() {
    initBoard();
    currentPlayer = 1;
    gameOver = false;
    winner = 0;
}
Adım 10

Görsel Çizim - draw() Fonksiyonu

Şimdi tüm görsel öğeleri çizen draw() fonksiyonunu yazalım.

ADIM 10 - TAM KOD sketch.js - draw() ve drawBoard()
function draw() {
    background(0, 100, 200);  // Mavi arka plan
    drawBoard();               // Tahtayı çiz
    
    if (gameOver) {
        drawGameOver();         // Oyun sonu mesajı
    } else {
        drawTurnIndicator();    // Sıra göstergesi
    }
}

function drawBoard() {
    for (let satir = 0; satir < 6; satir++) {
        for (let sutun = 0; sutun < 7; sutun++) {
            // Piksel koordinatları hesapla
            let x = sutun * 80 + 40;  // Sütun × hücre boyutu + merkez
            let y = satir * 80 + 40;  // Satır × hücre boyutu + merkez
            
            // Hücre durumuna göre renk seç
            if (gameBoard[satir][sutun] == 0) {
                fill(255);              // Beyaz = boş
            } else if (gameBoard[satir][sutun] == 1) {
                fill(255, 0, 0);        // Kırmızı = Oyuncu 1
            } else {
                fill(255, 255, 0);      // Sarı = Oyuncu 2
            }
            
            noStroke();
            ellipse(x, y, 70, 70);  // Daire çiz
        }
    }
}

function drawTurnIndicator() {
    fill(255);
    textSize(20);
    textAlign(LEFT);
    let text = (currentPlayer == 1) ? "Sıra: Kırmızı" : "Sıra: Sarı";
    text(text, 10, 470);
}

function drawGameOver() {
    // Yarı saydam siyah overlay
    fill(0, 0, 0, 150);
    rect(0, 0, width, height);
    
    // Kazanan mesajı
    fill(255);
    textSize(40);
    textAlign(CENTER, CENTER);
    
    let msg = (winner == 1) ? "KIRMIZI KAZANDI!" : "SARI KAZANDI!";
    text(msg, width/2, height/2);
    
    textSize(20);
    text("Yeniden başlamak için tıklayın", width/2, height/2 + 50);
}
📂 Adım 10 Sonrası Tam Kod
sketch.js - Tam Kod (Adım 10)
// Adım 10: Oyun Sonu Ekranı ile draw Fonksiyonu
let gameBoard = [];
let currentPlayer = 1;
let gameOver = false;
let winner = 0;

const DIRECTIONS = [
    [[0, 1], [0, -1]], [[1, 0], [-1, 0]], [[1, 1], [-1, -1]], [[1, -1], [-1, 1]]
];

function setup() {
    createCanvas(560, 480);
    initBoard();
}

function initBoard() {
    for (let r = 0; r &lt; 6; r++) {
        gameBoard[r] = [];
        for (let c = 0; c &lt; 7; c++) {
            gameBoard[r][c] = 0;
        }
    }
}

function countInDirection(row, col, dRow, dCol, player) {
    let count = 0;
    let r = row + dRow;
    let c = col + dCol;
    while (r &gt;= 0 &amp;&amp; r &lt; 6 &amp;&amp; c &gt;= 0 &amp;&amp; c &lt; 7 &amp;&amp; gameBoard[r][c] == player) {
        count++;
        r += dRow;
        c += dCol;
    }
    return count;
}

function checkWin(row, col, player) {
    for (let [d1, d2] of DIRECTIONS) {
        if (1 + countInDirection(row, col, d1[0], d1[1], player)
            + countInDirection(row, col, d2[0], d2[1], player) &gt;= 4) {
            return true;
        }
    }
    return false;
}

// ★ Oyun sonu ekranlı draw fonksiyonu ★
function draw() {
    background(0, 100, 200);
    drawBoard();

    if (gameOver) {
        drawGameOver();
    } else {
        fill(255);
        textSize(20);
        textAlign(LEFT);
        text((currentPlayer == 1) ? "Sıra: Kırmızı" : "Sıra: Sarı", 10, 470);
    }
}

function drawBoard() {
    for (let r = 0; r &lt; 6; r++) {
        for (let c = 0; c &lt; 7; c++) {
            let x = c * 80 + 40;
            let y = r * 80 + 40;
            if (gameBoard[r][c] == 0) fill(255);
            else if (gameBoard[r][c] == 1) fill(255, 0, 0);
            else fill(255, 255, 0);
            noStroke();
            ellipse(x, y, 70, 70);
        }
    }
}

function drawGameOver() {
    fill(0, 0, 0, 150);
    rect(0, 0, width, height);

    fill(255);
    textSize(40);
    textAlign(CENTER, CENTER);
    let msg = (winner == 1) ? "KIRMIZI KAZANDI!" : "SARI KAZANDI!";
    text(msg, width / 2, height / 2);

    textSize(20);
    text("Yeniden başlamak için tıklayın", width / 2, height / 2 + 50);
}

function mousePressed() {
    if (gameOver) {
        resetGame();
        return;
    }
    let sutun = floor(mouseX / 80);
    if (sutun &gt;= 0 &amp;&amp; sutun &lt; 7) {
        dropPiece(sutun);
    }
}

function dropPiece(sutun) {
    for (let satir = 5; satir &gt;= 0; satir--) {
        if (gameBoard[satir][sutun] == 0) {
            gameBoard[satir][sutun] = currentPlayer;
            if (checkWin(satir, sutun, currentPlayer)) {
                gameOver = true;
                winner = currentPlayer;
            } else {
                currentPlayer = (currentPlayer == 1) ? 2 : 1;
            }
            return;
        }
    }
}

function resetGame() {
    initBoard();
    currentPlayer = 1;
    gameOver = false;
    winner = 0;
}
Adım 11

Tamamlanmış Çalışan Kod

İşte şu ana kadar yazdığımız tüm kodun birleşimi. Bu kodu p5.js editörüne yapıştırarak çalışan bir oyun elde edebilirsiniz!

🎉

Tebrikler! Temel Oyun Tamamlandı!

Bu noktada çalışan bir Connect4 oyununuz var:

  • ✅ 7x6 oyun tahtası
  • ✅ İki oyunculu sıra sistemi
  • ✅ Akıllı kazanma algoritması (4 yön kontrolü)
  • ✅ Oyun sonu mesajı
  • ✅ Yeniden başlatma özelliği
ADIM 11 - NİHAİ KOD sketch.js - Çalışan Connect4 Oyunu
// ============================================
// CONNECT4 OYUNU - NİHAİ KOD (Adım 11)
// ============================================

// Oyun durumu değişkenleri
let gameBoard = [];
let currentPlayer = 1;   // 1 = Kırmızı, 2 = Sarı
let gameOver = false;
let winner = 0;

// Yön vektörleri (akıllı algoritma için)
const DIRECTIONS = [
    [[0, 1], [0, -1]],   // Yatay
    [[1, 0], [-1, 0]],   // Dikey
    [[1, 1], [-1, -1]], // Çapraz ↘
    [[1, -1], [-1, 1]]  // Çapraz ↙
];

function setup() {
    createCanvas(560, 480);
    initBoard();
}

function initBoard() {
    gameBoard = [];
    for (let r = 0; r < 6; r++) {
        gameBoard[r] = [];
        for (let c = 0; c < 7; c++) {
            gameBoard[r][c] = 0;
        }
    }
}

function draw() {
    background(0, 100, 200);
    drawBoard();
    
    if (gameOver) {
        drawGameOver();
    } else {
        drawTurnIndicator();
    }
}

function drawBoard() {
    for (let r = 0; r < 6; r++) {
        for (let c = 0; c < 7; c++) {
            let x = c * 80 + 40;
            let y = r * 80 + 40;
            
            if (gameBoard[r][c] == 0) fill(255);
            else if (gameBoard[r][c] == 1) fill(255, 0, 0);
            else fill(255, 255, 0);
            
            noStroke();
            ellipse(x, y, 70, 70);
        }
    }
}

function mousePressed() {
    if (gameOver) {
        resetGame();
        return;
    }
    
    let sutun = floor(mouseX / 80);
    if (sutun >= 0 && sutun < 7) {
        dropPiece(sutun);
    }
}

function dropPiece(sutun) {
    for (let satir = 5; satir >= 0; satir--) {
        if (gameBoard[satir][sutun] == 0) {
            gameBoard[satir][sutun] = currentPlayer;
            
            if (checkWin(satir, sutun, currentPlayer)) {
                gameOver = true;
                winner = currentPlayer;
            } else {
                currentPlayer = (currentPlayer == 1) ? 2 : 1;
            }
            return;
        }
    }
}

// ★ AKILLI KAZANMA ALGORİTMASI ★
function countInDirection(row, col, dRow, dCol, player) {
    let count = 0;
    let r = row + dRow;
    let c = col + dCol;
    
    while (r >= 0 && r < 6 && c >= 0 && c < 7 && 
           gameBoard[r][c] == player) {
        count++;
        r += dRow;
        c += dCol;
    }
    return count;
}

function checkWin(row, col, player) {
    for (let dir of DIRECTIONS) {
        let count1 = countInDirection(row, col, dir[0][0], dir[0][1], player);
        let count2 = countInDirection(row, col, dir[1][0], dir[1][1], player);
        
        if (count1 + count2 + 1 >= 4) {
            return true;
        }
    }
    return false;
}

function drawTurnIndicator() {
    fill(255);
    textSize(20);
    textAlign(LEFT);
    let t = (currentPlayer == 1) ? "Sıra: Kırmızı" : "Sıra: Sarı";
    text(t, 10, 470);
}

function drawGameOver() {
    fill(0, 0, 0, 150);
    rect(0, 0, width, height);
    
    fill(255);
    textSize(40);
    textAlign(CENTER, CENTER);
    let msg = (winner == 1) ? "KIRMIZI KAZANDI!" : "SARI KAZANDI!";
    text(msg, width/2, height/2);
    
    textSize(20);
    text("Yeniden başlamak için tıklayın", width/2, height/2 + 50);
}

function resetGame() {
    initBoard();
    currentPlayer = 1;
    gameOver = false;
    winner = 0;
}
💡 Ternary Operator

currentPlayer = (currentPlayer == 1) ? 2 : 1;

Bu kısa yazım şu anlama gelir: "Eğer currentPlayer 1 ise 2 yap, değilse 1 yap."

📂 Adım 11 Sonrası Tam Kod
sketch.js - Tam Kod (Adım 11)
// Adım 11: Tamamlanmış Çalışan Oyun
let gameBoard = [];
let currentPlayer = 1;
let gameOver = false;
let winner = 0;

const DIRECTIONS = [
    [[0, 1], [0, -1]],   // Yatay
    [[1, 0], [-1, 0]],   // Dikey
    [[1, 1], [-1, -1]],  // Çapraz ↘
    [[1, -1], [-1, 1]]   // Çapraz ↙
];

function setup() {
    createCanvas(560, 480);
    initBoard();
}

function initBoard() {
    for (let r = 0; r &lt; 6; r++) {
        gameBoard[r] = [];
        for (let c = 0; c &lt; 7; c++) {
            gameBoard[r][c] = 0;
        }
    }
}

function countInDirection(row, col, dRow, dCol, player) {
    let count = 0;
    let r = row + dRow;
    let c = col + dCol;
    while (r &gt;= 0 &amp;&amp; r &lt; 6 &amp;&amp; c &gt;= 0 &amp;&amp; c &lt; 7 &amp;&amp;
        gameBoard[r][c] == player) {
        count++;
        r += dRow;
        c += dCol;
    }
    return count;
}

function checkWin(row, col, player) {
    for (let [dir1, dir2] of DIRECTIONS) {
        let count = 1;
        count += countInDirection(row, col, dir1[0], dir1[1], player);
        count += countInDirection(row, col, dir2[0], dir2[1], player);
        if (count &gt;= 4) return true;
    }
    return false;
}

function draw() {
    background(0, 100, 200);
    drawBoard();

    if (gameOver) {
        drawGameOver();
    } else {
        fill(255);
        textSize(20);
        textAlign(LEFT);
        let t = (currentPlayer == 1) ? "Sıra: Kırmızı" : "Sıra: Sarı";
        text(t, 10, 470);
    }
}

function drawBoard() {
    for (let r = 0; r &lt; 6; r++) {
        for (let c = 0; c &lt; 7; c++) {
            let x = c * 80 + 40;
            let y = r * 80 + 40;
            if (gameBoard[r][c] == 0) fill(255);
            else if (gameBoard[r][c] == 1) fill(255, 0, 0);
            else fill(255, 255, 0);
            noStroke();
            ellipse(x, y, 70, 70);
        }
    }
}

function drawGameOver() {
    fill(0, 0, 0, 150);
    rect(0, 0, width, height);

    fill(255);
    textSize(40);
    textAlign(CENTER, CENTER);
    let msg = (winner == 1) ? "KIRMIZI KAZANDI!" : "SARI KAZANDI!";
    text(msg, width / 2, height / 2);

    textSize(20);
    text("Yeniden başlamak için tıklayın", width / 2, height / 2 + 50);
}

function mousePressed() {
    if (gameOver) {
        resetGame();
        return;
    }
    let sutun = floor(mouseX / 80);
    if (sutun &gt;= 0 &amp;&amp; sutun &lt; 7) {
        dropPiece(sutun);
    }
}

function dropPiece(sutun) {
    for (let satir = 5; satir &gt;= 0; satir--) {
        if (gameBoard[satir][sutun] == 0) {
            gameBoard[satir][sutun] = currentPlayer;
            if (checkWin(satir, sutun, currentPlayer)) {
                gameOver = true;
                winner = currentPlayer;
            } else {
                currentPlayer = (currentPlayer == 1) ? 2 : 1;
            }
            return;
        }
    }
}

function resetGame() {
    initBoard();
    currentPlayer = 1;
    gameOver = false;
    winner = 0;
}
Adım 12

Oyun Sonu Ekranı

Kazanan belli olduğunda bunu ekranda gösterelim.

ADIM 12 sketch.js
function draw() {
    background(0, 100, 200);
    drawBoard();
    
    if (gameOver) {
        // Yarı saydam siyah overlay
        fill(0, 0, 0, 150);
        rect(0, 0, width, height);
        
        // Kazanan mesajı
        fill(255);
        textSize(40);
        textAlign(CENTER, CENTER);
        if (winner == 1) {
            text("KIRMIZI KAZANDI!", width/2, height/2);
        } else {
            text("SARI KAZANDI!", width/2, height/2);
        }
        
        textSize(20);
        text("Yeniden başlamak için tıklayın", width/2, height/2 + 50);
    } else {
        // Sıra göstergesi
        fill(255);
        textSize(20);
        textAlign(LEFT);
        let playerText = (currentPlayer == 1) ? "Kırmızı" : "Sarı";
        text("Sıra: " + playerText, 10, 470);
    }
}
📂 Adım 12 Sonrası Tam Kod
sketch.js - Tam Kod (Adım 12)
// Adım 12: Oyun Sonu Ekranı (Step 11 ile aynı - complete working game)
let gameBoard = [];
let currentPlayer = 1;
let gameOver = false;
let winner = 0;

const DIRECTIONS = [
    [[0, 1], [0, -1]],   // Yatay
    [[1, 0], [-1, 0]],   // Dikey
    [[1, 1], [-1, -1]],  // Çapraz ↘
    [[1, -1], [-1, 1]]   // Çapraz ↙
];

function setup() {
    createCanvas(560, 480);
    initBoard();
}

function initBoard() {
    for (let r = 0; r &lt; 6; r++) {
        gameBoard[r] = [];
        for (let c = 0; c &lt; 7; c++) {
            gameBoard[r][c] = 0;
        }
    }
}

function countInDirection(row, col, dRow, dCol, player) {
    let count = 0;
    let r = row + dRow;
    let c = col + dCol;
    while (r &gt;= 0 &amp;&amp; r &lt; 6 &amp;&amp; c &gt;= 0 &amp;&amp; c &lt; 7 &amp;&amp; gameBoard[r][c] == player) {
        count++;
        r += dRow;
        c += dCol;
    }
    return count;
}

function checkWin(row, col, player) {
    for (let [dir1, dir2] of DIRECTIONS) {
        let count = 1 + countInDirection(row, col, dir1[0], dir1[1], player)
            + countInDirection(row, col, dir2[0], dir2[1], player);
        if (count &gt;= 4) return true;
    }
    return false;
}

function draw() {
    background(0, 100, 200);
    drawBoard();

    if (gameOver) {
        // ★ Oyun sonu ekranı ★
        fill(0, 0, 0, 150);
        rect(0, 0, width, height);

        fill(255);
        textSize(40);
        textAlign(CENTER, CENTER);
        let msg = (winner == 1) ? "KIRMIZI KAZANDI!" : "SARI KAZANDI!";
        text(msg, width / 2, height / 2);

        textSize(20);
        text("Yeniden başlamak için tıklayın", width / 2, height / 2 + 50);
    } else {
        fill(255);
        textSize(20);
        textAlign(LEFT);
        text((currentPlayer == 1) ? "Sıra: Kırmızı" : "Sıra: Sarı", 10, 470);
    }
}

function drawBoard() {
    for (let r = 0; r &lt; 6; r++) {
        for (let c = 0; c &lt; 7; c++) {
            let x = c * 80 + 40;
            let y = r * 80 + 40;
            if (gameBoard[r][c] == 0) fill(255);
            else if (gameBoard[r][c] == 1) fill(255, 0, 0);
            else fill(255, 255, 0);
            noStroke();
            ellipse(x, y, 70, 70);
        }
    }
}

function mousePressed() {
    if (gameOver) {
        resetGame();
        return;
    }
    let sutun = floor(mouseX / 80);
    if (sutun &gt;= 0 &amp;&amp; sutun &lt; 7) {
        dropPiece(sutun);
    }
}

function dropPiece(sutun) {
    for (let satir = 5; satir &gt;= 0; satir--) {
        if (gameBoard[satir][sutun] == 0) {
            gameBoard[satir][sutun] = currentPlayer;
            if (checkWin(satir, sutun, currentPlayer)) {
                gameOver = true;
                winner = currentPlayer;
            } else {
                currentPlayer = (currentPlayer == 1) ? 2 : 1;
            }
            return;
        }
    }
}

function resetGame() {
    initBoard();
    currentPlayer = 1;
    gameOver = false;
    winner = 0;
}
Adım 13

Oyunu Sıfırlama

Oyun bittiğinde tıklayarak yeni oyun başlatabilmeliyiz.

ADIM 13 sketch.js
function mousePressed() {
    if (gameOver) {
        // Oyunu sıfırla
        resetGame();
        return;
    }
    
    let sutun = floor(mouseX / 80);
    if (sutun >= 0 && sutun < 7) {
        dropPiece(sutun);
    }
}

function resetGame() {
    initBoard();
    currentPlayer = 1;
    gameOver = false;
    winner = 0;
}
📂 Adım 13 Sonrası Tam Kod
sketch.js - Tam Kod (Adım 13)
// Adım 13: Oyunu Sıfırlama
let gameBoard = [];
let currentPlayer = 1;
let gameOver = false;
let winner = 0;

const DIRECTIONS = [
    [[0, 1], [0, -1]], [[1, 0], [-1, 0]],
    [[1, 1], [-1, -1]], [[1, -1], [-1, 1]]
];

function setup() {
    createCanvas(560, 480);
    initBoard();
}

function initBoard() {
    for (let r = 0; r &lt; 6; r++) {
        gameBoard[r] = [];
        for (let c = 0; c &lt; 7; c++) {
            gameBoard[r][c] = 0;
        }
    }
}

function countInDirection(row, col, dRow, dCol, player) {
    let count = 0;
    let r = row + dRow;
    let c = col + dCol;
    while (r &gt;= 0 &amp;&amp; r &lt; 6 &amp;&amp; c &gt;= 0 &amp;&amp; c &lt; 7 &amp;&amp; gameBoard[r][c] == player) {
        count++;
        r += dRow;
        c += dCol;
    }
    return count;
}

function checkWin(row, col, player) {
    for (let [dir1, dir2] of DIRECTIONS) {
        let count = 1 + countInDirection(row, col, dir1[0], dir1[1], player)
            + countInDirection(row, col, dir2[0], dir2[1], player);
        if (count &gt;= 4) return true;
    }
    return false;
}

function draw() {
    background(0, 100, 200);
    drawBoard();

    if (gameOver) {
        drawGameOver();
    } else {
        fill(255);
        textSize(20);
        textAlign(LEFT);
        text((currentPlayer == 1) ? "Sıra: Kırmızı" : "Sıra: Sarı", 10, 470);
    }
}

function drawBoard() {
    for (let r = 0; r &lt; 6; r++) {
        for (let c = 0; c &lt; 7; c++) {
            let x = c * 80 + 40;
            let y = r * 80 + 40;
            if (gameBoard[r][c] == 0) fill(255);
            else if (gameBoard[r][c] == 1) fill(255, 0, 0);
            else fill(255, 255, 0);
            noStroke();
            ellipse(x, y, 70, 70);
        }
    }
}

function drawGameOver() {
    fill(0, 0, 0, 150);
    rect(0, 0, width, height);
    fill(255);
    textSize(40);
    textAlign(CENTER, CENTER);
    text((winner == 1) ? "KIRMIZI KAZANDI!" : "SARI KAZANDI!", width / 2, height / 2);
    textSize(20);
    text("Yeniden başlamak için tıklayın", width / 2, height / 2 + 50);
}

function mousePressed() {
    // ★ Oyun bittiyse sıfırla ★
    if (gameOver) {
        resetGame();
        return;
    }
    let sutun = floor(mouseX / 80);
    if (sutun &gt;= 0 &amp;&amp; sutun &lt; 7) {
        dropPiece(sutun);
    }
}

function dropPiece(sutun) {
    for (let satir = 5; satir &gt;= 0; satir--) {
        if (gameBoard[satir][sutun] == 0) {
            gameBoard[satir][sutun] = currentPlayer;
            if (checkWin(satir, sutun, currentPlayer)) {
                gameOver = true;
                winner = currentPlayer;
            } else {
                currentPlayer = (currentPlayer == 1) ? 2 : 1;
            }
            return;
        }
    }
}

// ★ Oyunu sıfırlama fonksiyonu ★
function resetGame() {
    initBoard();
    currentPlayer = 1;
    gameOver = false;
    winner = 0;
}
Adım 14

Beraberlik Kontrolü

Tahta dolduğunda ve kimse kazanmadığında beraberlik olur.

ADIM 14 sketch.js
function checkDraw() {
    // En üst satırda boş hücre var mı kontrol et
    for (let c = 0; c < 7; c++) {
        if (gameBoard[0][c] == 0) {
            return false; // Hala boş yer var
        }
    }
    return true; // Tahta dolu
}

// dropPiece içinde kazanma kontrolünden sonra:
if (!gameOver && checkDraw()) {
    gameOver = true;
    winner = 0; // 0 = beraberlik
}
📂 Adım 14 Sonrası Tam Kod
sketch.js - Tam Kod (Adım 14)
// Adım 14: Beraberlik Kontrolü
let gameBoard = [];
let currentPlayer = 1;
let gameOver = false;
let winner = 0;

const DIRECTIONS = [
    [[0, 1], [0, -1]], [[1, 0], [-1, 0]],
    [[1, 1], [-1, -1]], [[1, -1], [-1, 1]]
];

function setup() {
    createCanvas(560, 480);
    initBoard();
}

function initBoard() {
    for (let r = 0; r &lt; 6; r++) {
        gameBoard[r] = [];
        for (let c = 0; c &lt; 7; c++) {
            gameBoard[r][c] = 0;
        }
    }
}

function countInDirection(row, col, dRow, dCol, player) {
    let count = 0;
    let r = row + dRow;
    let c = col + dCol;
    while (r &gt;= 0 &amp;&amp; r &lt; 6 &amp;&amp; c &gt;= 0 &amp;&amp; c &lt; 7 &amp;&amp; gameBoard[r][c] == player) {
        count++;
        r += dRow;
        c += dCol;
    }
    return count;
}

function checkWin(row, col, player) {
    for (let [dir1, dir2] of DIRECTIONS) {
        let count = 1 + countInDirection(row, col, dir1[0], dir1[1], player)
            + countInDirection(row, col, dir2[0], dir2[1], player);
        if (count &gt;= 4) return true;
    }
    return false;
}

// ★ Beraberlik kontrolü - tahta dolu mu? ★
function checkDraw() {
    for (let c = 0; c &lt; 7; c++) {
        if (gameBoard[0][c] == 0) {
            return false;  // En üst satırda boşluk var
        }
    }
    return true;  // Tahta tamamen dolu
}

function draw() {
    background(0, 100, 200);
    drawBoard();

    if (gameOver) {
        drawGameOver();
    } else {
        fill(255);
        textSize(20);
        textAlign(LEFT);
        text((currentPlayer == 1) ? "Sıra: Kırmızı" : "Sıra: Sarı", 10, 470);
    }
}

function drawBoard() {
    for (let r = 0; r &lt; 6; r++) {
        for (let c = 0; c &lt; 7; c++) {
            let x = c * 80 + 40;
            let y = r * 80 + 40;
            if (gameBoard[r][c] == 0) fill(255);
            else if (gameBoard[r][c] == 1) fill(255, 0, 0);
            else fill(255, 255, 0);
            noStroke();
            ellipse(x, y, 70, 70);
        }
    }
}

function drawGameOver() {
    fill(0, 0, 0, 150);
    rect(0, 0, width, height);
    fill(255);
    textSize(40);
    textAlign(CENTER, CENTER);

    if (winner == 0) {
        text("BERABERE!", width / 2, height / 2);
    } else {
        text((winner == 1) ? "KIRMIZI KAZANDI!" : "SARI KAZANDI!", width / 2, height / 2);
    }

    textSize(20);
    text("Yeniden başlamak için tıklayın", width / 2, height / 2 + 50);
}

function mousePressed() {
    if (gameOver) {
        resetGame();
        return;
    }
    let sutun = floor(mouseX / 80);
    if (sutun &gt;= 0 &amp;&amp; sutun &lt; 7) {
        dropPiece(sutun);
    }
}

function dropPiece(sutun) {
    for (let satir = 5; satir &gt;= 0; satir--) {
        if (gameBoard[satir][sutun] == 0) {
            gameBoard[satir][sutun] = currentPlayer;

            if (checkWin(satir, sutun, currentPlayer)) {
                gameOver = true;
                winner = currentPlayer;
            } else if (checkDraw()) {
                gameOver = true;
                winner = 0;  // Beraberlik
            } else {
                currentPlayer = (currentPlayer == 1) ? 2 : 1;
            }
            return;
        }
    }
}

function resetGame() {
    initBoard();
    currentPlayer = 1;
    gameOver = false;
    winner = 0;
}
Adım 15

Parametrik Yapı - Sabitler

Kodumuzda çok fazla "sihirli sayı" (magic number) var: 6, 7, 80, 40... Bunları değişkenlere çevirelim.

🔧 Neden Parametrik?

  • Kod daha okunabilir olur
  • Değişiklik yapmak kolaylaşır (örn: 8x8 tahta)
  • Hatalar azalır

💡 const vs let - Ne Zaman Hangisi Kullanılır?

const (Sabit)
  • Değeri asla değişmez
  • Satır sayısı, sütun sayısı, boyutlar
  • Matematikte π gibi düşünün
  • Yanlışlıkla değiştirmeyi engeller
const ROWS = 6;      // ✓ Asla değişmez
const COLS = 7;      // ✓ Asla değişmez
ROWS = 8;            // ✗ HATA!
let (Değişken)
  • Değeri değişebilir
  • Oyuncu sırası, skor, durum
  • Oyun sırasında güncellenen değerler
  • Her hamleden etkilenen veriler
let currentPlayer = 1;  // Oyuncu sırası değişir
let gameOver = false;   // Oyun durumu değişir
currentPlayer = 2;      // ✓ Sorun yok

Kural: Değer hiç değişmeyecekse const, değişecekse let kullan.

ADIM 15 sketch.js
// Sabitler (en üstte tanımla)
const ROWS = 6;
const COLS = 7;
const CELL_SIZE = 80;
const PIECE_SIZE = 70;

// Değişkenler
let gameBoard = [];
let currentPlayer = 1;
let gameOver = false;
let winner = 0;

function setup() {
    createCanvas(COLS * CELL_SIZE, ROWS * CELL_SIZE);
    initBoard();
}

function initBoard() {
    for (let r = 0; r < ROWS; r++) {
        gameBoard[r] = [];
        for (let c = 0; c < COLS; c++) {
            gameBoard[r][c] = 0;
        }
    }
}

function drawBoard() {
    for (let r = 0; r < ROWS; r++) {
        for (let c = 0; c < COLS; c++) {
            let x = c * CELL_SIZE + CELL_SIZE / 2;
            let y = r * CELL_SIZE + CELL_SIZE / 2;
            
            // Renk seçimi...
            ellipse(x, y, PIECE_SIZE, PIECE_SIZE);
        }
    }
}
✨

Artık Kolayca Değiştirebilirsiniz!

Sadece sabitleri değiştirerek:

  • ROWS = 8, COLS = 8 → 8x8 tahta
  • CELL_SIZE = 60 → Daha küçük hücreler
  • PIECE_SIZE = 50 → Daha küçük taşlar
📂 Adım 15 Sonrası Tam Kod
sketch.js - Tam Kod (Adım 15)
// Adım 15: Parametrik Yapı - Sabitler
// ★ Sabitler (değişmeyecek değerler) ★
const ROWS = 6;
const COLS = 7;
const CELL_SIZE = 80;
const PIECE_SIZE = 70;

// Değişkenler (oyun sırasında değişebilir)
let gameBoard = [];
let currentPlayer = 1;
let gameOver = false;
let winner = 0;

const DIRECTIONS = [
    [[0, 1], [0, -1]], [[1, 0], [-1, 0]],
    [[1, 1], [-1, -1]], [[1, -1], [-1, 1]]
];

function setup() {
    createCanvas(COLS * CELL_SIZE, ROWS * CELL_SIZE);
    initBoard();
}

function initBoard() {
    for (let r = 0; r &lt; ROWS; r++) {
        gameBoard[r] = [];
        for (let c = 0; c &lt; COLS; c++) {
            gameBoard[r][c] = 0;
        }
    }
}

function countInDirection(row, col, dRow, dCol, player) {
    let count = 0;
    let r = row + dRow;
    let c = col + dCol;
    while (r &gt;= 0 &amp;&amp; r &lt; ROWS &amp;&amp; c &gt;= 0 &amp;&amp; c &lt; COLS &amp;&amp; gameBoard[r][c] == player) {
        count++;
        r += dRow;
        c += dCol;
    }
    return count;
}

function checkWin(row, col, player) {
    for (let [dir1, dir2] of DIRECTIONS) {
        let count = 1 + countInDirection(row, col, dir1[0], dir1[1], player)
            + countInDirection(row, col, dir2[0], dir2[1], player);
        if (count &gt;= 4) return true;
    }
    return false;
}

function checkDraw() {
    for (let c = 0; c &lt; COLS; c++) {
        if (gameBoard[0][c] == 0) return false;
    }
    return true;
}

function draw() {
    background(0, 100, 200);
    drawBoard();

    if (gameOver) {
        drawGameOver();
    } else {
        fill(255);
        textSize(20);
        textAlign(LEFT);
        text((currentPlayer == 1) ? "Sıra: Kırmızı" : "Sıra: Sarı", 10, height - 10);
    }
}

function drawBoard() {
    for (let r = 0; r &lt; ROWS; r++) {
        for (let c = 0; c &lt; COLS; c++) {
            let x = c * CELL_SIZE + CELL_SIZE / 2;
            let y = r * CELL_SIZE + CELL_SIZE / 2;

            if (gameBoard[r][c] == 0) fill(255);
            else if (gameBoard[r][c] == 1) fill(255, 0, 0);
            else fill(255, 255, 0);

            noStroke();
            ellipse(x, y, PIECE_SIZE, PIECE_SIZE);
        }
    }
}

function drawGameOver() {
    fill(0, 0, 0, 150);
    rect(0, 0, width, height);
    fill(255);
    textSize(40);
    textAlign(CENTER, CENTER);

    if (winner == 0) {
        text("BERABERE!", width / 2, height / 2);
    } else {
        text((winner == 1) ? "KIRMIZI KAZANDI!" : "SARI KAZANDI!", width / 2, height / 2);
    }

    textSize(20);
    text("Yeniden başlamak için tıklayın", width / 2, height / 2 + 50);
}

function mousePressed() {
    if (gameOver) {
        resetGame();
        return;
    }
    let sutun = floor(mouseX / CELL_SIZE);
    if (sutun &gt;= 0 &amp;&amp; sutun &lt; COLS) {
        dropPiece(sutun);
    }
}

function dropPiece(sutun) {
    for (let satir = ROWS - 1; satir &gt;= 0; satir--) {
        if (gameBoard[satir][sutun] == 0) {
            gameBoard[satir][sutun] = currentPlayer;

            if (checkWin(satir, sutun, currentPlayer)) {
                gameOver = true;
                winner = currentPlayer;
            } else if (checkDraw()) {
                gameOver = true;
                winner = 0;
            } else {
                currentPlayer = (currentPlayer == 1) ? 2 : 1;
            }
            return;
        }
    }
}

function resetGame() {
    initBoard();
    currentPlayer = 1;
    gameOver = false;
    winner = 0;
}
Adım 16

Offset ile Konumlandırma

Tahtayı ekranın ortasına veya belirli bir konuma yerleştirmek isteyebiliriz. Bunun için offset (kaydırma) değerleri kullanırız.

ADIM 16 sketch.js
const OFFSET_X = 50; // Soldan boşluk
const OFFSET_Y = 50; // Üstten boşluk

function setup() {
    // Canvas boyutunu offset'lerle birlikte hesapla
    let canvasWidth = COLS * CELL_SIZE + OFFSET_X * 2;
    let canvasHeight = ROWS * CELL_SIZE + OFFSET_Y * 2;
    createCanvas(canvasWidth, canvasHeight);
    initBoard();
}

function drawBoard() {
    for (let r = 0; r < ROWS; r++) {
        for (let c = 0; c < COLS; c++) {
            let x = c * CELL_SIZE + CELL_SIZE/2 + OFFSET_X;
            let y = r * CELL_SIZE + CELL_SIZE/2 + OFFSET_Y;
            // ...
        }
    }
}

function mousePressed() {
    // Mouse koordinatından offset'i çıkar
    let adjustedX = mouseX - OFFSET_X;
    let sutun = floor(adjustedX / CELL_SIZE);
    // ...
}
📂 Adım 16 Sonrası Tam Kod
sketch.js - Tam Kod (Adım 16)
// Adım 16: Offset ile Konumlandırma
const ROWS = 6;
const COLS = 7;
const CELL_SIZE = 80;
const PIECE_SIZE = 70;
const OFFSET_Y = 40;  // ★ Üstten boşluk (sıra göstergesi için) ★

let gameBoard = [];
let currentPlayer = 1;
let gameOver = false;
let winner = 0;

const DIRECTIONS = [
    [[0, 1], [0, -1]], [[1, 0], [-1, 0]],
    [[1, 1], [-1, -1]], [[1, -1], [-1, 1]]
];

function setup() {
    createCanvas(COLS * CELL_SIZE, ROWS * CELL_SIZE + OFFSET_Y);
    initBoard();
}

function initBoard() {
    for (let r = 0; r &lt; ROWS; r++) {
        gameBoard[r] = [];
        for (let c = 0; c &lt; COLS; c++) {
            gameBoard[r][c] = 0;
        }
    }
}

function countInDirection(row, col, dRow, dCol, player) {
    let count = 0;
    let r = row + dRow;
    let c = col + dCol;
    while (r &gt;= 0 &amp;&amp; r &lt; ROWS &amp;&amp; c &gt;= 0 &amp;&amp; c &lt; COLS &amp;&amp; gameBoard[r][c] == player) {
        count++;
        r += dRow;
        c += dCol;
    }
    return count;
}

function checkWin(row, col, player) {
    for (let [dir1, dir2] of DIRECTIONS) {
        let count = 1 + countInDirection(row, col, dir1[0], dir1[1], player)
            + countInDirection(row, col, dir2[0], dir2[1], player);
        if (count &gt;= 4) return true;
    }
    return false;
}

function checkDraw() {
    for (let c = 0; c &lt; COLS; c++) {
        if (gameBoard[0][c] == 0) return false;
    }
    return true;
}

function draw() {
    background(0, 100, 200);
    drawBoard();

    if (gameOver) {
        drawGameOver();
    } else {
        fill(255);
        textSize(20);
        textAlign(CENTER);
        text((currentPlayer == 1) ? "Sıra: Kırmızı" : "Sıra: Sarı", width / 2, 25);
    }
}

function drawBoard() {
    for (let r = 0; r &lt; ROWS; r++) {
        for (let c = 0; c &lt; COLS; c++) {
            // ★ OFFSET_Y eklendi ★
            let x = c * CELL_SIZE + CELL_SIZE / 2;
            let y = r * CELL_SIZE + CELL_SIZE / 2 + OFFSET_Y;

            if (gameBoard[r][c] == 0) fill(255);
            else if (gameBoard[r][c] == 1) fill(255, 0, 0);
            else fill(255, 255, 0);

            noStroke();
            ellipse(x, y, PIECE_SIZE, PIECE_SIZE);
        }
    }
}

function drawGameOver() {
    fill(0, 0, 0, 150);
    rect(0, OFFSET_Y, width, height - OFFSET_Y);
    fill(255);
    textSize(40);
    textAlign(CENTER, CENTER);

    if (winner == 0) {
        text("BERABERE!", width / 2, height / 2);
    } else {
        text((winner == 1) ? "KIRMIZI KAZANDI!" : "SARI KAZANDI!", width / 2, height / 2);
    }

    textSize(20);
    text("Yeniden başlamak için tıklayın", width / 2, height / 2 + 50);
}

function mousePressed() {
    if (gameOver) {
        resetGame();
        return;
    }
    // ★ mouseY kontrolü ★
    if (mouseY &lt; OFFSET_Y) return;

    let sutun = floor(mouseX / CELL_SIZE);
    if (sutun &gt;= 0 &amp;&amp; sutun &lt; COLS) {
        dropPiece(sutun);
    }
}

function dropPiece(sutun) {
    for (let satir = ROWS - 1; satir &gt;= 0; satir--) {
        if (gameBoard[satir][sutun] == 0) {
            gameBoard[satir][sutun] = currentPlayer;

            if (checkWin(satir, sutun, currentPlayer)) {
                gameOver = true;
                winner = currentPlayer;
            } else if (checkDraw()) {
                gameOver = true;
                winner = 0;
            } else {
                currentPlayer = (currentPlayer == 1) ? 2 : 1;
            }
            return;
        }
    }
}

function resetGame() {
    initBoard();
    currentPlayer = 1;
    gameOver = false;
    winner = 0;
}
Adım 17

Hover Efekti - Önizleme

Oyuncunun hangi sütuna taş koyacağını görmesi için mouse'un üzerinde olduğu sütunda bir önizleme gösterelim.

ADIM 17 sketch.js
function draw() {
    background(0, 100, 200);
    
    // Hover önizlemesi (oyun devam ediyorsa)
    if (!gameOver) {
        drawHoverPreview();
    }
    
    drawBoard();
    // ... diğer çizimler
}

function drawHoverPreview() {
    let adjustedX = mouseX - OFFSET_X;
    let sutun = floor(adjustedX / CELL_SIZE);
    
    if (sutun >= 0 && sutun < COLS) {
        // Yarı saydam renk
        if (currentPlayer == 1) {
            fill(255, 0, 0, 100);
        } else {
            fill(255, 255, 0, 100);
        }
        
        // Tahtanın üstünde önizleme taşı
        let x = sutun * CELL_SIZE + CELL_SIZE/2 + OFFSET_X;
        let y = OFFSET_Y / 2;
        ellipse(x, y, PIECE_SIZE, PIECE_SIZE);
    }
}
📂 Adım 17 Sonrası Tam Kod
sketch.js - Tam Kod (Adım 17)
// Adım 17: Hover Efekti
const ROWS = 6;
const COLS = 7;
const CELL_SIZE = 80;
const PIECE_SIZE = 70;
const OFFSET_Y = 40;

let gameBoard = [];
let currentPlayer = 1;
let gameOver = false;
let winner = 0;

const DIRECTIONS = [
    [[0, 1], [0, -1]], [[1, 0], [-1, 0]],
    [[1, 1], [-1, -1]], [[1, -1], [-1, 1]]
];

function setup() {
    createCanvas(COLS * CELL_SIZE, ROWS * CELL_SIZE + OFFSET_Y);
    initBoard();
}

function initBoard() {
    for (let r = 0; r &lt; ROWS; r++) {
        gameBoard[r] = [];
        for (let c = 0; c &lt; COLS; c++) {
            gameBoard[r][c] = 0;
        }
    }
}

function countInDirection(row, col, dRow, dCol, player) {
    let count = 0;
    let r = row + dRow;
    let c = col + dCol;
    while (r &gt;= 0 &amp;&amp; r &lt; ROWS &amp;&amp; c &gt;= 0 &amp;&amp; c &lt; COLS &amp;&amp; gameBoard[r][c] == player) {
        count++;
        r += dRow;
        c += dCol;
    }
    return count;
}

function checkWin(row, col, player) {
    for (let [dir1, dir2] of DIRECTIONS) {
        let count = 1 + countInDirection(row, col, dir1[0], dir1[1], player)
            + countInDirection(row, col, dir2[0], dir2[1], player);
        if (count &gt;= 4) return true;
    }
    return false;
}

function checkDraw() {
    for (let c = 0; c &lt; COLS; c++) {
        if (gameBoard[0][c] == 0) return false;
    }
    return true;
}

function draw() {
    background(0, 100, 200);

    // ★ Hover efekti - taşı bırakmadan önce göster ★
    if (!gameOver &amp;&amp; mouseY &gt;= OFFSET_Y) {
        let hoverCol = floor(mouseX / CELL_SIZE);
        if (hoverCol &gt;= 0 &amp;&amp; hoverCol &lt; COLS &amp;&amp; gameBoard[0][hoverCol] == 0) {
            let x = hoverCol * CELL_SIZE + CELL_SIZE / 2;
            if (currentPlayer == 1) {
                fill(255, 0, 0, 150);  // Yarı saydam kırmızı
            } else {
                fill(255, 255, 0, 150);  // Yarı saydam sarı
            }
            noStroke();
            ellipse(x, 20, PIECE_SIZE * 0.8, PIECE_SIZE * 0.8);
        }
    }

    drawBoard();

    if (gameOver) {
        drawGameOver();
    } else {
        fill(255);
        textSize(16);
        textAlign(LEFT);
        text((currentPlayer == 1) ? "Sıra: Kırmızı" : "Sıra: Sarı", 10, 25);
    }
}

function drawBoard() {
    for (let r = 0; r &lt; ROWS; r++) {
        for (let c = 0; c &lt; COLS; c++) {
            let x = c * CELL_SIZE + CELL_SIZE / 2;
            let y = r * CELL_SIZE + CELL_SIZE / 2 + OFFSET_Y;

            if (gameBoard[r][c] == 0) fill(255);
            else if (gameBoard[r][c] == 1) fill(255, 0, 0);
            else fill(255, 255, 0);

            noStroke();
            ellipse(x, y, PIECE_SIZE, PIECE_SIZE);
        }
    }
}

function drawGameOver() {
    fill(0, 0, 0, 150);
    rect(0, OFFSET_Y, width, height - OFFSET_Y);
    fill(255);
    textSize(40);
    textAlign(CENTER, CENTER);

    if (winner == 0) {
        text("BERABERE!", width / 2, height / 2);
    } else {
        text((winner == 1) ? "KIRMIZI KAZANDI!" : "SARI KAZANDI!", width / 2, height / 2);
    }

    textSize(20);
    text("Yeniden başlamak için tıklayın", width / 2, height / 2 + 50);
}

function mousePressed() {
    if (gameOver) {
        resetGame();
        return;
    }
    if (mouseY &lt; OFFSET_Y) return;

    let sutun = floor(mouseX / CELL_SIZE);
    if (sutun &gt;= 0 &amp;&amp; sutun &lt; COLS) {
        dropPiece(sutun);
    }
}

function dropPiece(sutun) {
    for (let satir = ROWS - 1; satir &gt;= 0; satir--) {
        if (gameBoard[satir][sutun] == 0) {
            gameBoard[satir][sutun] = currentPlayer;

            if (checkWin(satir, sutun, currentPlayer)) {
                gameOver = true;
                winner = currentPlayer;
            } else if (checkDraw()) {
                gameOver = true;
                winner = 0;
            } else {
                currentPlayer = (currentPlayer == 1) ? 2 : 1;
            }
            return;
        }
    }
}

function resetGame() {
    initBoard();
    currentPlayer = 1;
    gameOver = false;
    winner = 0;
}
Adım 18

Düşme Animasyonu

Taşların aniden yerleşmesi yerine, yukarıdan aşağı düşme animasyonu ekleyelim.

🎬 Animasyon Mantığı

  • Taş düşürüldüğünde hemen yerleştirme
  • Bunun yerine: animasyon değişkenleri ayarla
  • Her frame'de taşı biraz aşağı hareket ettir
  • Hedef konuma ulaşınca tahtaya yerleştir
ADIM 18 sketch.js
// Animasyon değişkenleri
let isAnimating = false;
let animPiece = {
    col: 0,
    targetRow: 0,
    currentY: 0,
    player: 0
};
const DROP_SPEED = 15; // Piksel/frame

function dropPiece(sutun) {
    if (gameOver || isAnimating) return;
    
    // Boş satırı bul
    for (let satir = ROWS - 1; satir >= 0; satir--) {
        if (gameBoard[satir][sutun] == 0) {
            // Animasyonu başlat
            isAnimating = true;
            animPiece.col = sutun;
            animPiece.targetRow = satir;
            animPiece.currentY = 0; // Üstten başla
            animPiece.player = currentPlayer;
            return;
        }
    }
}

function updateAnimation() {
    if (!isAnimating) return;
    
    let targetY = animPiece.targetRow * CELL_SIZE + CELL_SIZE/2 + OFFSET_Y;
    
    if (animPiece.currentY < targetY) {
        animPiece.currentY += DROP_SPEED;
    } else {
        // Animasyon bitti, taşı yerleştir
        gameBoard[animPiece.targetRow][animPiece.col] = animPiece.player;
        isAnimating = false;
        
        // Kazanma kontrolü
        if (checkWin(animPiece.targetRow, animPiece.col, animPiece.player)) {
            gameOver = true;
            winner = animPiece.player;
        } else {
            currentPlayer = (currentPlayer == 1) ? 2 : 1;
        }
    }
}

function drawAnimatingPiece() {
    if (!isAnimating) return;
    
    if (animPiece.player == 1) {
        fill(255, 0, 0);
    } else {
        fill(255, 255, 0);
    }
    
    let x = animPiece.col * CELL_SIZE + CELL_SIZE/2 + OFFSET_X;
    ellipse(x, animPiece.currentY, PIECE_SIZE, PIECE_SIZE);
}

function draw() {
    background(0, 100, 200);
    
    updateAnimation();
    drawAnimatingPiece();
    drawBoard();
    // ...
}
📂 Adım 18 Sonrası Tam Kod
sketch.js - Tam Kod (Adım 18)
// Adım 18: Düşme Animasyonu
const ROWS = 6;
const COLS = 7;
const CELL_SIZE = 80;
const PIECE_SIZE = 70;
const OFFSET_Y = 40;

let gameBoard = [];
let currentPlayer = 1;
let gameOver = false;
let winner = 0;

// ★ Animasyon değişkenleri ★
let isAnimating = false;
let animY = 0;
let animTargetY = 0;
let animCol = 0;
let animRow = 0;
let animPlayer = 0;

const DIRECTIONS = [
    [[0, 1], [0, -1]], [[1, 0], [-1, 0]],
    [[1, 1], [-1, -1]], [[1, -1], [-1, 1]]
];

function setup() {
    createCanvas(COLS * CELL_SIZE, ROWS * CELL_SIZE + OFFSET_Y);
    initBoard();
}

function initBoard() {
    for (let r = 0; r &lt; ROWS; r++) {
        gameBoard[r] = [];
        for (let c = 0; c &lt; COLS; c++) {
            gameBoard[r][c] = 0;
        }
    }
}

function countInDirection(row, col, dRow, dCol, player) {
    let count = 0;
    let r = row + dRow;
    let c = col + dCol;
    while (r &gt;= 0 &amp;&amp; r &lt; ROWS &amp;&amp; c &gt;= 0 &amp;&amp; c &lt; COLS &amp;&amp; gameBoard[r][c] == player) {
        count++;
        r += dRow;
        c += dCol;
    }
    return count;
}

function checkWin(row, col, player) {
    for (let [dir1, dir2] of DIRECTIONS) {
        let count = 1 + countInDirection(row, col, dir1[0], dir1[1], player)
            + countInDirection(row, col, dir2[0], dir2[1], player);
        if (count &gt;= 4) return true;
    }
    return false;
}

function checkDraw() {
    for (let c = 0; c &lt; COLS; c++) {
        if (gameBoard[0][c] == 0) return false;
    }
    return true;
}

function draw() {
    background(0, 100, 200);

    // Hover efekti
    if (!gameOver &amp;&amp; !isAnimating &amp;&amp; mouseY &gt;= OFFSET_Y) {
        let hoverCol = floor(mouseX / CELL_SIZE);
        if (hoverCol &gt;= 0 &amp;&amp; hoverCol &lt; COLS &amp;&amp; gameBoard[0][hoverCol] == 0) {
            let x = hoverCol * CELL_SIZE + CELL_SIZE / 2;
            if (currentPlayer == 1) fill(255, 0, 0, 150);
            else fill(255, 255, 0, 150);
            noStroke();
            ellipse(x, 20, PIECE_SIZE * 0.8, PIECE_SIZE * 0.8);
        }
    }

    drawBoard();

    // ★ Animasyon çizimi ★
    if (isAnimating) {
        let x = animCol * CELL_SIZE + CELL_SIZE / 2;
        if (animPlayer == 1) fill(255, 0, 0);
        else fill(255, 255, 0);
        noStroke();
        ellipse(x, animY, PIECE_SIZE, PIECE_SIZE);

        // Animasyonu ilerlet
        animY += 15;  // Düşme hızı
        if (animY &gt;= animTargetY) {
            animY = animTargetY;
            isAnimating = false;
            finishDrop();
        }
    }

    if (gameOver) {
        drawGameOver();
    } else if (!isAnimating) {
        fill(255);
        textSize(16);
        textAlign(LEFT);
        text((currentPlayer == 1) ? "Sıra: Kırmızı" : "Sıra: Sarı", 10, 25);
    }
}

function drawBoard() {
    for (let r = 0; r &lt; ROWS; r++) {
        for (let c = 0; c &lt; COLS; c++) {
            let x = c * CELL_SIZE + CELL_SIZE / 2;
            let y = r * CELL_SIZE + CELL_SIZE / 2 + OFFSET_Y;

            if (gameBoard[r][c] == 0) fill(255);
            else if (gameBoard[r][c] == 1) fill(255, 0, 0);
            else fill(255, 255, 0);

            noStroke();
            ellipse(x, y, PIECE_SIZE, PIECE_SIZE);
        }
    }
}

function drawGameOver() {
    fill(0, 0, 0, 150);
    rect(0, OFFSET_Y, width, height - OFFSET_Y);
    fill(255);
    textSize(40);
    textAlign(CENTER, CENTER);

    if (winner == 0) text("BERABERE!", width / 2, height / 2);
    else text((winner == 1) ? "KIRMIZI KAZANDI!" : "SARI KAZANDI!", width / 2, height / 2);

    textSize(20);
    text("Yeniden başlamak için tıklayın", width / 2, height / 2 + 50);
}

function mousePressed() {
    if (gameOver) {
        resetGame();
        return;
    }
    if (isAnimating) return;  // Animasyon sırasında tıklamayı engelle
    if (mouseY &lt; OFFSET_Y) return;

    let sutun = floor(mouseX / CELL_SIZE);
    if (sutun &gt;= 0 &amp;&amp; sutun &lt; COLS) {
        startDrop(sutun);
    }
}

// ★ Animasyonu başlat ★
function startDrop(sutun) {
    for (let satir = ROWS - 1; satir &gt;= 0; satir--) {
        if (gameBoard[satir][sutun] == 0) {
            isAnimating = true;
            animCol = sutun;
            animRow = satir;
            animPlayer = currentPlayer;
            animY = OFFSET_Y;
            animTargetY = satir * CELL_SIZE + CELL_SIZE / 2 + OFFSET_Y;
            return;
        }
    }
}

// ★ Animasyon bittiğinde ★
function finishDrop() {
    gameBoard[animRow][animCol] = animPlayer;

    if (checkWin(animRow, animCol, animPlayer)) {
        gameOver = true;
        winner = animPlayer;
    } else if (checkDraw()) {
        gameOver = true;
        winner = 0;
    } else {
        currentPlayer = (currentPlayer == 1) ? 2 : 1;
    }
}

function resetGame() {
    initBoard();
    currentPlayer = 1;
    gameOver = false;
    winner = 0;
    isAnimating = false;
}
Adım 19

Skor Sistemi

Oyuncuların kazanma sayılarını takip edelim.

ADIM 19 sketch.js
let score = {
    player1: 0,
    player2: 0
};

// Kazanma durumunda:
if (winner == 1) {
    score.player1++;
} else if (winner == 2) {
    score.player2++;
}

// Skoru göster:
function drawScore() {
    fill(255);
    textSize(18);
    textAlign(CENTER);
    text("Kırmızı: " + score.player1 + " - Sarı: " + score.player2, 
         width/2, 25);
}
📂 Adım 19 Sonrası Tam Kod
sketch.js - Tam Kod (Adım 19)
// Adım 19: Skor Sistemi
const ROWS = 6;
const COLS = 7;
const CELL_SIZE = 80;
const PIECE_SIZE = 70;
const OFFSET_Y = 40;

let gameBoard = [];
let currentPlayer = 1;
let gameOver = false;
let winner = 0;

// ★ Skor değişkenleri ★
let score1 = 0;  // Kırmızı skor
let score2 = 0;  // Sarı skor

let isAnimating = false;
let animY = 0;
let animTargetY = 0;
let animCol = 0;
let animRow = 0;
let animPlayer = 0;

const DIRECTIONS = [
    [[0, 1], [0, -1]], [[1, 0], [-1, 0]],
    [[1, 1], [-1, -1]], [[1, -1], [-1, 1]]
];

function setup() {
    createCanvas(COLS * CELL_SIZE, ROWS * CELL_SIZE + OFFSET_Y);
    initBoard();
}

function initBoard() {
    for (let r = 0; r &lt; ROWS; r++) {
        gameBoard[r] = [];
        for (let c = 0; c &lt; COLS; c++) {
            gameBoard[r][c] = 0;
        }
    }
}

function countInDirection(row, col, dRow, dCol, player) {
    let count = 0;
    let r = row + dRow;
    let c = col + dCol;
    while (r &gt;= 0 &amp;&amp; r &lt; ROWS &amp;&amp; c &gt;= 0 &amp;&amp; c &lt; COLS &amp;&amp; gameBoard[r][c] == player) {
        count++;
        r += dRow;
        c += dCol;
    }
    return count;
}

function checkWin(row, col, player) {
    for (let [dir1, dir2] of DIRECTIONS) {
        let count = 1 + countInDirection(row, col, dir1[0], dir1[1], player)
            + countInDirection(row, col, dir2[0], dir2[1], player);
        if (count &gt;= 4) return true;
    }
    return false;
}

function checkDraw() {
    for (let c = 0; c &lt; COLS; c++) {
        if (gameBoard[0][c] == 0) return false;
    }
    return true;
}

function draw() {
    background(0, 100, 200);

    // ★ Skor gösterimi ★
    fill(255);
    textSize(16);
    textAlign(LEFT);
    text("Kırmızı: " + score1, 10, 25);
    textAlign(RIGHT);
    text("Sarı: " + score2, width - 10, 25);

    if (!gameOver &amp;&amp; !isAnimating &amp;&amp; mouseY &gt;= OFFSET_Y) {
        let hoverCol = floor(mouseX / CELL_SIZE);
        if (hoverCol &gt;= 0 &amp;&amp; hoverCol &lt; COLS &amp;&amp; gameBoard[0][hoverCol] == 0) {
            let x = hoverCol * CELL_SIZE + CELL_SIZE / 2;
            if (currentPlayer == 1) fill(255, 0, 0, 150);
            else fill(255, 255, 0, 150);
            noStroke();
            ellipse(x, 20, PIECE_SIZE * 0.6, PIECE_SIZE * 0.6);
        }
    }

    drawBoard();

    if (isAnimating) {
        let x = animCol * CELL_SIZE + CELL_SIZE / 2;
        if (animPlayer == 1) fill(255, 0, 0);
        else fill(255, 255, 0);
        noStroke();
        ellipse(x, animY, PIECE_SIZE, PIECE_SIZE);

        animY += 15;
        if (animY &gt;= animTargetY) {
            animY = animTargetY;
            isAnimating = false;
            finishDrop();
        }
    }

    if (gameOver) {
        drawGameOver();
    } else if (!isAnimating) {
        textAlign(CENTER);
        text((currentPlayer == 1) ? "Sıra: Kırmızı" : "Sıra: Sarı", width / 2, 25);
    }
}

function drawBoard() {
    for (let r = 0; r &lt; ROWS; r++) {
        for (let c = 0; c &lt; COLS; c++) {
            let x = c * CELL_SIZE + CELL_SIZE / 2;
            let y = r * CELL_SIZE + CELL_SIZE / 2 + OFFSET_Y;

            if (gameBoard[r][c] == 0) fill(255);
            else if (gameBoard[r][c] == 1) fill(255, 0, 0);
            else fill(255, 255, 0);

            noStroke();
            ellipse(x, y, PIECE_SIZE, PIECE_SIZE);
        }
    }
}

function drawGameOver() {
    fill(0, 0, 0, 150);
    rect(0, OFFSET_Y, width, height - OFFSET_Y);
    fill(255);
    textSize(40);
    textAlign(CENTER, CENTER);

    if (winner == 0) text("BERABERE!", width / 2, height / 2);
    else text((winner == 1) ? "KIRMIZI KAZANDI!" : "SARI KAZANDI!", width / 2, height / 2);

    textSize(20);
    text("Yeniden başlamak için tıklayın", width / 2, height / 2 + 50);
}

function mousePressed() {
    if (gameOver) {
        resetGame();
        return;
    }
    if (isAnimating) return;
    if (mouseY &lt; OFFSET_Y) return;

    let sutun = floor(mouseX / CELL_SIZE);
    if (sutun &gt;= 0 &amp;&amp; sutun &lt; COLS) {
        startDrop(sutun);
    }
}

function startDrop(sutun) {
    for (let satir = ROWS - 1; satir &gt;= 0; satir--) {
        if (gameBoard[satir][sutun] == 0) {
            isAnimating = true;
            animCol = sutun;
            animRow = satir;
            animPlayer = currentPlayer;
            animY = OFFSET_Y;
            animTargetY = satir * CELL_SIZE + CELL_SIZE / 2 + OFFSET_Y;
            return;
        }
    }
}

function finishDrop() {
    gameBoard[animRow][animCol] = animPlayer;

    if (checkWin(animRow, animCol, animPlayer)) {
        gameOver = true;
        winner = animPlayer;
        // ★ Kazanan için skor artır ★
        if (winner == 1) score1++;
        else score2++;
    } else if (checkDraw()) {
        gameOver = true;
        winner = 0;
    } else {
        currentPlayer = (currentPlayer == 1) ? 2 : 1;
    }
}

function resetGame() {
    initBoard();
    currentPlayer = 1;
    gameOver = false;
    winner = 0;
    isAnimating = false;
    // NOT: Skorlar sıfırlanmaz!
}
Adım 20

Kazanan Çizgiyi Vurgulama

Kazanan 4'lü çizgiyi görsel olarak vurgulayalım.

ADIM 20 sketch.js
let winningCells = []; // Kazanan hücrelerin koordinatları

// checkHorizontal içinde kazanma bulunduğunda:
if (count >= 4) {
    // Kazanan hücreleri kaydet
    winningCells = [];
    for (let i = 0; i < 4; i++) {
        winningCells.push({row: satir, col: c - i});
    }
    return true;
}

// drawBoard içinde:
function isWinningCell(r, c) {
    for (let cell of winningCells) {
        if (cell.row == r && cell.col == c) {
            return true;
        }
    }
    return false;
}

// Kazanan hücrelere parlama efekti:
if (isWinningCell(r, c)) {
    strokeWeight(4);
    stroke(255); // Beyaz çerçeve
} else {
    noStroke();
}
📂 Adım 20 Sonrası Tam Kod
sketch.js - Tam Kod (Adım 20)
// Adım 20: Kazanan Çizgisi Vurgulama
const ROWS = 6;
const COLS = 7;
const CELL_SIZE = 80;
const PIECE_SIZE = 70;
const OFFSET_Y = 40;

let gameBoard = [];
let currentPlayer = 1;
let gameOver = false;
let winner = 0;
let score1 = 0;
let score2 = 0;

// ★ Kazanan çizgisi koordinatları ★
let winningCells = [];

let isAnimating = false;
let animY = 0;
let animTargetY = 0;
let animCol = 0;
let animRow = 0;
let animPlayer = 0;

const DIRECTIONS = [
    [[0, 1], [0, -1]], [[1, 0], [-1, 0]],
    [[1, 1], [-1, -1]], [[1, -1], [-1, 1]]
];

function setup() {
    createCanvas(COLS * CELL_SIZE, ROWS * CELL_SIZE + OFFSET_Y);
    initBoard();
}

function initBoard() {
    for (let r = 0; r &lt; ROWS; r++) {
        gameBoard[r] = [];
        for (let c = 0; c &lt; COLS; c++) {
            gameBoard[r][c] = 0;
        }
    }
}

function countInDirection(row, col, dRow, dCol, player) {
    let count = 0;
    let r = row + dRow;
    let c = col + dCol;
    while (r &gt;= 0 &amp;&amp; r &lt; ROWS &amp;&amp; c &gt;= 0 &amp;&amp; c &lt; COLS &amp;&amp; gameBoard[r][c] == player) {
        count++;
        r += dRow;
        c += dCol;
    }
    return count;
}

// ★ Kazanan hücreleri bulan geliştirilmiş checkWin ★
function checkWin(row, col, player) {
    for (let [dir1, dir2] of DIRECTIONS) {
        let cells = [[row, col]];

        // İlk yönde say ve hücreleri topla
        let r = row + dir1[0];
        let c = col + dir1[1];
        while (r &gt;= 0 &amp;&amp; r &lt; ROWS &amp;&amp; c &gt;= 0 &amp;&amp; c &lt; COLS &amp;&amp; gameBoard[r][c] == player) {
            cells.push([r, c]);
            r += dir1[0];
            c += dir1[1];
        }

        // Ters yönde say ve hücreleri topla
        r = row + dir2[0];
        c = col + dir2[1];
        while (r &gt;= 0 &amp;&amp; r &lt; ROWS &amp;&amp; c &gt;= 0 &amp;&amp; c &lt; COLS &amp;&amp; gameBoard[r][c] == player) {
            cells.push([r, c]);
            r += dir2[0];
            c += dir2[1];
        }

        if (cells.length &gt;= 4) {
            winningCells = cells;
            return true;
        }
    }
    return false;
}

function checkDraw() {
    for (let c = 0; c &lt; COLS; c++) {
        if (gameBoard[0][c] == 0) return false;
    }
    return true;
}

function draw() {
    background(0, 100, 200);

    fill(255);
    textSize(16);
    textAlign(LEFT);
    text("Kırmızı: " + score1, 10, 25);
    textAlign(RIGHT);
    text("Sarı: " + score2, width - 10, 25);

    if (!gameOver &amp;&amp; !isAnimating &amp;&amp; mouseY &gt;= OFFSET_Y) {
        let hoverCol = floor(mouseX / CELL_SIZE);
        if (hoverCol &gt;= 0 &amp;&amp; hoverCol &lt; COLS &amp;&amp; gameBoard[0][hoverCol] == 0) {
            let x = hoverCol * CELL_SIZE + CELL_SIZE / 2;
            if (currentPlayer == 1) fill(255, 0, 0, 150);
            else fill(255, 255, 0, 150);
            noStroke();
            ellipse(x, 20, PIECE_SIZE * 0.6, PIECE_SIZE * 0.6);
        }
    }

    drawBoard();

    if (isAnimating) {
        let x = animCol * CELL_SIZE + CELL_SIZE / 2;
        if (animPlayer == 1) fill(255, 0, 0);
        else fill(255, 255, 0);
        noStroke();
        ellipse(x, animY, PIECE_SIZE, PIECE_SIZE);

        animY += 15;
        if (animY &gt;= animTargetY) {
            animY = animTargetY;
            isAnimating = false;
            finishDrop();
        }
    }

    if (gameOver) {
        drawGameOver();
    } else if (!isAnimating) {
        textAlign(CENTER);
        text((currentPlayer == 1) ? "Sıra: Kırmızı" : "Sıra: Sarı", width / 2, 25);
    }
}

function drawBoard() {
    for (let r = 0; r &lt; ROWS; r++) {
        for (let c = 0; c &lt; COLS; c++) {
            let x = c * CELL_SIZE + CELL_SIZE / 2;
            let y = r * CELL_SIZE + CELL_SIZE / 2 + OFFSET_Y;

            // ★ Kazanan hücreleri vurgula ★
            let isWinningCell = false;
            if (gameOver &amp;&amp; winner != 0) {
                for (let cell of winningCells) {
                    if (cell[0] == r &amp;&amp; cell[1] == c) {
                        isWinningCell = true;
                        break;
                    }
                }
            }

            if (gameBoard[r][c] == 0) fill(255);
            else if (gameBoard[r][c] == 1) fill(255, 0, 0);
            else fill(255, 255, 0);

            noStroke();
            ellipse(x, y, PIECE_SIZE, PIECE_SIZE);

            // ★ Kazanan hücrelere halo efekti ★
            if (isWinningCell) {
                noFill();
                stroke(255);
                strokeWeight(4);
                ellipse(x, y, PIECE_SIZE + 10, PIECE_SIZE + 10);
            }
        }
    }
}

function drawGameOver() {
    fill(0, 0, 0, 150);
    rect(0, OFFSET_Y, width, height - OFFSET_Y);
    fill(255);
    textSize(40);
    textAlign(CENTER, CENTER);

    if (winner == 0) text("BERABERE!", width / 2, height / 2);
    else text((winner == 1) ? "KIRMIZI KAZANDI!" : "SARI KAZANDI!", width / 2, height / 2);

    textSize(20);
    text("Yeniden başlamak için tıklayın", width / 2, height / 2 + 50);
}

function mousePressed() {
    if (gameOver) {
        resetGame();
        return;
    }
    if (isAnimating) return;
    if (mouseY &lt; OFFSET_Y) return;

    let sutun = floor(mouseX / CELL_SIZE);
    if (sutun &gt;= 0 &amp;&amp; sutun &lt; COLS) {
        startDrop(sutun);
    }
}

function startDrop(sutun) {
    for (let satir = ROWS - 1; satir &gt;= 0; satir--) {
        if (gameBoard[satir][sutun] == 0) {
            isAnimating = true;
            animCol = sutun;
            animRow = satir;
            animPlayer = currentPlayer;
            animY = OFFSET_Y;
            animTargetY = satir * CELL_SIZE + CELL_SIZE / 2 + OFFSET_Y;
            return;
        }
    }
}

function finishDrop() {
    gameBoard[animRow][animCol] = animPlayer;

    if (checkWin(animRow, animCol, animPlayer)) {
        gameOver = true;
        winner = animPlayer;
        if (winner == 1) score1++;
        else score2++;
    } else if (checkDraw()) {
        gameOver = true;
        winner = 0;
    } else {
        currentPlayer = (currentPlayer == 1) ? 2 : 1;
    }
}

function resetGame() {
    initBoard();
    currentPlayer = 1;
    gameOver = false;
    winner = 0;
    isAnimating = false;
    winningCells = [];  // ★ Kazanan hücreleri temizle ★
}
Adım 21

OOP'ye Giriş - Game Class

Kodumuzu daha organize etmek için Object-Oriented Programming (OOP) kullanabiliriz. Tüm oyun mantığını bir sınıf içinde toplayalım.

🏗️ Neden OOP?

  • Kod daha organize ve okunabilir
  • Değişkenler ve fonksiyonlar bir arada
  • Birden fazla oyun örneği oluşturulabilir
  • Test etmesi daha kolay
ADIM 21 sketch.js
class Connect4Game {
    constructor(rows, cols, cellSize) {
        this.ROWS = rows;
        this.COLS = cols;
        this.CELL_SIZE = cellSize;
        this.PIECE_SIZE = cellSize * 0.875;
        
        this.board = [];
        this.currentPlayer = 1;
        this.gameOver = false;
        this.winner = 0;
        this.score = {p1: 0, p2: 0};
        
        this.initBoard();
    }
    
    initBoard() {
        this.board = [];
        for (let r = 0; r < this.ROWS; r++) {
            this.board[r] = [];
            for (let c = 0; c < this.COLS; c++) {
                this.board[r][c] = 0;
            }
        }
    }
    
    dropPiece(col) {
        if (this.gameOver) return false;
        
        for (let r = this.ROWS - 1; r >= 0; r--) {
            if (this.board[r][col] == 0) {
                this.board[r][col] = this.currentPlayer;
                
                if (this.checkWin(r, col)) {
                    this.gameOver = true;
                    this.winner = this.currentPlayer;
                } else {
                    this.currentPlayer = (this.currentPlayer == 1) ? 2 : 1;
                }
                return true;
            }
        }
        return false;
    }
    
    checkWin(row, col) {
        let player = this.board[row][col];
        return this.checkHorizontal(row, player) ||
               this.checkVertical(col, player) ||
               this.checkDiagonals(row, col, player);
    }
    
    checkHorizontal(row, player) {
        let count = 0;
        for (let c = 0; c < this.COLS; c++) {
            if (this.board[row][c] == player) {
                count++;
                if (count >= 4) return true;
            } else {
                count = 0;
            }
        }
        return false;
    }
    
    checkVertical(col, player) {
        let count = 0;
        for (let r = 0; r < this.ROWS; r++) {
            if (this.board[r][col] == player) {
                count++;
                if (count >= 4) return true;
            } else {
                count = 0;
            }
        }
        return false;
    }
    
    checkDiagonals(row, col, player) {
        // Yön vektörleri ile akıllı kontrol
        const dirs = [[[1,1],[-1,-1]], [[1,-1],[-1,1]]];
        for (let dir of dirs) {
            let count = 1;
            count += this.countDir(row, col, dir[0], player);
            count += this.countDir(row, col, dir[1], player);
            if (count >= 4) return true;
        }
        return false;
    }
    
    countDir(row, col, d, player) {
        let count = 0, r = row + d[0], c = col + d[1];
        while (r >= 0 && r < this.ROWS && c >= 0 && c < this.COLS &&
               this.board[r][c] == player) {
            count++; r += d[0]; c += d[1];
        }
        return count;
    }
    
    reset() {
        this.initBoard();
        this.currentPlayer = 1;
        this.gameOver = false;
        this.winner = 0;
    }
}

// Kullanım:
let game;

function setup() {
    game = new Connect4Game(6, 7, 80);
    createCanvas(game.COLS * game.CELL_SIZE, 
                 game.ROWS * game.CELL_SIZE);
}
Final

🎮 Çalışan Oyun

Tüm adımların sonunda, tam fonksiyonel bir Connect4 oyunumuz oldu!

🎮 Connect4 - Final Demo
Kırmızı: 0 Sarı: 0
Sıra: Kırmızı
Özet

🎓 Neler Öğrendik?

Teknik Beceriler:

Konu Öğrenim Örnek
2D Diziler Grid tabanlı oyun tahtası gameBoard[satir][sutun]
İç İçe Döngüler 2D yapıları tarama for satır { for sütun { } }
Algoritma Tasarımı Kazanma kontrolü Yatay, dikey, çapraz kontrol
Verimlilik Sadece gerekli kontroller Son hamle etrafını kontrol
Parametrik Kod Sabitler ve değişkenler ROWS, COLS, CELL_SIZE
Animasyon Frame tabanlı hareket currentY += DROP_SPEED
OOP Sınıf tabanlı organizasyon class Connect4Game { }
1-3

Temel Yapı

Canvas, Grid, 2D Dizi

→
4-6

Etkileşim

Tıklama, Taş Düşürme

→
7-14

Oyun Mantığı

Kazanma Algoritması

→
15-18

İyileştirmeler

Parametrik, Animasyon

→
19-21

Profesyonel

Skor, OOP

🚀 Sonraki Adımlar

  • AI Rakip: Minimax algoritması ile bilgisayar rakibi
  • Ses Efektleri: Taş düşme, kazanma sesleri
  • Tema Seçimi: Farklı renk şemaları
  • Online Multiplayer: Socket.io ile çok oyunculu
  • Mobil Uyumluluk: Touch events