JOGO DE XADREZ EM DELPHI: CAPÍTULO 6 - MOVIMENTOS DO REI, XEQUE, XEQUE-MATE E EMPATE

Adicionado em 15/12/2023

Compartilhar em:

Sejam bem-vindos ao sexto capítulo do nosso tutorial sobre como criar um tabuleiro de xadrez usando Delphi. Neste capítulo, iremos explorar o movimento do rei no contexto do nosso jogo de xadrez desenvolvido em Delphi. Embora o movimento do rei seja bastante simples, permitindo que ele se desloque uma casa em qualquer direção, a complexidade surge devido às várias regras que regem o término do jogo. Neste capítulo, abordaremos os seguintes tópicos:

  • Movimento básico do rei.
  • Movimento do roque e suas múltiplas verificações.
  • Verificações de xeque e xeque-mate.
  • Verificações de empate.

Movimento básico do Rei

Explore o mundo da programação com nosso site dedicado a programadores iniciantes e avançados. Encontre tutoriais detalhados e exemplos práticos nas linguagens Pascal, Delphi, JavaScript e Python. Aprenda a programar de forma clara e eficaz, ideal para quem está começando ou deseja aprimorar suas habilidades

Assim como fizemos para as outras peças, vamos iniciar declarando a função responsável por verificar e validar o movimento do rei:

Pressione a combinação de teclas Ctrl + Shift + C para declarar a função. Vamos começar a nova função definindo inicialmente a resposta como negativa (Result := False), ou seja, consideraremos inicialmente o movimento do rei como inválido. Além disso, adicionaremos as variáveis que rastreiam quantas colunas e linhas o rei está tentando percorrer, denominadas DifCol e DifLin. Também declararemos uma variável do tipo Integer chamada I, que será utilizada para validar certos movimentos do rei. Por fim, criaremos a variável ReiCor para determinar a cor do rei com o qual estamos lidando e a variável Movimento do tipo TMovimento, que trabalhará com registros das jogadas.

Assim como fizemos para as outras peças, a primeira verificação consiste em determinar se o rei não está tentando mover-se para uma casa ocupada por uma peça da mesma cor. Se essa condição for satisfeita, a função é encerrada. Em seguida, calculamos a variação na posição da coluna e da linha e armazenamos esses valores nas variáveis DifCol e DifLin.

Após esse cálculo, adicionamos uma condicional que verifica se a variação da coluna ou da linha é igual ou menor que 1. Nesse caso, o resultado da função será positivo. É importante notar que usamos "menor ou igual a 1" em vez de "igual a 1" porque esse cálculo permite que o rei se mova em qualquer direção. Se houver variação na coluna, mas não na linha, isso indica que o rei moveu-se horizontalmente. Se houver variação na linha, mas não na coluna, o rei moveu-se verticalmente. Se houver variação em ambas as direções, o rei moveu-se diagonalmente. O código a seguir ilustra essa lógica:

Movimento do Roque

Aqui começa a parte um pouco mais complexa do movimento do rei, principalmente porque envolve não apenas o movimento de uma, mas de duas peças. Além disso, o movimento do roque não é simétrico, o que nos obriga a estabelecer diferentes condições para verificar o roque do lado do rei e o roque do lado da rainha. Outras condições incluem a necessidade de que o roque só possa ocorrer se não houver nenhuma peça adversária colocando o rei em xeque ou que possa se mover para a linha entre o rei e a torre. Também é crucial que não haja nenhuma peça obstruindo o caminho entre o rei e a torre do roque. Por fim, o roque só é válido se tanto o rei quanto a torre envolvida no roque nunca tiverem se movido anteriormente.

É importante destacar que várias condições precisam ser satisfeitas para que o roque seja considerado válido. No momento, iremos abordar as condições relacionadas ao xeque posteriormente. Para lidar com as diversas condições do roque, começaremos com duas etapas mais simples. Primeiro, faremos uma verificação da posição entre o rei e a torre para assegurar que não existam peças obstruindo o caminho entre eles. Além disso, adicionaremos duas variáveis globais: uma chamada "houve_Roque" do tipo booleana, que indicará quando o roque do rei foi realizado durante a execução do movimento, e uma matriz (array) booleana chamada "_Roque" com quatro entradas. Essas quatro entradas diferentes serão usadas como interruptores para verificar se a torre ou o rei se moveram. Quando ocorrer o movimento de qualquer um deles, a variável correspondente será desativada.

