Tamam, şimdi yüzü açık veya kapalı olarak gösterebileceğimiz karolardan oluşan bir ağımız var. Ancak, oyunu gerçekten oynamak için bir yolumuz yok. Size hatırlatmak amacıyla, oyunun nasıl olduğunu burada bulabilirsiniz:
  • Oyun başlarken bütün kartlar kapalıdır.
  • Oyuncu iki kartı çevirir, bunları tıklayarak seçeriz.
  • İki karodaki görüntü aynıysa, yüzleri açık kalırlar. Öyle değilse, kısa bir beklemeden sonra yeniden ters çevrilmelidir.

Tıklayın-karoları çevirin

Tekrar karoları yüzleri kapalı olacak şekilde çevirerek başlarız:
for (var i = 0; i < tiles.length; i++) {
    tiles[i].drawFaceDown();
}
Bir fayansı ters çevirmek için, oyuncunun ona tıklaması gerekir. ProcessingJS programlarında tıklamaya yanıt vermek için, bir mouseClicked fonksiyonu tanımlayabiliriz ve fare her tıklandığında, bu kod yürütülür.
mouseClicked = function() {
  // process click somehow
};
Programımız oyuncunun bir yere tıkladığını gördüğünde, mouseX ve mouseY'yi kullanarak bir karoya mı tıkladıklarını kontrol etmek isteriz. Tile'a, verilen bir x ve y bir karonun alanının içindeyse, doğru verenisUnderMouse yöntemini ekleyerek başlayalım. Karoları çizdiğimiz şekle göre, karonun x ve y'si karonun sol üst köşesine denk gelir, dolayısıyla sadece verilen x this.x ile this.x + this.width ve verilen y this.y ile this.y + this.width arasındaysa, doğru değerini vermeliyiz:
Tile.prototype.isUnderMouse = function(x, y) {
return x >= this.x && x <= this.x + this.width  &&
y >= this.y && y <= this.y + this.width;
};
Artık bu yöntemi elde ettiğimize göre, bunu mouseClicked'de bir for döngüsünde kullanarak, her karonun mouseX ve mouseY altında olup olmadığını kontrol edebiliriz. Öyle olduğunda, bunları yüzü yukarıya bakacak şekilde çizeceğiz:
mouseClicked = function() {
    for (var i = 0; i < tiles.length; i++) {
        if (tiles[i].isUnderMouse(mouseX, mouseY)) {
            tiles[i].drawFaceUp();
        }
    }
};
Bu şöyle bir şeye benziyor. Birkaç karoya tıklayın ve neler olacağını görün:
Dikkatinizi çeken bir şey var mı? Oyunu oynamanın bir yönünü uyguladık - oyuncu karoları çevirebilecek, ama önemli bir kısıtlamamız eksik: bir seferde, ikiden fazla karo çevirememeliler.
Bir şekilde çevrilen karoların sayısını takip etmemiz gerekecek. Bunun basit bir yolu, bir oyuncu bir kartı açtığında, arttıracağımız global bir numFlipped değişkeni olurdu. Sonra, eğer numFlipped 2 ise, karo isabet kontrolünü atlayabiliriz.
var numFlipped = 0;
mouseClicked = function() {
    for (var i = 0; i < tiles.length; i++) {
        if (tiles[i].isUnderMouse(mouseX, mouseY)) {
            if (numFlipped < 2) {
              tiles[i].drawFaceUp();
              numFlipped++;
            }
        }
    }
};
Ancak, bu tam doğru değil - bu kodla, aynı karoya üst üste iki kere tıkladığımızda ne olur? Bunu bir düşünün - onu "iki kere" çevirir, numFlipped'i 2'ye ayarlar ve başka çevirmeleri engellerdi. Bunun yerine, yüzü yukarı bakmayan bir kartı çevirmek istiyoruz.
for döngüsündeki geçerli karonun yüzünün yukarı mı, aşağı mı baktığını nasıl biliriz? Daha önce drawFaceUp yöntemini çağırmış olabiliriz, ama bunun kaydını tutmadık. Ba ba bum, boole zamanı geldi! Bir karonun durumunu gösteren bir boole kuracak şekilde çizim yöntemlerini değiştirelim.
Tile.prototype.drawFaceDown = function() {
    // ...
    this.isFaceUp = false;
};

Tile.prototype.drawFaceUp = function() {
    // ...
    this.isFaceUp = true;
};
Şimdi fayansın gerçekten de aşağı baktığından emin olacak şekilde kontrolü değiştirebiliriz:
if (tiles[i].isUnderMouse(mouseX, mouseY)) {
            if (numFlipped < 2 && !tiles[i].isFaceUp) {
              tiles[i].drawFaceUp();
              numFlipped++;
            }
        }

Fayansları çevirmede gecikme

