Bir kodun hikayesi: Sıfırdan profesyonel bir oyuna...
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:
Her p5.js projesinde olduğu gibi, önce canvas'ımızı oluşturuyoruz. Connect4 için 7 sütun ve 6 satır gerekiyor.
function setup() { createCanvas(560, 480); } function draw() { background(0, 100, 200); // Mavi arka plan }
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.
Şimdi 7x6'lık grid'imizi çizelim. Her hücre için bir daire çizeceğiz.
2D grid çizmek için iki döngü kullanıyoruz:
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); } } }
+40 ekleyerek dairenin merkezini hücrenin ortasına getiriyoruz (80/2 = 40)
Görsel grid'i çizdik, ama oyun durumunu nerede tutacağız? Bunun için 2 boyutlu bir dizi (2D array) kullanacağız.
Her hücre 3 durumdan birinde olabilir:
0 = Boş hücre1 = Oyuncu 1 (Kırmızı)2 = Oyuncu 2 (Sarı)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); } } }
Oyuncunun hangi sütuna tıkladığını algılamamız gerekiyor. mousePressed() fonksiyonu ve
mouseX değişkeni bunu sağlar.
Mouse X koordinatından sütun numarasını bulmak için:
// ... önceki kod ... function mousePressed() { let sutun = floor(mouseX / 80); print("Tıklanan sütun:", sutun); }
Connect4'te taşlar yukarıdan düşer ve en alttaki boş hücreye yerleşir. Bu mantığı kodlamamız gerekiyor.
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; 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.
Oyuncuların sıranın kimde olduğunu bilmesi gerekiyor. Ekrana bir metin ekleyelim.
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); } }
Şimdi en kritik kısma geldik: 4 taşın yan yana olup olmadığını kontrol etmek.
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!
Her hamlede TÜM tahtayı tara:
Sadece SON TAŞIN etrafını kontrol et:
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!
Kazanma 4 farklı yönde olabilir. Her yön için son taştan iki tarafa bakıp aynı renk taşları sayacağız:
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)
Fonksiyon, belirli bir görevi yapan kod parçasıdır. Bir kez yazıp, istediğimiz kadar çağırabiliriz.
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.
Bir yönde kaç tane aynı renk taş olduğunu sayan yardımcı fonksiyon yazacağız:
// 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 }
Artık countInDirection() fonksiyonumuz var. Şimdi 4 yön için bu fonksiyonu kullanarak
kazanma kontrolü yapacağız.
Her yön için:
return komutu iki şey yapar:
Kazanma kontrolünde return true "evet kazandı", return false "hayır
kazanmadı" anlamına gelir.
// ============================================ // 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 }
Şimdi dropPiece() fonksiyonunda taş konulduktan sonra kazanma kontrolü yapalım.
checkWin() çağırNormal dizi tek boyutlu bir listedir. 2D dizi ise "satır ve sütun" şeklinde organize edilmiş bir tablodur.
gameBoard[satir][sutun] şeklinde erişiyoruz: önce satır, sonra sütun!
// 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; }
Şimdi tüm görsel öğeleri çizen draw() fonksiyonunu yazalım.
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); }
İş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!
Bu noktada çalışan bir Connect4 oyununuz var:
// ============================================ // 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; }
currentPlayer = (currentPlayer == 1) ? 2 : 1;
Bu kısa yazım şu anlama gelir: "Eğer currentPlayer 1 ise 2 yap, değilse 1 yap."
Kazanan belli olduğunda bunu ekranda gösterelim.
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); } }
Oyun bittiğinde tıklayarak yeni oyun başlatabilmeliyiz.
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; }
Tahta dolduğunda ve kimse kazanmadığında beraberlik olur.
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 }
Kodumuzda çok fazla "sihirli sayı" (magic number) var: 6, 7, 80, 40... Bunları değişkenlere çevirelim.
const (Sabit)const ROWS = 6; // ✓ Asla değişmez const COLS = 7; // ✓ Asla değişmez ROWS = 8; // ✗ HATA!
let (Değişken)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.
// 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); } } }
Sadece sabitleri değiştirerek:
ROWS = 8, COLS = 8 → 8x8 tahtaCELL_SIZE = 60 → Daha küçük hücrelerPIECE_SIZE = 50 → Daha küçük taşlarTahtayı ekranın ortasına veya belirli bir konuma yerleştirmek isteyebiliriz. Bunun için offset (kaydırma) değerleri kullanırız.
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); // ... }
Oyuncunun hangi sütuna taş koyacağını görmesi için mouse'un üzerinde olduğu sütunda bir önizleme gösterelim.
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); } }
Taşların aniden yerleşmesi yerine, yukarıdan aşağı düşme animasyonu ekleyelim.
// 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(); // ... }
Oyuncuların kazanma sayılarını takip edelim.
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); }
Kazanan 4'lü çizgiyi görsel olarak vurgulayalım.
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(); }
Kodumuzu daha organize etmek için Object-Oriented Programming (OOP) kullanabiliriz. Tüm oyun mantığını bir sınıf içinde toplayalım.
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); }
Tüm adımların sonunda, tam fonksiyonel bir Connect4 oyunumuz oldu!
| 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 { } |
Canvas, Grid, 2D Dizi
Tıklama, Taş Düşürme
Kazanma Algoritması
Parametrik, Animasyon
Skor, OOP