Takip et

JavaScript ile Hızlı, Tarayıcı Tabanlı Bir Sudoku Platformu Nasıl Oluşturdum?

Her başarılı projenin temelinde sağlam bir mimari yatar. Sudoku platformunu geliştirirken, modülerlik, performans ve ölçeklenebilirlik ilkelerini gö…

JavaScript ile Hızlı, Tarayıcı Tabanlı Bir Sudoku Platformu Nasıl Oluşturdum?

Sudoku, milyonlarca insan tarafından sevilen, mantık ve stratejiye dayalı klasik bir bulmaca oyunudur. Bu makalede, JavaScript kullanarak tarayıcıda akıcı ve hızlı çalışan bir Sudoku platformunu nasıl inşa ettiğimi detaylı bir şekilde anlatacağım. Geliştirme sürecindeki temel mimari kararlardan kullanıcı arayüzü tasarımına, oyun mantığının uygulanmasından performans optimizasyonlarına kadar birçok konuya değinerek, kendi tarayıcı tabanlı oyununuzu geliştirmek isteyenlere ilham vermeyi ve pratik bilgiler sunmayı hedefliyorum.

1. Sudoku Platformunun Temelleri ve Mimari Yaklaşım

Her başarılı projenin temelinde sağlam bir mimari yatar. Sudoku platformunu geliştirirken, modülerlik, performans ve ölçeklenebilirlik ilkelerini göz önünde bulundurdum. Amacım, sadece çalışan bir oyun değil, aynı zamanda bakımı kolay, genişletilebilir ve farklı zorluk seviyelerini destekleyebilen bir yapı kurmaktı.

1.1. Proje Yapısı ve Modülerlik

Proje dizin yapısını, her bir bileşenin kendi sorumluluk alanına sahip olacağı şekilde düzenledim. Bu, kodun okunabilirliğini artırırken, hata ayıklama ve yeni özellik ekleme süreçlerini de kolaylaştırdı. Örneğin, oyun mantığı (Sudoku çözücü, doğrulayıcı), UI bileşenleri (ızgara, butonlar) ve yardımcı araçlar (zamanlayıcı, skor tablosu) ayrı modüller halinde tutuldu.


/src
  /core
    sudokuSolver.js
    sudokuGenerator.js
    validation.js
  /ui
    gridRenderer.js
    controls.js
  /utils
    timer.js
    storage.js
  app.js
  index.html
  style.css

1.2. Teknolojilerin Seçimi

Tarayıcı tabanlı bir platform için doğal olarak HTML, CSS ve JavaScript üçlüsünü kullandım. Ek bir framework (React, Vue, Angular gibi) kullanmaktan kaçındım, çünkü projenin kapsamı ve performans hedefleri Vanilla JavaScript ile daha iyi yönetilebilirdi. Bu yaklaşım, bağımlılıkları azaltarak sayfa yükleme süresini ve genel performansı optimize etmeme yardımcı oldu.

1.3. Durum Yönetimi

Oyunun mevcut durumunu (ızgara verisi, seçili hücre, oyun süresi, zorluk seviyesi vb.) tek bir JavaScript objesi içinde merkezi olarak yönettim. Bu, farklı modüllerin oyunun durumuna tutarlı bir şekilde erişmesini ve güncellemesini sağladı. Durum değişiklikleri, kullanıcı arayüzünün reaktif bir şekilde güncellenmesini tetikledi.

2. Oyun Tahtası ve Kullanıcı Arayüzü (UI) Geliştirme

Kullanıcı deneyimi, bir oyunun başarısında kritik rol oynar. Sudoku platformunun UI'sını tasarlarken basitlik, anlaşılırlık ve etkileşim odaklı bir yaklaşım benimsedim.

2.1. HTML Yapısı ve CSS Stilleri

Sudoku tahtası, 9x9'luk bir ızgarayı temsil eden bir HTML

veya
yapısıyla oluşturuldu. Her hücre, kullanıcının sayı girebileceği bir elementi veya sadece sayıyı gösterecek bir
olarak tasarlandı. CSS ile hücrelerin boyutları, kenarlıkları ve özellikle 3x3'lük blokları ayıran kalın çizgiler görsel olarak belirginleştirildi. Aktif hücre, yanlış girişler ve sabit sayılar için farklı stil sınıfları tanımlandı.

2.2. JavaScript ile Dinamik Izgara Oluşturma

Izgara, sayfa yüklendiğinde JavaScript tarafından dinamik olarak oluşturuldu. Bu, oyunun farklı zorluk seviyelerinde veya farklı ızgara boyutlarında kolayca adapte olmasını sağladı. Her hücreye benzersiz bir ID atayarak (örn. cell-0-0), DOM manipülasyonlarını ve olay dinleyicilerini daha verimli hale getirdim.