Tamam, iki-fayansı-çevirme mantığımız tamamlandı. Sonra ne yapıyoruz? Oyun kurallarına bir daha bakalım:
Eğer iki karonun görüntüsü aynıysa, yüzleri yukarı bakan şekilde kalırlar. Aksi takdirde, karolar bir süre sonra geri çevrilir.
Önce karoları otomatikman geri çevirecek ikinci kısmı uygulayacağız, çünkü yeni eşleşmeleri arayamazsak, birinci kısmı test etmemiz zorlaşır.
turnFaceDown yöntemini kullanarak karoları geri çevirmeyi biliyoruz, ancak bunu nasıl bir süre sonra yaparız? Her dil ve ortamın kod yürütmenin geciktirilmesine yaklaşımı farklıdır ve biz bunu ProcessingJS'de nasıl yapacağımızı bulmak zorundayız. Bir tür süre izleme - gecikme süresinin geçip geçmediğini - ve süre geçtikten sonra kodu çağırma yöntemine ihtiyacımız var. Size şunu önerirdim:
  • Başlangıç değeri boş olan global bir delayStartFC değişkeni oluştururuz.
  • mouseClicked fonksiyonunda, ikinci karoyu çevirdikten hemen sonra, frameCount'un güncel değerini delayStartFC'de saklarız. Bu değişken, program çalışmaya başladıktan sonra kaç çerçevenin geçtiğini bize söyler ve bu, programlardaki süreyi anlamanın bir yoludur.
  • draw fonksiyonunu tanımlarız, çünkü program çalıştıkça bu fonksiyon sürekli çağırılacaktır; yani her frameCount değeri için çağırılacağını biliyoruz. Bu fonksiyonda, frameCount'un yeni değerinin eskisinden önemli ölçüde yüksek olup olmadığını kontrol ederiz ve öyleyse, tüm karoları ters çevirir ve numFlipped'i 0'a eşitleriz. delayStartFC'yi de boş olarak ayarlarız.
Bu aslında, çok fazla kod uygulanmasını gerektirmeyen güzel bir çözümdür. Performansı en yüksek düzeye çıkarmak için, loop ve noLoop fonksiyonlarını kullanarak, çizim kodunun sadece gecikme oluşurken çağrıldığından emin olun. Hepsi burada:
var numFlipped = 0;
var delayStartFC = null;

mouseClicked = function() {
    for (var i = 0; i < tiles.length; i++) {
        if (tiles[i].isUnderMouse(mouseX, mouseY)) {
            if (numFlipped < 2 && !tiles[i].isFaceUp) {
                tiles[i].drawFaceUp();
                numFlipped++;
                if (numFlipped === 2) {
                    delayStartFC = frameCount;
                    loop();
                }
            } 
        }
    }
};

draw = function() {
    if (delayStartFC && (frameCount - delayStartFC) > 30) {
        for (var i = 0; i < tiles.length; i++) {
            tiles[i].drawFaceDown();
        }
        numFlipped = 0;
        delayStartFC = null;
        noLoop();
    }
};
Aşağıda bunu deneyin - karoların otomatik olarak geri çevrilmesi çok süper, öyle değil mi?
Ancak karoların her zaman tekrar geri çevrilmesini istemiyoruz - hatırlarsanız, iki karo eşleşirse, yüzleri açık kalmalılar. Bu, 2 tane karo çevrildiğinde ve gecikmeyi ayarlamadan önce, fayansların eşleşip eşleşmediğini kontrol etmemiz gerektiği anlamına gelir. Sahte kodda, bu şöyle olur:
eğer iki karo ters çevrildiyse:
    eğer birinci karonun yüzü ikincisiyle aynıysa:
       karoların yüzünü açık tut
İki karonun çevrili olmasıyla ilgili zaten bir kontrolümüz var, (numFlipped === 2), o zaman karoların yüzünün aynı olup olmadığını nasıl kontrol ederiz? İlk olarak, çevrilmiş iki fayansa erişmek için bir yola ihtiyacımız var. Dizide yineleme yapabiliriz, isFaceUp doğruya ayarlı olarak tüm karoları buluruz ve sonra bunları bir dizide saklarız. Daha iyi bir fikrim var: çevrilmiş karoları, kolay erişim için, bir dizide saklayın. Aslında, numFlipped'i bir diziyle değiştirebiliriz ve sonra önceden numFlipped kullandığımız her yerde flippedTiles.length kullanabiliriz.
Değiştirdiğimiz mouseClick fonksiyonumuz şuna benzer:
var flippedTiles = [];
var delayStartFC = null;

