Estrutura de Dados Bidimensional (Matriz)


As matrizes expandem o conceito de vetores, que são estruturas unidimensionais (como uma lista). Uma matriz funciona como uma "tabela" bidimensional, com linhas e colunas, sendo ideal para representar problemas que dependem de uma disposição em duas dimensões, como coordenadas em um mapa, peças em um tabuleiro ou até mesmo os pixels de uma imagem.

1. Declaração e Criação de Matrizes

Enquanto um vetor precisa de apenas um índice para acessar uma posição, uma matriz necessita de dois: um para a linha e outro para a coluna. A indexação também começa em 0, então a primeira posição de uma matriz é (0,0). A principal diferença na declaração em C# é o uso de uma vírgula dentro dos colchetes ([,]) para indicar as duas dimensões.

Exemplo de vetor e matriz

1.1 Declaração com atribuição direta

É possível definir os valores da matriz diretamente em sua criação. A formatação com múltiplas linhas ajuda a visualizar a estrutura, mas não é obrigatória.

double[,] notas = { 
    { 8.7, 5.4, 6.2 },
    { 3.8, 2.7, 8.5 },
    { 9.1, 0.9, 4.2 },
    { 7.1, 1.8, 8.8 } 
};

1.2 Acessando e alterando valores

Para acessar ou alterar um valor, você deve sempre informar os dois índices (linha e coluna).

// Altera o valor na primeira linha, segunda coluna
notas[0, 1] = 6.4;

// Copia o valor da última linha e última coluna para uma variável
double nota_do_antonio = notas[3, 2];

1.3 Declaração com tamanho definido

Você pode declarar uma matriz especificando o número de linhas e colunas, para depois preencher os valores.

double[,] notas = new double[4, 3];

1.4 Declaração sem tamanho pré-definido

Também é possível declarar a matriz e inicializá-la em outro ponto do código.

double[,] notas;
notas = new double[4, 3];

2. Resolução de Problemas Utilizando Matrizes

Matrizes são perfeitas para jogos de tabuleiro como xadrez, damas ou, como no exemplo a seguir, Campo Minado.

Exemplo: Lógica do Jogo Campo Minado

Vamos usar uma matriz 10x10 para o campo, onde `0` é uma posição vazia, `1` é uma bomba e `2` é a bandeira. Outra matriz, `jogo`, registrará as ações do jogador.

Criação e inicialização do campo:

int[,] campo = new int[10, 10]; // Matriz com posições dos elementos do campo
int[,] jogo = new int[10, 10];  // Matriz que registra ações do jogador

int qtdLinhas = campo.GetLength(0);
int qtdColunas = campo.GetLength(1);

// Preenche a matriz 'campo' com 0 e a matriz 'jogo' com -1
for (int l = 0; l < qtdLinhas; l++)
{
    for (int c = 0; c < qtdColunas; c++)
    {
        campo[l, c] = 0;
        jogo[l, c] = -1;
    }
}

Para percorrer ou preencher uma matriz, a forma padrão é utilizar duas estruturas for, uma dentro da outra. O laço externo percorre as linhas, e o interno percorre as colunas.

Sequência para percorrer uma matriz

Posicionamento aleatório dos elementos:

// Posicionamento aleatório da bandeira
Random gerador = new Random();
int linha = gerador.Next(qtdLinhas);
int coluna = gerador.Next(qtdColunas);
campo[linha, coluna] = 2;

// Posicionamento aleatório das 5 bombas
int bombasPosicionadas = 0;
do
{
    linha = gerador.Next(qtdLinhas);
    coluna = gerador.Next(qtdColunas);
    if (campo[linha, coluna] == 0)
    {
        campo[linha, coluna] = 1;
        bombasPosicionadas++;
    }
} while (bombasPosicionadas < 5);

Loop principal de interação com o jogador:

bool fimJogo = false;
do
{
    // Imprime o tabuleiro do jogador
    for (int l = 0; l < qtdLinhas; l++)
    {
        for (int c = 0; c < qtdColunas; c++)
        {
            Console.Write(string.Format("{0} ", jogo[l, c]));
        }
        Console.Write(Environment.NewLine + Environment.NewLine);
    }

    Console.Write("Selecione uma linha [1-10]: ");
    linha = Convert.ToInt32(Console.ReadLine()) - 1;
    Console.Write("Selecione uma coluna [1-10]: ");
    coluna = Convert.ToInt32(Console.ReadLine()) - 1;

    switch (campo[linha, coluna])
    {
        case 0:
            jogo[linha, coluna] = 0;
            Console.Write("Continue tentando.\n\n");
            break;
        case 1:
            jogo[linha, coluna] = 1;
            Console.Write("BOOOM. Você perdeu.\n\n");
            fimJogo = true;
            break;
        default:
            jogo[linha, coluna] = 2;
            Console.Write("Parabéns. Você ganhou!\n\n");
            fimJogo = true;
            break;
    }
} while (!fimJogo);