function createSudokuGrid(gridData) {
    const gridElement = document.getElementById('sudoku-grid');
    gridElement.innerHTML = ''; // Önceki ızgarayı temizle

    for (let row = 0; row < 9; row++) {
        for (let col = 0; col < 9; col++) {
            const cell = document.createElement('div');
            cell.classList.add('sudoku-cell');
            cell.dataset.row = row;
            cell.dataset.col = col;

            const value = gridData[row][col];
            if (value !== 0) {
                cell.textContent = value;
                cell.classList.add('fixed'); // Başlangıçta verilen sayılar
            } else {
                const input = document.createElement('input');
                input.type = 'text';
                input.maxLength = 1;
                input.dataset.row = row;
                input.dataset.col = col;
                cell.appendChild(input);
            }
            gridElement.appendChild(cell);
        }
    }
}

2.3. Kullanıcı Etkileşimleri ve Olay Dinleyicileri

Kullanıcının hücrelere sayı girmesi, butonlara tıklaması gibi etkileşimler için olay dinleyicileri (event listeners) kullanıldı. Özellikle hücrelere giriş yapıldığında, girilen sayının geçerliliği anında kontrol edildi ve görsel geri bildirim sağlandı (örn. yanlış sayı kırmızı renkte gösterildi). Klavye navigasyonu da eklenerek kullanıcı deneyimi zenginleştirildi.

3. Sudoku Mantığı: Doğrulama ve Çözümleme Algoritmaları

Sudoku oyununun kalbi, sayıların kurallara uygunluğunu denetleyen doğrulama ve bulmacayı çözebilen çözümleme algoritmalarında yatar.

3.1. Sudoku Kurallarını Doğrulama

Bir Sudoku ızgarasının geçerli olup olmadığını kontrol etmek için üç temel kural seti uygulandı:

  1. Her satırda 1'den 9'a kadar her sayı bir kez bulunmalı.
  2. Her sütunda 1'den 9'a kadar her sayı bir kez bulunmalı.
  3. Her 3x3'lük blokta 1'den 9'a kadar her sayı bir kez bulunmalı.

Bu kontroller, kullanıcı bir sayı girdiğinde veya oyunun tamamlandığında tetiklenerek anında geri bildirim sağladı. Performans için, sadece değişen satır, sütun ve blok kontrol edildi.


function isValid(board, row, col, num) {
    // Satır kontrolü
    for (let x = 0; x < 9; x++) {
        if (board[row][x] === num && x !== col) return false;
    }

    // Sütun kontrolü
    for (let x = 0; x < 9; x++) {
        if (board[x][col] === num && x !== row) return false;
    }

    // 3x3 kutu kontrolü
    let startRow = row - (row % 3);
    let startCol = col - (col % 3);
    for (let i = 0; i < 3; i++) {
        for (let j = 0; j < 3; j++) {
            if (board[i + startRow][j + startCol] === num && (i + startRow !== row || j + startCol !== col)) return false;
        }
    }
    return true;
}

3.2. Geri İzlemeli (Backtracking) Çözümleme Algoritması

Sudoku bulmacalarını çözmek ve yeni bulmacalar üretmek için geri izlemeli (backtracking) algoritmayı kullandım. Bu algoritma, boş hücreleri sırayla doldurmaya çalışır. Bir sayı yerleştirildiğinde, bir sonraki boş hücreye geçer. Eğer bir noktada geçerli bir sayı bulunamazsa, bir önceki adıma geri döner ve farklı bir sayı denemeye başlar. Bu yöntem, tüm olası çözümleri sistematik olarak keşfeder.


function solveSudoku(board) {
    for (let row = 0; row < 9; row++) {
        for (let col = 0; col < 9; col++) {
            if (board[row][col] === 0) { // Boş hücre bul
                for (let num = 1; num <= 9; num++) {
                    if (isValid(board, row, col, num)) {
                        board[row][col] = num;
                        if (solveSudoku(board)) { // Rekürsif çağrı
                            return true;
                        } else {
                            board[row][col] = 0; // Geri izleme: sayıyı geri al
                        }
                    }
                }
                return false; // Geçerli sayı bulunamadı
            }
        }
    }
    return true; // Tüm hücreler dolduruldu
}

4. Performans Optimizasyonu ve Akıcı Kullanıcı Deneyimi

Tarayıcı tabanlı bir uygulama için performans kritik öneme sahiptir. Özellikle Sudoku gibi sürekli etkileşim gerektiren bir oyunda, UI'ın donmaması ve işlemlerin hızlı yanıt vermesi beklenir.

4.1. DOM Manipülasyonlarını Azaltma

