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

Ако си зад уеб филтър, моля, увери се, че домейните *. kastatic.org и *. kasandbox.org са разрешени.

Основно съдържание

Игра "Памет": Рисуване на мрежа от плочки

Първата стъпка от играта на "Памет" е да разбъркаме на случаен принцип всички плочки и след това да ги поставим в правоъгълна решетка с лицето надолу, така че да не можем да видим кое изображение е от другата страна плочката.

Плочки с лицето надолу

За да започнем да програмираме играта, нека се съсредоточим върху създаването на плочки с лицето надолу, и по-късно да измислим как да направим различните изображения.
Плочката е достатъчно важен обект в играта "Памет", за да използваме принципите на обектно-ориентираното програмиране, за да дефинираме тип Tile, а след това да създадем няколко инстанции от него. След това ще можем да асоциираме с него свойства (като разположение и име) и поведение (обръщане с лицето нагоре или надолу) за всяка плочка Tile.
За начало ще дефинираме функцията конструктор на Tile. Тъй като все още не работим с изображения, ще подадем само аргументите x и y. Освен това ще запазим широчината на плочката (която е константа) в свойство на обекта.
var Tile = function(x, y) {
  this.x = x;
  this.y = y;
  this.width = 50;
};
След като дефинирахме конструктора, можем да използваме цикъл, за да създадем плочките с подходящи позиции x и y. Всъщност ще използваме два for цикъла – вложен for цикъл – тъй като така ще е по-лесно да генерираме координатите за мрежа.
Първо трябва да декларираме празен tiles масив, в който да запазим всички тези плочки:
var tiles = [];
Външният ни цикъл итерира през колкото колони искаме, вътрешният цикъл итерира през всеки от редовете и всяка нова плочка Tile се инициализира с x и y, които отговарят на тези ред и колона.
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));
  }
}
Но така е трудно да разберем дали плочките ще изглеждат добре, защото все още нямаме код, който да ги нарисува! Всъщност може би трябваше да направим първо това. Понякога в програмирането е трудно да знаем какво трябва да направим по-напред, нали? Сега да добавим метод към обекта Tile, който рисува на платното плочка с лицето надолу. Ще нарисуваме закръглен правоъгълник с листото на Кан Академия на зададената позиция.
Tile.prototype.draw = function() {
  fill(214, 247, 202);
  strokeWeight(2);
  rect(this.x, this.y, this.width, this.width, 10);
  image(getImage("avatars/leaf-green"),
        this.x, this.y, this.width, this.width);
};
Толкова сме близо до това да проверим как изглеждат нашите плочки! Нека добавим нов for цикъл, който обхожда всички плочки и извиква метода за чертане draw върху тях:
for (var i = 0; i < tiles.length; i++) {
    tiles[i].draw();
}
Ето как изглежда нашата програма с всичкия код. Опитай да си поиграеш с различните числа във вложения цикъл, за да провериш как ще се промени мрежата или начинът, по който са нарисувани (може би различно лого?).

Плочки с лицето нагоре

Сега, когато имаме мрежа от плочки с лицето надолу, нека да разгледаме един по-труден проблем: трябва да зададем на всяка плочка изображение, такова че в масива да има по 2 изображения от всяко, разпределени на случаен принцип. Вероятно има много начини, по които можем да направим това, но аз предлагам този:
  1. Създаваме масив от възможните изображения, като използваме функцията getImage, за да избираме изображение от библиотеката.
  2. За лицата на нашите 20 плочки ще ни трябват само 10 изображения, затова създаваме масив, който съдържа по 2 копия от 10 случайно избрани изображения от първия масив.
  3. Разбъркваме избраните изображения в масива, за да може двойките изображения да не стоят едно до друго в масива.
  4. Във вложения for цикъл, в който създаваме плочките, ще присвоим изображение от масива на всяка плочка.