Agora, como mencionado anteriormente, é essencial garantir que não existam peças capazes de se mover para o caminho entre o rei e a torre para confirmar o roque. Para lidar com essa verificação, criaremos um novo tipo chamado "Roque", que indicará o tipo de roque a ser realizado, ou seja, ROQUEBR, ROQUEBD, ROQUEPR, ROQUEPD para os roques do lado branco da rainha, branco do rei, preto do rei e preto da rainha. Vamos declarar esse tipo logo após o identificador "type" em nosso unit.

Agora, adicione o seguinte código logo após o código que foi adicionado anteriormente para verificar o roque do Rei:

Este trecho de código realiza a seguinte verificação: primeiro, verifica se o movimento é válido (Result = True) e se um roque foi tentado. Em seguida, ele examina a localização onde a tentativa de roque foi realizada. Nesse caso, a validação ocorrerá somente se todas as casas entre o rei e a torre estiverem vazias. Para realizar essa verificação, usamos a função CasasRoqueLivres. Agora, é necessário declarar essa função. Vamos retornar à parte superior do nosso código e adicionar a função CasasRoqueLivres conforme mostrado abaixo:

Veja que estamos utilizando o tipo Roque que declaramos anteriormente. Agora pressione Ctrl + Shift + C para declarar a função e vamos colocar o seguinte código dentro dela:

Esta função desempenha um procedimento muito simples. Começamos a função com um resultado positivo (Result := True). Em seguida, usamos uma estrutura de caso (case) para verificar qual é o tipo de roque que foi solicitado. Dependendo do tipo de roque, realizamos uma verificação rápida para determinar se há alguma peça adversária que pode se mover para o caminho entre o rei e a torre. A função ChecagemRapida, uma vez que é utilizada apenas dentro desta função, foi incorporada diretamente na função CasasRoqueLivres, como demonstrado anteriormente. É importante notar que, ao fazer isso, nenhuma outra função terá acesso à função ChecagemRapida (e ela não será utilizada em nenhum outro contexto).

A função ChecagemRapida executa uma varredura rápida em busca de peças adversárias e verifica a validade de seus movimentos em relação às casas entre o rei e a torre. Neste ponto, chamamos outra função externa chamada "PodeMover" para validar adequadamente o movimento das peças nessas casas.

Agora, precisamos declarar a função "PodeMover" dentro do nosso TForm1:

Novamente pressione a combinação Ctrl + Shift + C para declarar a função PodeMover. Dentro desta função vamos adicionar o seguinte código:

Esta função faz uso das funções de verificação para as diferentes peças que foram definidas anteriormente. Aqui, declaramos uma variável global chamada "pm" (um booleano) que indica, dentro das funções de validação, se o acesso a elas está ocorrendo como parte de uma simulação, conduzida pela função "PodeMover" (pm = True), ou não. Isso é de extrema importância para evitar que a função entre em um loop infinito, chamando a si mesma repetidamente.

Logo em seguida, utilizamos uma estrutura condicional "case" para determinar qual movimento de peça deve ser verificado. A validação do movimento, representada pela variável "MovimentoValido", é realizada por meio das funções de validação específicas para cada tipo de peça. A única exceção é o peão, para o qual adicionamos a condição booleana "CapPeao" em conjunto com o operador lógico AND (e). Isso é necessário porque o ataque do peão não é executado através do seu movimento direto, mas sim através do seu movimento diagonal.

Para concluir a seção do Roque, vamos adicionar os seguintes comandos:

Esta seção do código desativa os possíveis movimentos de roque caso o rei se mova. No condicional inicial, verificamos primeiramente se o movimento é válido, ou seja, Result = True. Além disso, introduzimos chaves booleanas de verificação chamadas "Simulado" e "pm". Como mencionado anteriormente, "pm" indica que o movimento do rei está sendo acessado através da função "PodeMover". A chave "Simulado" funciona de maneira semelhante à chave "pm" e é usada para indicar que outras funções estão simulando o movimento que invocará a função de movimento do rei. Essas chaves são essenciais para evitar um loop infinito, uma vez que a função subsequente "EstaEmxeque" simula o movimento do rei usando a função "CheckMovRei", e também evitam que o roque seja desativado por meio da simulação do movimento do rei.

