Pokud vidíš tuto zprávu, znamená to, že máš problém s načítáním externích zdrojů na našich stránkách.

If you're behind a web filter, please make sure that the domains *.kastatic.org and *.kasandbox.org are unblocked.

Hlavní obsah

Pexeso: Vykreslení čtvercové mřížky

Prvním krokem hry "pexeso" je náhodně zamíchat všechny kartičky a poté je vyskládat v obdélníkovém tvaru, obrázkem dolů, abychom neviděli, který obrázek je na druhé straně každé kartičky.

Kartičky obrázkem dolů

Pojďme začít programovat hru tím, že vytvoříme obrácené kartičky, tedy obrázkem dolů. Kartičky s různými obrázky vyřešíme později.
Kartičky jsou v pexesu tak důležitým objektem, že použijeme objektově-orientované principy pro definování objektu Tile a následně vytvoříme několik jeho instancí. Pak budeme moci spojit jak vlastnosti (jako umístění a obrázek), tak chování (jako je jejich nakreslení) každé kartičky pomocí Tile.
Pro začátek definujeme funkci konstruktoru Tile. Vzhledem k tomu, že zatím nepracujeme s obrázky, předáme mu pouze argumenty x a y. Také si zapamatujeme velikost kartičky (konstantu) ve formě vlastnosti na objektu.
var Tile = function(x, y) {
  this.x = x;
  this.y = y;
  this.size = 50;
};
Nyní, když jsme definovali konstruktor, můžeme jej použít v cyklu pro vytvoření kartiček v odpovídajících souřadnicích x a y. Ve skutečnosti použijeme dva cykly for - vnořené cykly for - protože je pak koncepčně snadné generovat souřadnice pro souřadnicovou síť.
Nejprve je třeba deklarovat prázdné pole tiles pro ukládání všech našich kartiček:
var tiles = [];
Náš vnější cyklus iteruje pro tolik sloupců, kolik chceme, náš vnořený cyklus iteruje pro každý z řádků, a každý nový Tile je inicializován s x a y, které odpovídají danému řádku a sloupci.
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));
  }
}
Ale je těžké posoudit, zda budou kartičky vypadat dobře, když ještě nemáme žádný kód pro jejich nakreslení! Možná bychom to měli udělat úplně jako první. Někdy je v programování těžké určit, co udělat jako první, že? Přidejme nyní metodu k objektu Tile, která na plátno nakreslí obrácenou kartičku. Nakreslíme zaoblený obdélník s pěkným listem Khan Academy navrchu.
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);
};
Už jsme docela blízko k tomu, abychom se mohli přesvědčit o tom, jak vypadají naše kartičky! Přidejme nový cyklus for, který se iteruje přes všechny kartičky a volá jejich metodu pro nakreslení:
for (var i = 0; i < tiles.length; i++) {
    tiles[i].draw();
}
Zde můžeš vidět, jak náš program se vším tím kódem vypadá. Zkus pozměnit různá čísla ve vnořeném cyklu a zjisti, jak se tím změní mřížka, nebo zkus změnit metodu kreslení (možná použít jiné logo?)

Přední strana kartiček

Když už máme vytvořenou řadu obrácených kartiček, pojďme se vypořádat s trochu složitějším problémem: přidělit každé z nich obrázek a to tak, že jsou v poli vždy dva stejné obrázky, které jsou náhodně rozděleny. Určitě existuje mnoho způsobů, jak toho dosáhnout, ale tady je náš návrh:
  1. Vytvoříme pole všech možných obrázků pomocí funkce getImage, obrázky vybereme z naší knihovny.
  2. Budeme potřebovat pouze 10 obrázků pro vyplnění našich 20 kartiček, pak vytvoříme nové pole, které bude obsahovat 2 kopie z 10 náhodně vybraných obrázků z prvního pole.
  3. Zamícháme pole vybraných obrázků tak, aby páry obrázků nebyly v poli vedle sebe.
  4. Ve vnořeném cyklu for, kde vytváříme kartičky, přiřadíme ke každé z kartiček nějaký obrázek z pole.
