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

Se você está atrás de um filtro da Web, certifique-se que os domínios *.kastatic.org e *.kasandbox.org estão desbloqueados.

Conteúdo principal

Tabuleiro de peças

A primeira etapa para jogar o jogo de "Memória" é embaralhar aleatoriamente todas as peças e então colocá-las arrumadas em uma grade retangular, viradas para baixo, para que não possamos ver qual a imagem está do outro lado de cada peça.

Peças com a face para baixo

Para começar a programar o jogo, vamos nos preocupar apenas em criar peças com a face voltada para baixo, e deixar pra pensar em como faremos as imagens diferentes mais tarde.
A "peça" é um objeto tão importante do "Jogo da Memória" que usaremos princípios de orientação a objetos para definir um objeto do tipo Tile (peça, em inglês) e criaremos várias instâncias dele. Depois poderemos associar tanto propriedades (como localização e imagem) como comportamento (como virar as peças com a face voltada para baixo ou para cima) a cada um dos objetos Tile.
Para começar, iremos definir a função do construtor Tile. Como ainda não estamos lidando com imagens, iremos apenas passar os argumentos x e y a ele. Também vamos lembrar a largura (width) da peça, um argumento constante, em uma propriedade do objeto.
var Tile = function(x, y) {
    this.x = x;
    this.y = y;
    this.width = 70;
};
Agora que definimos o construtor, podemos usá-lo em um laço para criar peças com as posições x e y apropriadas. Na verdade, vamos usar dois laços for – um laço for aninhado – já que o conceito facilita a geração de coordenadas para uma grade.
Vamos começar criando um array vazio tiles:
var tiles = [];
Nosso laço for externo itera em quantas colunas quisermos, enquanto nosso for interno itera para cada uma das linhas, e cada novo objeto de tipo Tile é inicializado com valores de x e y que correspondam à linha e à coluna.
var NUM_COLS = 5;
var NUM_ROWS = 4;
for (var i = 0; i < NUM_COLS; i++) {
    for (var j = 0; j < NUM_ROWS; j++) {
        tiles.push(new Tile(i * 78 + 10, j * 78 + 40));
    }
}
Ah, mas é difícil saber se as peças vão ficar legais, porque ainda não temos um código para desenhá-las! Na verdade, talvez devêssemos ter feito isso primeiro. Algumas vezes, na programação, é difícil saber o que fazer primeiro, não é verdade? Agora vamos adicionar um método para o objeto Tile que desenha uma peça com a face para baixo na tela. Vamos desenhar um retângulo arredondado com uma bela folha da Khan Academy na parte superior, na posição determinada.
Tile.prototype.drawFaceDown = 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);
};
E agora podemos ver como nossas peças ficaram. Vamos adicionar um novo laço for que itere por todas as peças e chame o método de desenho nelas:
for (var i = 0; i < tiles.length; i++) {
    tiles[i].drawFaceDown();
}
É assim que nosso programa se parece, com toda a programação feita até agora. Tente colocar números diferentes nas estruturas aninhadas para ver como a grade muda, ou tente mudar a maneira de desenhar as peças (um logotipo diferente, talvez?).

Peças com a face para cima

Agora que temos uma grade de peças com as faces voltadas para baixo, vamos tratar de um problema mais complicado: dar a cada uma delas uma imagem, de forma que haja 2 peças de cada imagem no array, distribuídas de forma aleatória. Existem muitas formas de fazer isso, mas eis o que eu sugeriria:
  1. Criamos um array com as imagens possíveis, usando a função getImage para pegá-las na nossa biblioteca.
  2. Vamos precisar de apenas 10 imagens para nossas 20 peças, então criamos um novo array que armazene 2 cópias de 10 imagens do primeiro array selecionadas aleatoriamente.
  3. Embaralhamos o array com as imagens selecionadas, assim os pares de imagens não ficarão em posições próximas no array.
  4. Na estrutura for aninhada, onde criamos as peças, vamos associar uma imagem do array a cada peça.