Agora, precisamos declarar a função "EstaEmXeque", que também será usada posteriormente para verificar movimentos que colocam o rei em xeque ou determinar o xeque-mate. Para declarar a função "EstaEmxeque", siga as instruções abaixo:

Pressionem novamente a combinação de teclas Ctrl + Shift + C para declarar a função. Agora, dentro da função EstaEmXeque adicione o seguinte código:

Esta função recebe as coordenadas da posição do rei em NCol e NLinha, bem como a cor do rei como parâmetros. Começamos a função com uma saída negativa, indicando que o rei não está em xeque (Result := False). Em seguida, iniciamos um loop para buscar peças adversárias que possam atacar o rei. Para isso, utilizamos uma estrutura condicional que verifica se a casa não está vazia e se a peça contida na casa é da cor oposta. Isso é verificado através da função interna "EAdversario" (é adversário), que foi declarada dentro da nossa função.

Em seguida, simulamos o movimento para verificar se as peças do adversário podem capturar o rei sendo analisado. Para isso, atualizamos as variáveis globais "Linha" e "Coluna", que são utilizadas em nosso código para representar o início de um movimento. Antes de modificar o valor dessas variáveis, é importante armazenar seus valores originais em outras variáveis temporárias.

Adicionamos um condicional que verifica se o movimento já está sendo simulado, com o objetivo de evitar um loop infinito. Também verificamos se a chamada está sendo feita pelo rei, mesmo que seja uma simulação, e se houve movimento da peça. Se essas condições forem atendidas, ativamos a chave "Simulado" para informar outras funções que uma varredura simulada dos movimentos está em andamento. Em seguida, utilizamos uma estrutura condicional "case" para verificar o tipo de peça encontrada durante a varredura e simulamos o movimento, como discutido anteriormente.

Após a simulação dos movimentos, restauramos nossas variáveis para concluir a função.

Além disso, desativamos a possibilidade de roque caso o rei tenha se movido. No entanto, o roque também não é mais válido caso a torre tenha se movido. Portanto, também precisamos adicionar comandos dentro da função "CheckMovTorre" que invalide o roque caso a torre se mova. Para fazer isso, vá até a função "CheckMovTorre" e, no final do seu código, adicione o seguinte trecho:

Este trecho de código verifica se a casa da qual a torre se moveu era a sua posição inicial. Se essa condição for verdadeira, o correspondente movimento de roque será invalidado. É importante notar que adicionamos a constante "Tabu" (Tabuleiro), que é do tipo "Matrix" (Array). Esta constante contém todas as casas do tabuleiro de xadrez identificadas pelos seus respectivos nomes. Embora essa constante não seja estritamente necessária para invalidar o roque quando a torre se move, ela será útil para a validação de empates.

É relevante observar que utilizamos uma constante em vez de uma variável para representar os nomes das casas no tabuleiro. Isso ocorre porque os nomes das casas do tabuleiro não devem ser modificados; ou seja, eles são constantes. A declaração desta constante global pode ser realizada da seguinte forma:

Conforme demonstrado nos capítulos anteriores, a verificação dos movimentos das peças, incluindo o movimento do rei, é realizada no evento TabuleiroMouseUp. Esse evento aciona as funções necessárias para determinar se o movimento é válido ou não para a peça em questão. A última atualização desse evento foi feita para tratar do movimento do peão, onde mostramos como registrar movimentos EnPassant e promoções do peão.

Neste capítulo, vamos abordar como registrar o movimento do rei, o roque e, posteriormente, o xeque em nossa lista de movimentos. Essa lista de movimentos não apenas nos permite avançar e voltar na sequência de jogadas, mas também auxilia na validação de movimentos especiais, como foi o caso do movimento EnPassant do peão.

Antes de iniciarmos o registro dos movimentos, vamos atualizar o nosso tipo "TMovimento" com as novas jogadas que devem ser registradas. Faremos adições relacionadas aos movimentos de roque, condições de xeque, xeque-mate, empate e parâmetros de ajuste, como "pos_Tab," que será usado posteriormente para evitar empates. A atualização do tipo "TMovimento" ficará da seguinte forma:

Agora, dentro do nosso evento TabuleiroMouseUp, modificamos o código para o seguinte:

Observe que usamos uma estrutura condicional para verificar quando o roque foi executado e, em seguida, registramos esse movimento em nossa lista de jogadas. Além disso, configuramos as variáveis "Simulado" e "houve_Roque" no início do código para evitar problemas de inicialização.

Verificação do Xeque e Xeque Mate

A verificação do xeque e xeque-mate pode ser considerada a parte mais complexa da programação do nosso tabuleiro de xadrez. Isso se deve ao fato de que a verificação do xeque e do xeque-mate envolve todas as peças do tabuleiro, além da validação do movimento do próprio rei. Além disso, o rei não pode mover-se para uma casa que possa colocá-lo em xeque, as peças da mesma cor não podem se mover de forma que isso coloque o seu próprio rei em xeque, o roque não pode ser realizado se o rei estiver em xeque ou se o caminho entre o rei e a torre estiver em xeque.

Por outro lado, a verificação do xeque-mate envolve a análise de todas as possíveis jogadas para verificar se existe alguma que possa retirar o rei do xeque.

Continuaremos a implementação dessas verificações no mesmo evento TabuleiroMouseUp, adicionando os comandos a seguir logo após o registro do Roque.

Os condicionais são criados para as peças brancas e pretas. Dentro deles, iniciamos a verificação para determinar se o rei está em xeque, utilizando a função "EstaEmXeque", que foi apresentada anteriormente. Se o rei estiver em xeque, passamos a verificar se existe algum movimento que pode retirar o rei do xeque, utilizando a função "ExisteMovimentoParaSairDoXeque". Caso essa função retorne um resultado negativo (False), então confirmamos a ocorrência do xeque-mate. Nesse caso, exibimos uma mensagem ao usuário informando que ocorreu o xeque-mate, definimos a variável global "FimDeJogo" como positiva e registramos o xeque-mate na nossa lista de movimentos.

É importante notar que no início do evento TabuleiroMouseUp, adicionamos a seguinte condição: "if FimDeJogo then Exit;". Isso significa que, se o jogo já tiver terminado, não será possível avançar além desse ponto.

Agora, vamos adicionar a função "ExisteMovimentoParaSairDoxeque". Novamente, declare essa função na parte superior da unit.

Pressione Ctrl + Shift + C para declarar a função e adicione o seguinte código:

Observe que esta é uma função concisa que realiza uma varredura em todas as peças do tabuleiro em busca de qualquer peça da mesma cor que possa realizar um movimento para retirar o rei do xeque. Isso é realizado através da função "MovimentoSimuladoEmXeque". Se essa função retornar "False", significa que existem movimentos que podem tirar o rei do xeque, e, nesse caso, a função retorna "True".

Agora, vamos proceder com a declaração da função "MovimentoSimuladoEmXeque".

Pressione Ctrl+Shift+C e adicione o seguinte código:

Esta função é muito semelhante às descritas anteriormente e basicamente simula a jogada das peças. A variável booleana "MovimentoValido" será verdadeira (Result = True) se o movimento da peça da mesma cor for válido e, crucialmente, se não colocar o próprio rei em xeque.

A decisão de realizar essa validação foi motivada pelo fato de que precisaremos adicionar uma verificação em cada peça para determinar se o movimento dela pode colocar o próprio rei em xeque. Aproveitamos essa oportunidade para tornar as verificações de movimentos para sair do xeque mais fáceis de serem realizadas.

No entanto, até o momento, ainda não implementamos o código dentro das funções de cada peça para verificar se elas colocam o próprio rei em xeque após realizar um movimento. Portanto, teremos que fazer isso para cada tipo de peça.

Começaremos esse processo pelo peão. Vamos voltar para a função de validação do peão, "CheckMovPeao", e, no final do seu código, adicionaremos o seguinte trecho:

Agora, observe que estamos utilizando outra função chamada "SimularEVerificarXeque" para avaliar a possibilidade de xeque. Esta função tem a responsabilidade de organizar e chamar a função "Emxeque" de forma adequada para verificar a presença ou ausência de xeque. Certifique-se de declarar a função "SimularEVerificarXeque" no início da sua unit.

Pressione Ctrl + Shift + C para declarar a função e adicione o seguinte código:

Nesta função, estamos tratando do movimento da peça de forma a evitar que ela coloque o próprio rei em xeque após o movimento. Além disso, adicionamos uma condição especial para lidar com o movimento do rei. A maior parte da lógica nesta função é semelhante àquela apresentada anteriormente.

