If you're seeing this message, it means we're having trouble loading external resources on our website.

Ha webszűrőt használsz, győződj meg róla, hogy a *.kastatic.org és a *.kasandbox.org nincsenek blokkolva.

Fő tartalom

Memóriajáték: Rács rajzolása

A „Memória” játék első lépése, hogy véletlenszerűen összekeverjük a kártyákat, és fejjel lefele elrendezzük egy téglalap alakú rácson, így nem láthatjuk a képeket a kártyák másik oldalán.

Lefordított kártyák

Kezdjük azzal a játék programozását, hogy lefele fordított kártyákat készítünk. Majd később találjuk ki, hogyan csináljuk meg a különböző képeket.
A „kártya” van annyira fontos objektum a „Memória” játékban, hogy objektum-orientált elveket használva definiáljunk egy Tile objektumot, és aztán létrehozzuk ennek több példányát. Így képesek leszünk minden egyes kártyánál (Tile) összekapcsolni a tulajdonságokat (például a helyzetet, és a képet), és a viselkedést is (például a megrajzolást).
Kezdjük azzal, hogy definiáljuk a Tile konstruktor függvényt. Mivel a képpel most még nem foglalkozunk, csak az x és y argumentumokat adjuk át. Ne felejtsük el a kártya (konstans) méretét (size) sem, ami szintén az objektum tulajdonsága.
var Tile = function(x, y) {
  this.x = x;
  this.y = y;
  this.size = 50;
};
Most, hogy definiáltuk a konstruktort, használhatjuk ezt egy ciklusban, amelyben elkészítjük a kártyákat a megfelelő x és y helyzetekben. Valójában két for ciklust fogunk használni – egymásba ágyazott for ciklusokat – ami alapjaiban leegyszerűsíti a rács koordinátáinak generálását.
Először deklarálunk egy üres tiles tömböt, amiben eltároljuk az összes kártyát:
var tiles = [];
A külső ciklusunk addig iterál, amennyi oszlopot csak szeretnénk, a belső ciklusunk pedig addig, amennyi sort, és minden új Tile-t inicializálunk azzal az x és y értékkel, ami a neki megfelelő sort illetve oszlopot határozza meg.
var NUM_COLS = 5;
var NUM_ROWS = 4;
for (var i = 0; i < NUM_COLS; i++) {
  for (var j = 0; j < NUM_ROWS; j++) {
    var tileX = i * 54 + 5;
    var tileY = j * 54 + 40;
    tiles.push(new Tile(tileX, tileY));
  }
}
De, jajj, nehéz eldönteni, hogy a kártyák jók-e, mert nincs még kódunk, ami megrajzolja őket! Valójában ezzel kellett volna kezdenünk. A programozásban néha nehéz előre kitalálni, hogy mivel is kezdjük, ugye? Adjunk hozzá egy metódust a Tile objektumhoz, amely megrajzolja a kártyát a vásznon lefordítva. Egy lekerekített téglalapot fogunk rajzolni a megadott helyre egy jópofa Khan levéllel a tetején.
Tile.prototype.draw = function() {
  fill(214, 247, 202);
  strokeWeight(2);
  rect(this.x, this.y, this.size, this.size, 10);
  image(getImage("avatars/leaf-green"),
        this.x, this.y, this.size, this.size);
};
Most már nagyon közel vagyunk ahhoz, hogy meg tudjuk nézni, hogy festenek a kártyáink! Adjunk hozzá egy új for ciklust, ami végigjárja az összes kártyát és meghívja a kirajzoló metódusukat:
for (var i = 0; i < tiles.length; i++) {
    tiles[i].draw();
}
Így fest a programunk az eddigi kódokkal. Módosítgasd a számokat az egymásba ágyazott for ciklusban, hogy lásd, hogyan változik a rács, vagy változtasd meg a kirajzolást (például egy másik logót használva)!

Felfordított kártyák