DOM manipülasyonları pahalı işlemlerdir. Bu nedenle, ızgarayı güncellerken mümkün olduğunca az DOM işlemi yapmaya özen gösterdim. Örneğin, bir hücrenin değeri değiştiğinde tüm ızgarayı yeniden çizmek yerine, sadece ilgili hücrenin içeriğini güncelledim. Ayrıca, olay dinleyicilerini hücrelere tek tek eklemek yerine, olay delegasyonu (event delegation) kullanarak ana ızgara elementine tek bir dinleyici ekledim. Bu, bellek kullanımını ve performansını artırdı.

4.2. Algoritma Verimliliği

Sudoku çözücü ve üretici algoritmalarının verimliliği doğrudan oyunun hızını etkiler. Geri izleme algoritması, en kötü durumda eksponansiyel karmaşıklığa sahip olsa da, Sudoku'nun 9x9 gibi sabit ve küçük boyutu için genellikle yeterince hızlı çalışır. Ancak, daha büyük ızgaralar veya daha karmaşık bulmacalar için Dancing Links (DLX) gibi daha gelişmiş algoritmalar düşünülebilir.

4.3. Zamanlayıcı ve Animasyon Optimizasyonları

Oyun içi zamanlayıcı ve olası animasyonlar için requestAnimationFrame kullanarak tarayıcının render döngüsüyle senkronize çalıştım. Bu, CPU kullanımını azaltarak daha akıcı animasyonlar ve daha iyi pil ömrü sağladı.

5. Oyun Üretimi ve Zorluk Seviyeleri

Sadece bir Sudoku çözücüye sahip olmak yeterli değil; platformun sürekli yeni ve oynanabilir bulmacalar sunabilmesi gerekiyordu.

5.1. Sudoku Bulmacası Üretimi

Yeni bir Sudoku bulmacası üretmek için aşağıdaki adımları izledim:

  1. Tamamen dolu ve geçerli bir Sudoku ızgarası oluşturmak için geri izleme algoritmasını kullandım (rastgele sayılarla başlayarak).
  2. Bu dolu ızgaradan rastgele hücreleri sırayla kaldırdım.
  3. Her kaldırma işleminden sonra, kalan bulmacanın tek bir çözümü olup olmadığını kontrol ettim. Eğer birden fazla çözümü varsa veya çözümü kalmazsa, kaldırma işlemini geri aldım.

Bu yöntem, her zaman tekil çözümü olan geçerli Sudoku bulmacaları üretmemi sağladı.

5.2. Zorluk Seviyeleri

Oyunun zorluk seviyesini belirlemek için başlangıçta kaldırılan hücre sayısını kullandım. Ne kadar çok hücre kaldırılırsa, bulmaca o kadar zor olur. Ancak, sadece hücre sayısı değil, aynı zamanda kalan sayıların dağılımı ve bulmacayı çözmek için gereken mantıksal adımların karmaşıklığı da zorluğu etkiler. Bu nedenle, zorluk seviyelerini ayarlarken hem kaldırılan hücre sayısını hem de çözüm algoritmasının adımlarını göz önünde bulunduran deneysel bir yaklaşım benimsedim.

Örnek zorluk seviyesi tablosu:

Zorluk Seviyesi Başlangıçta Açık Hücre Sayısı (Yaklaşık) Açıklama
Kolay 45-55 Çoğunlukla basit çıkarımlar gerektirir.
Orta 35-44 Biraz daha fazla mantık ve ileri teknikler.
Zor 25-34 Gelişmiş Sudoku teknikleri (X-Wing, Swordfish) gerekebilir.
Uzman 20-24 Çok az ipucu, derinlemesine analiz gerektirir.

6. Gelişmiş Özellikler ve Gelecek Planları

Temel oyun mekaniği oluştuktan sonra, kullanıcı deneyimini zenginleştirmek ve platformu daha çekici hale getirmek için bazı ek özellikler ekledim ve gelecekteki geliştirmeler için planlar yaptım.

6.1. İpuçları ve Not Alma

Kullanıcıların zorlandığında yardım alabilmesi için "İpucu" butonu ekledim. Bu buton, çözüm algoritmasını kullanarak bir sonraki doğru sayıyı gösterir. Ayrıca, kullanıcıların potansiyel sayıları not alabilmesi için her hücreye küçük notlar ekleme özelliği de sundum. Bu, gerçek hayattaki Sudoku deneyimini dijital ortama taşıdı.

6.2. Oyun Durumunu Kaydetme

Kullanıcının tarayıcısını kapatıp geri geldiğinde oyununa kaldığı yerden devam edebilmesi için oyun durumunu localStorage kullanarak kaydettim. Bu, özellikle uzun süren bulmacalar için kullanıcı dostu bir özellik oldu.