mouseClicked = function() {
    for (var i = 0; i < tiles.length; i++) {
        if (tiles[i].isUnderMouse(mouseX, mouseY)) {
            if (flippedTiles.length < 2 && !tiles[i].isFaceUp) {
                tiles[i].drawFaceUp();
                flippedTiles.push(tiles[i]);
                if (flippedTiles.length === 2) {
                    delayStartFC = frameCount;
                    loop();
                }
            } 
        }
    }
};
Şimdi, flippedTiles dizisindeki iki karonun yüzünün gerçekten de aynı olup olmadığını bulmamız lazım. Evet, face özelliği nedir? Bu bir nesnedir - ve aslında, eşleşen karoların yüzü aynı nesne olmalıdır, değişken bilgisayar belleğinde aynı yere işaret etmelidir. Bunun nedeni, her görüntü nesnesini sadece bir kere oluşturmamız (getImage("avatars/old-spice-man" için olduğu gibi) ve sonra, aynı görüntü nesnesini faces dizisine iki kere itmemizdir:
var face = possibleFaces[randomInd];
    selected.push(face);
    selected.push(face);
En azında JavaScript'te eşitlik işlemcisi nesnelere işaret eden iki değişkende kullanıldığında ve bu değişkenlerin ikisi de bellekteki aynı nesneyi belirttiğinde, doğru verir. Bu, kontrolümüzün basit olabileceği anlamına gelir - her karonun face özelliğinde eşitlik işlemcisini kullanın:
if (flippedTiles[0].face === flippedTiles[1].face) {
// ...
}
Artık karoların eşleştiğini bildiğimize göre, onların yüzünü açık tutmamız gerekiyor. Şu anda, bir gecikmeden sonra ters çevriliyorlar. Bu durumda animasyonu ayarlamayabilirdik, ancak unutmayın, sonraki çevirmelerde animasyon olacak - dolayısıyla buna güvenemeyiz. Bunun yerine, "hey, bunların hepsini çevirdiğimizde, şunları çevirmemeliyiz" diyebilmemizi sağlayan bir yola ihtiyacımız var. Boole özelliğinin uygun olduğu başka bir yere benziyor! Bu if bloğunun içinde bir isMatch özelliğini doğruya eşitleyebilir ve sonra sadece doğru olmadığında karoları çevirebiliriz:
if (flippedTiles[0].face === flippedTiles[1].face) {
flippedTiles[0].isMatch = true;
flippedTiles[1].isMatch = true;
}
for (var i = 0; i < tiles.length; i++) {
    if (!tiles[i].isMatch) {
        tiles[i].drawFaceDown();
    }
}
Aşağıdaki iki eşleşen karoyu bulduğunuzda, bekleme süresinden sonra (ve sonraki çevirmelerden sonra) bunlar yukarı bakar şekilde kalmalıdır:

Puanlama

Şimdi sadece bir şey eksik: puanlama. İşte size oyun kurallarının bununla ilgili kısmının hatırlatması:
Bu oyunun amacı, bütün kartların mümkün olduğunca az denemede açılmasıdır (yani, tüm eş resimlerin bulunması).
Deneme sayısını nasıl izleriz? Evet, bir "deneme" iki karoyu her çevirdiğinizde olur, bu da flippedTiles.length === 2'yi kontrol eden if bloğumuzla eşleşir. Bu koşullu önerme içinde artırabildiğimiz yeni bir global değişkeni, numTries, ekleyebiliriz.
if (flippedTiles.length === 2) {
numTries++;
// ...
}
Oyun bittiğinde - oyuncu karoların hepsini eşleştirdiğinde - puanı gösterebiliriz. Bunu nasıl kontrol ederiz? Şimdi tüm karolarımızın bir dizisi var ve her birinin eşleşme olup olmadığını belirten bir boole değeri var; böylece bunda döngü yapabiliriz ve bunların hepsinin doğru olup olmadığını kontrol edebiliriz. Bunu bu kodun içinde başarmanın güzel bir yolu, döngüye girmeden önce başlangıç boole değerini doğruya eşitlemek ve döngüdeki her öğeyle VElemektir. Hiç yanlış görmediği (ve dizi 0 ögeden uzun olduğu!) sürece, sonunda yine doğru olacaktır. Bu şöyle görünür:
var foundAllMatches = true;
    for (var i = 0; i < tiles.length; i++) {
        foundAllMatches = foundAllMatches && tiles[i].isMatch;
    }
Eşleşmelerin hepsini bulduğunda, kullanıcıya deneme sayısıyla birlikte bir tebrik metni gösterebiliriz:
if (foundAllMatches) {
    fill(0, 0, 0);
    text("You found them all in " + numTries + " tries", 20, 360);
}
Bu arada, bu kodun hepsini mouseClicked fonksiyonunun sonuna koyalım ki, bu eldeki eşleşmeyi kontrol ettikten sonra yürütelim.
Bunu aşağıda deneyebilirsiniz, ama kazanma durumuna gelmeniz biraz süre alabilir (tabii ki, darılmak yok, ben de hemen yapamıyorum!). Oyununuzda ulaşılması zor kısımları test etmeniz için size bir ipucu: oraya daha kolay ulaşacak şekilde, oyunu geçici olarak değiştirin. Örneğin, bu oyunda NUM_ROWS ve NUM_COLS'u daha küçük sayılarla değiştirirseniz çok daha kısa sürede bitirebilirsiniz. Şimdi, bunu aşağıda deneyin!
Yükleniyor