Most, hogy elkészültünk a lefordított kártyákkal, foglalkozzunk kicsit a trükkösebb feladattal: rendeljünk mindegyik kártyához egy képet úgy, hogy minden képből kettő szerepeljen a tömbben, véletlenszerűen elosztva. Sokféle módon meg tudnánk ezt oldani, de a következőt javasoljuk:
  1. Készítsünk egy tömböt a lehetséges képekkel, a képeket a függvénykönyvtárunkból kiválasztva a getImage függvény segítségével.
  2. Csak 10 képre lesz szükségünk a 20 kártyához, ezért készítsünk egy új tömböt, amely az első tömbben tárolt véletlenszerűen kiválasztott 10 kép 2-2 másolatát tárolja
  3. Keverjük össze a kiválasztott képek tömbjét, így a képpárok már nem egymás mellett fognak elhelyezkedni a tömbben.
  4. Az egymásba ágyazott for ciklusban, ahol a kártyákat elkészítjük, rendeljük hozzá a képeket a tömbből minden egyes kártyához.
Lehet, hogy ezek a lépések még nem érthetőek – készítsük elő mindent egyesével, és nézzük meg, hogy néznek ki.
1. lépés: Készítsünk egy tömböt a lehetséges képekkel, a képeket a függvénykönyvtárunkból kiválasztva a getImage függvény segítségével:
var faces = [
    getImage("avatars/leafers-seed"),
    getImage("avatars/leafers-seedling"),
    getImage("avatars/leafers-sapling"),
    getImage("avatars/leafers-tree"),
    getImage("avatars/leafers-ultimate"),
    getImage("avatars/marcimus"),
    getImage("avatars/mr-pants"),
    getImage("avatars/mr-pink"),
    getImage("avatars/old-spice-man"),
    getImage("avatars/robot_female_1"),
    getImage("avatars/piceratops-tree"),
    getImage("avatars/orange-juice-squid")
];
Csomó avatárt választottunk ki, de nyugodtan megváltoztathatod a képeket a kedvenc képeidre. Az fontos, hogy figyelj arra, hogy legalább 10 kép legyen a tömbben, így lesz elég kép a 20 kártyánkhoz. Akár 10-nél sokkal több képet is hozzáadhatunk, így a játékunk változatosabb lesz, mert a tömböt csak a következő lépésben fogjuk leszűkíteni.
2. lépés: Csak 10 képre lesz szükségünk a 20 kártyához, ezért készítsünk egy új tömböt, ami az első tömbben tárolt véletlenszerűen kiválasztott 10 kép 2-2 másolatát tárolja.
Ahhoz, hogy ezt megcsináljuk, készítünk egy for ciklust, ami 10 alkalommal iterál. Minden iterációban véletlenszerűen kiválasztunk egy indexet a faces tömbből, ezt kétszer elhelyezzük a selected tömbben, majd a splice metódust használva eltávolítjuk a faces tömbből, hogy ne válasszuk ki már többször. Az utolsó lépés nagyon fontos!
var selected = [];
for (var i = 0; i < 10; i++) {
    // Randomly pick one from the array of faces
    var randomInd = floor(random(faces.length));
    var face = faces[randomInd];
    // Push 2 copies onto array
    selected.push(face);
    selected.push(face);
    // Remove from faces array so we don't re-pick
    faces.splice(randomInd, 1);
}
3. lépés: Keverjük össze a kiválasztott képek tömbjét, így a képpárok már nem egymás mellett fognak elhelyezkedni a tömbben.
Valószínűleg már kevertél kártyapaklit a való életben kártyapaklit, de vajon tömböt kevertél már JavaScriptben? A legnépszerűbb keverési technika bármelyik programozási nyelvben a Fisher-Yates keverés, amit mi is használni fogunk.
A Fisher-Yates keverés úgy kezdődik, hogy elsőként kiválasztunk egy véletlen elemet bárhol a tömbből, és kicseréljük ezt a tömb utolsó elemével. A következő lépésben kiválasztunk egy véletlen elemet bárhol a tömbből az utolsó elemet kivéve, és kicseréljük ezt az utolsó előtti elemmel. Ezt így folytatjuk addig, amíg minden elemet ki nem cseréltünk.
Végigkattintgathatod az alábbi vizualizációt, hogy megértsd mire gondolok:
Ahhoz, hogy ezt JavaScriptben implementáljuk, készítsünk egy shuffleArray függvényt, ami vesz egy tömböt, és összekeveri az elemeit, megváltoztatva az eredeti tömböt:
var shuffleArray = function(array) {
    var counter = array.length;

    // While there are elements in the array
    while (counter > 0) {
        // Pick a random index
        var ind = Math.floor(Math.random() * counter);
        // Decrease counter by 1
        counter--;
        // And swap the last element with it
        var temp = array[counter];
        array[counter] = array[ind];
        array[ind] = temp;
    }
};
Ha az algoritmus nem érthető teljesen a vizualizáción végighaladva és a kódot elolvasva, próbáld ki egy valódi kártyapaklival vagy nézd meg hogyan csinálja Adam Khoury ebben a Youtube videóban.
Most, hogy már definiáltuk a függvényt, meg is hívjuk:
shuffleArray(selected);
És már van is egy tömbünk 10 pár képpel, véletlenszerűen összekeverve!
4. lépés: Az egymásba ágyazott for ciklusban, ahol a kártyákat elkészítjük, rendeljük hozzá a képeket a tömbből minden egyes kártyához.
A selected tömbünkben van 20 képünk, és végigiterálunk 20 alkalommal, hogy példányosítsuk a kártyáinkat a rács adott pontjaiban. Egy véletlen kép kiválasztásához minden kártyánál meghívhatjuk a pop metódust a tömbre. Ez a metódus eltávolítja az utolsó elemet a tömbből és visszatér vele, és ez a legegyszerűbb módja annak, hogy biztosan minden egyes képet hozzárendeljünk valamelyik kártyához, és ne is legyen dupla hozzárendelés.
for (var i = 0; i < NUM_COLS; i++) {
  for (var j = 0; j < NUM_ROWS; j++) {
    var tileX = i * 54 + 5;
    var tileY = j * 54 + 40;
    var tileFace = selected.pop();
    var tile = new Tile(tileX, tileY, tileFace);
    tiles.push(tile);
  }
}
Észrevetted, hogyan adja át a kód a tileFace-t a Tile konstruktor harmadik paramétereként? A konstruktorunknak eredetileg 2 paramétere volt, x és y, de most módosítjuk, hogy el tudjuk tárolni a képet minden egyes kártyához, illetve azt is eltároljuk, hogy felfordított állapotban van-e:
var Tile = function(x, y, face) {
    this.x = x;
    this.y = y;
    this.size = 70;
    this.face = face;
    this.isFaceUp = false;
};
Most már elméletileg minden képet hozzárendeltük valamelyik kártyához, de még nem rajzoltuk ki őket! Módosítsuk a Tile.draw metódust úgy, hogy kirajzolja a kártyákat, amik felfordított állapotúak:
Tile.prototype.draw = function() {
    fill(214, 247, 202);
    strokeWeight(2);
    rect(this.x, this.y, this.size, this.size, 10);
    if (this.isFaceUp) {
        image(this.face, this.x, this.y,
              this.size, this.size);
    } else {
        image(getImage("avatars/leaf-green"),
              this.x, this.y, this.size, this.size);
    }
};
Végül tesztelhetjük, hogy működik-e, azzal, hogy megváltoztatjuk a for ciklusunkat úgy, hogy minden isFaceUp tulajdonságot true értékre állítunk a kirajzolás előtt:
for (var i = 0; i < tiles.length; i++) {
  tiles[i].isFaceUp = true;
  tiles[i].draw();
}
És itt van az egész együtt. Kezdd újra, hogy lásd, hogyan változnak a kártyák minden alkalommal!

Szeretnél részt venni a beszélgetésben?

Még nincs hozzászólás.
Tudsz angolul? Kattints ide, ha meg szeretnéd nézni, milyen beszélgetések folynak a Khan Academy angol nyelvű oldalán.