Agora, vamos estender essa verificação de xeque para o bispo. Vá até a função "CheckMovBispo" e adicione o seguinte trecho ao final do seu código:

E agora para o seu cavalo (CheckMovCavalo):

Para a torre (CheckMovTorre):

E por fim, o rei (CheckMovRei):

Observe que não é necessário realizar esse procedimento para a rainha (CheckMovRainha), uma vez que as verificações de movimento da rainha são baseadas nos movimentos do bispo e da torre.

Com essas implementações, agora dispomos de todas as verificações necessárias para conduzir a análise de xeque ou xeque-mate.

Verificação do empate

Chegamos agora a última parte deste capítulo onde vamos mostrar como verificar se houve empate ou não. O empate no xadrez pode ocorrer por diversas formas. Entre eles estão:

  • Empate por material insuficiente: ocorre quando as peças remanescentes no tabuleiro de ambos os lados não são suficientes para que o jogo termine em xeque-mate.
  • Empate por repetição de posição: ocorre quando uma mesma posição é repetida no tabuleiro por mais de três vezes.
  • Empate por 50 movimentos: ocorre quando não há progresso no jogo por mais de 50 movimentos, o que acontece quando não ocorre movimento de peões e nenhuma captura de peças em 50 movimentos.
  • Empate por Stalemate ou Afogamento: ocorre quando um dos jogadores não tem mais movimentos disponíveis para fazer, e seu rei não está em xeque.
  • Empate por acordo entre jogadores: acontece quando os jogadores decidem terminar o jogo em empate por mútuo acordo.

Agora vamos explicar como abordar esses tipos de empate, à exceção do empate por acordo entre jogadores, pois este não requer validação por parte do nosso tabuleiro.

Para começar, vamos criar uma variável global chamada "ChecarEmpate," que será responsável por verificar todos esses tipos de empate e determinar se algum deles ocorreu.

Pressione a combinação de teclas Ctrl + Shift + C e adicione o seguinte código:

Observe que esta função simplesmente chama outras funções que efetivamente realizam a verificação das condições de empate. Se um empate for identificado, a função emitirá um aviso para o usuário indicando que ocorreu um empate e qual o tipo de empate que ocorreu.

Agora, vamos proceder com a declaração da função "VerificarMaterialInsuficiente" no início do nosso código.

Pressione Ctrl + Shift + C para criar a função e adicione o seguinte código:

Este código é bastante conciso, pois realiza uma varredura no tabuleiro em busca das peças remanescentes. Essas peças são agrupadas dentro de uma variável do tipo texto (String) chamada "PecasRestantes." Utilizamos a lógica OR para verificar se as peças restantes no tabuleiro são insuficientes para resultar em um xeque-mate.

Agora, repetiremos o mesmo processo para declarar a função "VerificarRepeticaoPosicao." Após declarar e criar a função, adicione o código a seguir:

Nesta função, criamos uma string chamada "PosicaoAtual" que representa a posição atual do tabuleiro. Isso é feito pela função "GerarStringPosicaoAtual," que é declarada dentro da própria função. A string "PosicaoAtual" será comparada com posições anteriores que foram registradas na nossa variável global "HistoricoMovimentos," utilizando o parâmetro "pos_Tab" que foi declarado no início deste capítulo. Utilizamos um contador para verificar se a posição do tabuleiro se repete mais de três vezes; se isso acontecer, a condição de empate é estabelecida e o resultado da função é definido como positivo (Result := True).

É importante notar que, para realizar esse procedimento, é necessário manter a posição das peças do tabuleiro atualizada dentro do parâmetro "pos_Tab" na variável "HistoricoMovimentos." Para fazer isso, voltamos ao evento "TabuleiroMouseUp" e adicionamos o comando "AtualizarPosString" da seguinte forma:

Veja que agora temos a função AtualizarPosString. Declare esta função na parte superior da sua unit, e dentro desta função, adicione o seguinte código:

Observe que esta função realiza exatamente o mesmo que foi feito dentro da função "VerificarRepeticaoPosicao" com o auxílio da função interna "GerarStringPosicaoAtual." Como essa funcionalidade é usada duas vezes, seria benéfico declará-la globalmente para que possa ser utilizada em ambos os casos.