Така описани тези стъпки може и да нямат смисъл – нека ги направим една по една, за да ги разберем.
Стъпка 1: Създаваме масив от възможните изображения, като използваме функцията getImage, за да изберем изображения от нашата библиотека:
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")
];
Избрах няколко аватара, но ти можеш да избереш каквито изображения пожелаеш. Важното е да се увериш, че в масива има поне 10 изображения за нашите 20 плочки. Можем да добавим много повече от 10 изображения, за да може играта ни да бъде по-разнообразна всеки път, когато я играем, тъй като в следващата стъпка ще стесним списъка.
Стъпка 2: Ще ни трябват само 10 изображения за лицата на нашите 20 плочки, затова можем да създадем нов масив, който държи по 2 копия от 10 изображения, случайно избрани от първия масив.
За да го направим, създаваме for цикъл, който итерира 10 пъти. Във всяка итерация избираме случаен индекс от масива faces, подаваме го 2 пъти в масива selected, а след това използваме метода splice, за да ги извадим от масива faces, за да не ги изберем отново. Тази стъпка е много важна!
var selected = [];
for (var i = 0; i < 10; i++) {
    // Избираме случайно изображение от масива
    var randomInd = floor(random(faces.length));
    var face = faces[randomInd];
    // Подаваме 2 копия в масива
    selected.push(face);
    selected.push(face);
    // Премахваме изображенията от масива с лицата, за да не ги изберем отново
    faces.splice(randomInd, 1);
}
Стъпка 3: Разбъркваме избраните изображения в масива, за да може двойките изображения да не стоят едно до друго.
Сигурно ти се е случвало да разбъркваш карти, но едва ли е ставало в JavaScript. Най-популярният начин да разбъркаш нещо във всеки програмен език се нарича разбъркване на Фишер Йейтс и него ще използваме ето тук.
Фишер-Йейтс разбъркването започва като първо избере произволен елемент от масива и го размени с последния елемент в масива. В следващата стъпка избира произволен елемент от масива освен последния елемент и го разменя с предпоследния елемент. Разбъркването продължава, докато не размени всички елементи.
Можеш да си поиграеш с тази визуализация, за да видиш какво имам предвид
За да имплементираме това в JavaScript, нека създадем функция shuffleArray, която приема масив, разбърква елементите му и го променя:
var shuffleArray = function(array) {
    var counter = array.length;

    // Докато има елементи в масива
    while (counter > 0) {
        // Избери произволен индекс
        var ind = Math.floor(Math.random() * counter);
        // Намали брояча с 1
        counter--;
        // И размени последния елемент с него
        var temp = array[counter];
        array[counter] = array[ind];
        array[ind] = temp;
    }
};
Ако този алгоритъм все още не ти е ясен, след като разгледаш визуализацията и прочетеш кода, можеш да опиташ с истинска колода карти или да видиш как Адам Коури обяснява как се прави в своето видео в YouTube.
След като дефинираме функцията, трябва също и да я извикаме:
shuffleArray(selected);
И сега имаме масив от 10 двойки произволно подредени изображения!
Стъпка 4: Във вложения for цикъл, в който създаваме плочките, ще зададем изображение от масива на всяка плочка.
В нашия масив selected имаме 20 изображения, през които итерираме 20 пъти, за да инстанцираме нови плочки на позиции в мрежата. За да зададем всяко изображение на плочка, можем да извикаме метода pop на масива. Това метод премахва последния елемент от масива и го връща, а това е най-лесният начин да сме сигурни, че задаваме всички изображения, без да ги повтаряме.
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);
  }
}
Забелязваш ли как този код подава tileFace като трети параметър на конструктора Tile? Нашият конструктор първоначално имаше 2 параметъра, x и y, но сега го модифицираме така, че да можем също да запомним и изображението на лицето на всяка плочка, както и дали е с лицето нагоре или не:
var Tile = function(x, y, face) { this.x = x; this.y = y; this.width = 70; this.face = face; this.isFaceUp = false; };
Така теоретично имаме изображения, присвоени на всяка плочка, но които все още не показваме! Нека променим метода Tile.draw, така че да чертае плочки, които са с лицето нагоре:
Tile.prototype.draw = function() {
    fill(214, 247, 202);
    strokeWeight(2);
    rect(this.x, this.y, this.width, this.width, 10);
    if (this.isFaceUp) {
        image(this.face, this.x, this.y,
              this.width, this.width);
    } else {
        image(getImage("avatars/leaf-green"),
              this.x, this.y, this.width, this.width);
    }
};
Накрая, за да проверим дали всичко работи, можем да променим своя for цикъл и да променим стойността на свойството isFaceUp на всяка плочка на true преди да я начертаем:
for (var i = 0; i < tiles.length; i++) {
  tiles[i].isFaceUp = true;
  tiles[i].draw();
}
Ето това е целият код. Опитай да рестартираш, за да видиш как плочките се променят всеки път.

Искаш ли да се присъединиш към разговора?

Все още няма публикации.
Разбираш ли английски? Натисни тук, за да видиш още дискусии в английския сайт на Кан Академия.