Tyto kroky ti ještě nemusí dávat smysl - udělejme je postupně a uvidíme, jak nám to půjde.
Krok 1: Vytvoříme pole možných obrázků pomocí funkce getImage, obrázky vybereme z naší knihovny:
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")
];
Vybrali jsem několik náhodných avatarů, ale klidně je můžeš změnit a vybrat si obrázky dle vlastních preferencí. Důležité je ujistit se, aby tvé pole obsahovalo alespoň 10 obrázků, abychom měli dostatek obrázků pro všech 20 kartiček. Můžeme však přidat mnohem více než 10 obrázků, uděláme tak naši hru pokaždé o něco různorodější, protože v dalším kroku seznam zúžíme.
Krok 2: Pro vyplnění našich 20 kartiček budeme potřebovat pouze 10 obrázků, proto vytvoříme nové pole, které bude obsahovat 2 kopie 10 náhodně vybraných obrázků z prvního pole.
Abychom toho dosáhli, vytvoříme cyklus for, který se bude desetkrát iterovat. Při každé iteraci náhodně vybíráme index z pole faces a dvakrát ho předáváme do pole selected, poté použijeme metodu splice pro jeho odstranění z pole faces. To kvůli tomu, abychom jej nevybrali dvakrát. Tento poslední krok je velmi důležitý!
var selected = [];
for (var i = 0; i < 10; i++) {
    // Náhodně vybere jeden obrázek z pole obrázků
    var randomInd = floor(random(faces.length));
    var face = faces[randomInd];
    // Vloží dvě kopie do pole
    selected.push(face);
    selected.push(face);
    // Odstraní jej z pole, aby nebyl vybrán znovu
    faces.splice(randomInd, 1);
}
Krok 3: Pole vybraných obrázků zamícháme tak, aby páry obrázků nebyly v poli vedle sebe.
Pravděpodobně už s mícháním karet nějaké zkušenosti máš, ale víš, jak se provede zamíchání pole v JavaScriptu? Nejpopulárnější technika pro zamíchání v jakémkoli programovacím jazyce je Fisherův-Yatesův algoritmus a to je to, co zde použijeme.
Míchání pomocí Fisherova-Yatesova algoritmu začíná výběrem náhodného prvku kdekoli v poli a záměnou za poslední prvek v poli. V dalším kroku se vybere náhodný prvek odkudkoliv v poli kromě posledního prvku a zamění se za předposlední prvek. Pokračuje se tak dlouho, dokud nebude zaměněn každý prvek.
Můžeš se proklikat touto vizualizací a podívat se, co máme na mysli:
Pro implementování v JavaScriptu vytvoříme funkci shuffleArray, která vezme pole a zamíchá jeho prvky, čímž změní původní pole:
var shuffleArray = function(array) {
    var counter = array.length;

    // Pokud jsou prvky v poli
    while (counter > 0) {
        // Vybere náhodný index
        var ind = Math.floor(Math.random() * counter);
        // Sníží počítadlo o 1
        counter--;
        // A zamění za něj poslední prvek pole
        var temp = array[counter];
        array[counter] = array[ind];
        array[ind] = temp;
    }
};
Pokud ti po ukázce vizualizace a kódu algoritmus pořád ještě nedává smysl, můžeš ho vyzkoušet se skutečným balíčkem karet, nebo se podívat, jak to ve svém Youtube videu dělá Adam Khoury.
Po nadefinování této funkce ji musíme zavolat:
shuffleArray(selected);
A nyní máme pole 10 párů obrázků, které jsou náhodně zamíchány!
Krok 4: Ve vnořeném cyklu for, kde vytváříme kartičky, přiřadíme ke každé z kartiček nějaký obrázek z pole.
V našem poli selected máme 20 obrázků a provádíme celkem dvacet iterací, abychom umístili nové kartičky na pozicích v souřadnicové síti. Chceme-li vybrat náhodný obrázek pro každou kartičku, můžeme na pole zavolat metodu pop. Tato metoda vrací prvek z pole, ze kterého jej současně odstraní, a je tedy pro nás nejsnadnějším způsobem, jak se ujistit, že přiřadíme všechny obrázky, aniž bychom je přiřadili dvakrát.
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);
  }
}
Všimáš si, jak tento kód předává tileFace jako třetí parametr konstruktoru Tile? Náš konstruktor měl původně pouze 2 parametry, x a y, ale nyní jej upravujeme tak, abychom si mohli pamatovat obrázek každé kartičky a také to, zda je obrácena lícem vzhůru:
var Tile = function(x, y, face) {
    this.x = x;
    this.y = y;
    this.size = 70;
    this.face = face;
    this.isFaceUp = false;
};
Teoreticky tedy máme obrázky přiřazené ke každé kartičce, ale zatím je pořád nezobrazujeme! Pojďme upravit metodu Tile.draw, aby mohla nakreslit kartičky, které jsou obráceny lícem vzhůru:
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);
    }
};
Na závěr pojďme otestovat, jak to všechno funguje. Můžeme upravit náš cyklus for tak, aby nastavil vlastnost kartičky isFaceUp na true před jejím nakreslením.
for (var i = 0; i < tiles.length; i++) {
  tiles[i].isFaceUp = true;
  tiles[i].draw();
}
Zde je vše dohromady. Zkus program restartovat a uvidíš, jak se kartičky pokaždé změní.

Chceš se zapojit do diskuze?

Zatím žádné příspěvky.
Umíš anglicky? Kliknutím zobrazíš diskuzi anglické verze Khan Academy.