Agora, passaremos para a função "Verificar50Movimentos," que verificará se houve algum progresso no jogo nos últimos 50 movimentos. Declare a função e adicione o seguinte código:

Observe a brevidade deste código. Apesar de ser extremamente conciso, é necessário ter um meio de atualizar o contador representado pela variável "ContadorMovimentosSemCapturaOuMovPeao" (sinta-se à vontade para abreviar o nome dessa variável em seu código, se preferir). Neste ponto, é interessante notar como evitamos a utilização de um condicional "IF," incorporando a condição booleana diretamente na resposta do "Result."

Para implementar a variável global "ContadorMovimentosSemCapturaOuMovPeao" como um inteiro, e dentro do nosso evento "TabuleiroMouseUp," adicione o seguinte código:

Adicionamos esta parte de código logo após a verificação de "houve_Roque" e antes da verificação de xeque ou xeque-mate. Este condicional verifica a cada movimento se ocorreu captura de peça ou se a peça movida foi um peão. Caso contrário, o contador é incrementado. Se o contador atingir 50 movimentos, o empate é declarado.

Agora, vamos tratar do último tipo possível de empate no xadrez, o StaleMate ou afogamento do rei. Declare a função "VerificarStaleMate" e adicione o seguinte código dentro da função:

Para verificar o StaleMate, utilizamos novamente a função "PodeMover." Esta função verifica se há qualquer tipo de movimento disponível. Se houver, o StaleMate não é confirmado. No entanto, se não houver movimentos disponíveis, a segunda verificação é feita para verificar se o rei está em xeque. Se o rei estiver em xeque e não houver movimentos possíveis, isso caracteriza um xeque-mate e não um StaleMate. Por outro lado, se o rei não estiver em xeque, então o empate por afogamento é estabelecido.

Agora, após criar as funções de verificação, devemos chamá-las dentro do evento "TabuleiroMouseUp." Para isso, adicionamos o seguinte código (preste atenção na localização deste código dentro deste evento, pois isso é de extrema importância para que o código seja executado corretamente):

Após finalizar este passo, tudo deve estar pronto para as verificações relativas ao movimento do rei e as condições de fim de jogo e empate. O código completo do movimento do rei é:

E o código completo para o evento TabuleiroMouseUp é:

Inicialização das variáveis

Neste capítulo apresentamos diversas variáveis globais que devem ser adicionadas ao código para que as validações sejam feitas de forma correta. Agora, devemos inicializar as variáveis para que elas já contenham algum valor quando forem ser utilizadas.

Para fazer isso, dê dois cliques sobre o seu Form para entrar no evento OnCreate. Agora, vamos adicionar as nossas variáveis com seus valores iniciais. Para o Simulado, FimDeJogo, pm e houve_Roque vamos inicializar com valores negativos, ou seja, colocar eles como False.

Para a variável _Roque do tipo matriz, precisamos inicializar as variáveis de forma negativa para os quatro tipos de roque. Para isso vamos usar um loop FOR. O código do seu evento OnCreate deve se parecer com o seguinte:

Sumário

Neste capítulo, abordamos o movimento do rei, avaliamos as condições de xeque, xeque-mate e empate. Como mencionado anteriormente, esta seção é extensa, e pequenos problemas no código podem resultar em erros difíceis de identificar. O código apresentado neste capítulo foi testado e deve funcionar conforme o esperado. Se você estiver seguindo o passo a passo dos capítulos, lembre-se de que houve uma atualização no capítulo 5 do nosso tutorial, que é essencial para o funcionamento adequado deste capítulo.

É importante ressaltar que este código foi desenvolvido pelos proprietários do site abcdocodigo.com e, portanto, não deve ser utilizado para fins comerciais sem a autorização dos autores.

Lembre-se de que este código serve como um tutorial para ajudar novos programadores a compreender a lógica por trás do código de xadrez. Existem partes do código que podem ser simplificadas e otimizadas para reduzir a quantidade de código necessária.

Após este capítulo, teremos um último capítulo em que mostraremos como adicionar uma inteligência artificial ao nosso jogo de xadrez e melhorar o design do nosso tabuleiro.

Espero que tenham gostado deste capítulo, e se tiverem alguma dúvida, não hesitem em entrar em contato.

< Capítulo 5 - Capítulo 7 >