Interação com o campo minado

3. Utilização de Funções com Matrizes

Matrizes podem ser passadas como argumentos para funções, assim como os vetores. Uma observação importante é que matrizes (e vetores) são passadas por referência. Isso significa que qualquer alteração feita na matriz dentro da função se refletirá na matriz original, fora da função.

Exemplo: Lógica do Jogo da Velha

Código principal do jogo:

char[,] tabuleiro = new char[3, 3];
int linha, coluna;
bool fimJogo = false;
int jogador = 1;
int jogada = 0;

// Preenchimento da matriz com espaços em branco
for (int l = 0; l < 3; l++)
    for (int c = 0; c < 3; c++)
        tabuleiro[l, c] = ' ';

do
{
    imprimirTabuleiro(tabuleiro);
    if (jogador == 1)
        Console.Write("JOGADOR 1:\n");
    else
        Console.Write("JOGADOR 2:\n");

    Console.Write("Selecione uma linha [1-3]: ");
    linha = Convert.ToInt32(Console.ReadLine()) - 1;
    Console.Write("Selecione uma coluna [1-3]: ");
    coluna = Convert.ToInt32(Console.ReadLine()) - 1;
    
    jogada++;
    fimJogo = conferirJogada(tabuleiro, linha, coluna, jogador, jogada);

    // Troca de jogador
    if (jogador == 1)
        jogador = 2;
    else
        jogador = 1;
} while (!fimJogo);

Função imprimirTabuleiro

Esta função apenas lê a matriz e a exibe no console com formatação.

static void imprimirTabuleiro(char[,] tabuleiro)
{
    for (int l = 0; l < 3; l++)
    {
        for (int c = 0; c < 3; c++)
        {
            Console.Write(string.Format("{0}", tabuleiro[l, c]));
            if (c < 2)
                Console.Write("|");
        }
        Console.Write(Environment.NewLine);
        if (l < 2)
            Console.Write("-----\n");
    }
}

Função conferirJogada

Esta função recebe a jogada, altera o tabuleiro (marcando 'X' ou 'O') e verifica se houve um vencedor ou empate.

static bool conferirJogada(char[,] tabuleiro, int linha, int coluna, int jogador, int jogada)
{
    bool trinca = false;
    if (jogador == 1)
        tabuleiro[linha, coluna] = 'X';
    else
        tabuleiro[linha, coluna] = 'O';

    // Verificar na mesma linha
    for (int c = 0; c < 3; c++)
    {
        if (tabuleiro[linha, c] != tabuleiro[linha, coluna])
            break;
        if (c == 2)
            trinca = true;
    }

    // Verificar na mesma coluna
    if (!trinca)
    {
        for (int l = 0; l < 3; l++)
        {
            if (tabuleiro[l, coluna] != tabuleiro[linha, coluna])
                break;
            if (l == 2)
                trinca = true;
        }
    }

    // Verificar na diagonal principal
    if (!trinca)
    {
        if (linha == coluna)
        {
            for (int cont = 0; cont < 3; cont++)
            {
                if (tabuleiro[cont, cont] != tabuleiro[linha, coluna])
                    break;
                if (cont == 2)
                    trinca = true;
            }
        }
    }

    // Verificar na diagonal secundária
    if (!trinca)
    {
        if (linha + coluna == 2)
        {
            for (int cont = 0; cont < 3; cont++)
            {
                if (tabuleiro[cont, 2 - cont] != tabuleiro[linha, coluna])
                    break;
                if (cont == 2)
                    trinca = true;
            }
        }
    }

    if (trinca)
    {
        Console.WriteLine();
        imprimirTabuleiro(tabuleiro);
        Console.Write("JOGADOR " + jogador + " VENCEU!\n\n");
        return true;
    }

    if (jogada == 9)
    {
        Console.WriteLine();
        imprimirTabuleiro(tabuleiro);
        Console.Write("EMPATE!\n\n");
        return true;
    }
    else
    {
        Console.Write("\nPRÓXIMO JOGADOR...\n\n");
        return false;
    }
}

Interação com o jogo da velha

Veja o seguinte conteúdo para melhor entendimento sobre matrizes multidimensionais: link


Considerações Finais