function saveGame() {
    localStorage.setItem('sudokuGame', JSON.stringify({
        board: currentBoard,
        initialBoard: initialBoard,
        time: currentTime,
        // Diğer durum bilgileri
    }));
}

function loadGame() {
    const savedGame = localStorage.getItem('sudokuGame');
    if (savedGame) {
        const data = JSON.parse(savedGame);
        currentBoard = data.board;
        initialBoard = data.initialBoard;
        currentTime = data.time;
        // UI'ı güncelle
        renderGrid(currentBoard);
        updateTimer(currentTime);
    }
}

6.3. Gelecek Planları

Gelecekte, kullanıcıların ilerlemesini takip etmek için bir skor tablosu, farklı temalar ve özelleştirme seçenekleri eklemeyi planlıyorum. Ayrıca, çok oyunculu Sudoku veya günlük meydan okumalar gibi sosyal özellikler de platformu daha ilgi çekici hale getirebilir.

Sonuç

JavaScript ile hızlı ve tarayıcı tabanlı bir Sudoku platformu inşa etmek, hem teknik bir meydan okuma hem de oldukça keyifli bir geliştirme süreciydi. Bu proje, Vanilla JavaScript'in gücünü, performans optimizasyonlarının önemini ve iyi bir kullanıcı deneyiminin nasıl tasarlanacağını anlamak için harika bir fırsat sundu. Modüler bir yapı, verimli algoritmalar ve dikkatli DOM yönetimi sayesinde, kullanıcıların keyifle oynayabileceği akıcı bir Sudoku deneyimi sunmayı başardım. Umarım bu makale, kendi web tabanlı oyunlarınızı geliştirmek isteyenlere yol gösterir ve ilham verir.

Sık Sorulan Sorular (SSS)

S1: Neden bir framework (React, Vue vb.) kullanmadınız?

Cevap: Projenin temel amacı, Vanilla JavaScript'in yeteneklerini ve performansını sergilemekti. Ayrıca, küçük ve orta ölçekli projelerde framework bağımlılığından kaçınmak, sayfa boyutunu küçültür ve yükleme sürelerini optimize eder. Bu, özellikle Sudoku gibi basit bir UI gerektiren bir oyun için yeterli ve hatta daha verimli bir yaklaşım olabilir.

S2: Sudoku bulmacası üretimi için kullandığınız algoritma her zaman tek çözümlü bulmacalar mı üretiyor?

Cevap: Evet, kullandığım yöntem (dolu bir ızgaradan hücreleri kaldırıp her adımda tek çözümlülüğü kontrol etme) sayesinde, her zaman tek çözümlü bulmacalar üretilmesi garanti edilir. Bu kontrol adımı, bulmacanın oynanabilirliği ve adil olması açısından kritik öneme sahiptir.

S3: Performans için başka hangi optimizasyonları düşünebilirim?

Cevap: Daha karmaşık uygulamalar için Web Workers kullanarak yoğun hesaplama gerektiren işlemleri (örneğin, çok zor seviyelerde bulmaca üretimi) ana iş parçacığından ayırabilirsiniz. Ayrıca, sanal DOM veya canvas tabanlı renderlama gibi teknikler de büyük ölçekli UI güncellemelerinde performansı artırabilir.

S4: Mobil cihazlar için responsive tasarım nasıl sağlandı?

Cevap: CSS Media Queries kullanarak ekran boyutlarına göre ızgara ve kontrol butonlarının boyutlarını ve düzenini ayarladım. Esnek kutu (Flexbox) ve ızgara (Grid) düzenleri, farklı cihazlarda uyumlu bir görünüm sağlamak için etkili bir şekilde kullanıldı.

S5: Hata durumları (örneğin, geçersiz giriş) nasıl yönetiliyor?

Cevap: Kullanıcı bir hücreye geçersiz bir sayı girdiğinde (örn. 1-9 dışında bir karakter), bu giriş anında reddedilir veya hücreye görsel bir hata sınıfı eklenerek (kırmızı kenarlık gibi) kullanıcıya geri bildirim sağlanır. Ayrıca, oyunun genel durumunu kontrol eden doğrulama fonksiyonları, her girişten sonra tetiklenerek olası hataları belirler.

Yorumlar
İçeriği beğendiniz mi? Bir tartışma başlatın veya görüşlerinizi paylaşın.
Yorum Yaz

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

Gönder

E-posta Bülteni
Yazılım Topluluğuna Katılın
En son güncellemeleri, yaratıcı ipuçlarını ve özel kaynakları doğrudan e-posta kutunuza alın. Tasarım ve inovasyonun geleceğini birlikte keşfedelim.