Esses passos podem não fazer sentido ainda – vamos implementar cada um deles e ver como fica.
Etapa 1: Criamos um array com as imagens possíveis, usando a função getImage para pegá-las na nossa biblioteca:
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")
];
Eu peguei algumas imagens, mas você pode escolher suas imagens favoritas. O importante é que este array tenha pelo menos 10 imagens, assim não faltarão imagens para nossas 20 peças. Contudo, podemos colocar muito mais que 10 imagens, para dar ao nosso jogo uma maior variedade a cada vez que ele for jogado, porque vamos limitar a lista na próxima etapa.
Etapa 2: Vamos precisar de apenas 10 imagens para nossas 20 peças, então criamos um novo array que armazene 2 cópias de 10 imagens selecionadas aleatoriamente do primeiro array.
Para isso, vamos criar um laço for que itere 10 vezes. Em cada iteração, pegamos aleatoriamente um índice do array faces, o inserimos duas vezes no array selected, e então usamos o método splice para removê-los do array faces, assim não selecionaremos o mesmo índice duas vezes. Esse último passo é muito importante!
var selected = [];
for (var i = 0; i < 10; i++) {
    // Escolhe um aleatoriamente do array de faces
    var randomInd = floor(random(faces.length));
    var face = faces[randomInd];
    // Insere 2 cópias no array
    selected.push(face);
    selected.push(face);
    // Remove do array faces para não escolhermos o mesmo
    faces.splice(randomInd, 1);
}
Etapa 3: Embaralhamos o array com as imagens selecionadas, assim os pares de imagens não ficarão em posições próximas no array.
Você deve estar se perguntando: como embaralhar um array em JavaScript? Existem algumas técnicas, mas vou mostrar a minha preferida.
Em JavaScript, todo objeto array possui um método sort que irá ordenar o array "lexicograficamente". Isso significa que ele irá converter cada item em uma string, ordenando com se fossem palavras em um dicionário. Por exemplo, vamos ver como ele ordena um array de números e letras.
var items = ["A", 1, "C", "H", 10, "D", 2];
items.sort();
1,10,2,A,C,D,H
Esse tipo de ordenação pode às vezes ser útil, mas na maioria das vezes queremos ordenar nosso array de outra forma. Por exemplo: se tivéssemos um array de números, poderíamos querer ordená-lo numericamente. É por isso que o método sort opcionalmente aceita um argumento, uma função callback que será chamada em cada par de itens de um array, e retornar um valor para indicar se ele deve ser classificado como maior que o outro. Um número negativo significa que o primeiro item deve ser o primeiro, um número positivo indica que o segundo item deve ser o primeiro, e zero fará com que a ordem dos itens não mude.
Para ordenar um array numericamente do menor para o maior, podemos passar uma função que retorna a-b.
var nums = [1, 5, 10, 2, 4];
nums.sort(function(a, b) {
   return  a-b;
});
// 1,2,4,5,10
OK. Então como nós usamos isso para ordenar um array aleatoriamente? Bem, nós precisamos apenas retornar um número aleatório desta função, um número que seja positivo ou negativo. Abaixo está como podemos fazer isso, em nosso array selected criado anteriormente.
selected.sort(function() {
    return 0.5 - random();
});
E agora temos um array de 10 pares de imagens, ordenado aleatoriamente!
Etapa 4: No laço aninhado, onde criamos as peças, vamos associar uma imagem do array para cada peça.
Nós temos 20 imagens no nosso array selected, e estamos iterando 20 vezes para instanciar novas peças em locais na grade. Para atribuir cada imagem a uma peça, podemos usar o método pop no array. Esse método remove o último elemento do array e o retorna, é o jeito mais fácil de termos certeza que todas as imagens foram atribuídas sem que elas sejam duplicadas.
for (var i = 0; i < NUM_COLS; i++) {
    for (var j = 0; j < NUM_ROWS; j++) {
        tiles.push(new Tile(i * 78 + 10, j * 78 + 40, selected.pop()));
    }
}
Agora temos, teoricamente, imagens atribuídas para cada peça, mas não estamos exibindo-as ainda! Vamos adicionar um método para o objeto Tile que será responsável por desenhar as faces viradas para cima. Será similar ao de desenhá-las viradas para baixo exceto que a propriedade this.face é passada para a função image.
Tile.prototype.drawFaceUp = function() {
    fill(214, 247, 202);
    strokeWeight(2);
    rect(this.x, this.y, this.width, this.width, 10);
    image(this.face, this.x, this.y, this.width, this.width);
};
Para minimizar a repetição de código, poderíamos colocar um método adicional responsável apenas por desenhar o contorno da peça, e então chamar esse método dentro de cada um dos métodos de desenho. Mas vamos deixar como está por enquanto.
Finalmente, para testar se tudo funciona, podemos mudar nosso for para chamar a função drawFaceUp ao invés de drawFaceDown:
for (var i = 0; i < tiles.length; i++) {
    tiles[i].drawFaceUp();
}
Juntando tudo, aqui está nosso programa. Experimente reiniciá-lo para ver como as peças mudam a cada jogo.

Quer participar da conversa?

Você entende inglês? Clique aqui para ver mais debates na versão em inglês do site da Khan Academy.