• Python

2022.1 Prova 4 - Questão 1: Agente inteligente para o jogo de xadrez#

Vamos desenvolver a base para um agente inteligente capaz de jogar xadrez. O objetivo não é construir o agente em si, mas preparar as funções necessárias.

Nesta atividade utilizaremos apenas um subconjunto das peças existentes: rei (♔), cavalo (♘), torre (♖) e bispo (♗). Cada peça tem uma maneira de se mover pelo tabuleiro. Esses movimentos estão indicados na página de movimentos.

Note que estamos usando um tabuleiro invertido no eixo \(y\) em relação a tabuleiros comuns. Isso foi feito para os índices da matriz e do tabuleiro ficarem parecidos.

Esta notação é mostrada na figura a seguir.

Notação de linha x coluna.

Também note que sempre vamos fazer as análises do ponto de vista de uma peça de cada vez apenas, e ainda que valem regras diferentes das do xadrez, para simplicidade:

  • Pode-se capturar o rei
  • O jogo continua sem o rei
  • Os valores considerados para as peças não são acurados

Importante

Faça o download do exercício pelo link no final desta página!

Nível Básico (C)#

Para o nível básico, você deve implementar uma função chamada calcula_movimentos_possiveis(tabuleiro, peca, linha, coluna) para o rei, bispo e torre (o cavalo está no proficiente). O tabuleiro é representado por uma lista com 8 listas, com 8 strings cada. Cada elemento dessa lista de listas será uma das strings abaixo:

String Significado Em português
'' String vazia Espaço vazio
'PM' Possible Movement Movimento possível
'WK' White King Rei Branco
'WN' White kNight Cavalo Branco
'WR' White Rook Torre Branca
'WB' White Bishop Bispo Branco
'BK' Black King Rei Preto
'BN' Black kNight Cavalo Preto
'BR' Black Rook Torre Preta
'BB' Black Bishop Bispo Preto

Note que além das peças e do espaço vazio, temos também 'PM', que indica que aquela posição pode ser alcançada a partir da posição atual de uma determinada peça.

Alguns exemplos:

Exemplo 1#

No exemplo abaixo temos um bispo preto na linha 3 e coluna 5:

[
    ['', '', '', '', '', '', '', ''],
    ['', '', '', '', '', '', '', ''],
    ['', '', '', '', '', '', '', ''],
    ['', '', '', '', '', 'BB', '', ''],
    ['', '', '', '', '', '', '', ''],
    ['', '', '', '', '', '', '', ''],
    ['', '', '', '', '', '', '', ''],
    ['', '', '', '', '', '', '', ''],
]

Exemplo 2#

No exemplo abaixo temos um cavalo branco na linha 2 e coluna 4 e um rei preto na linha 4 e coluna 5.

[
    ['', '', '', '', '', '', '', ''],
    ['', '', '', '', '', '', '', ''],
    ['', '', '', '', 'WN', '', '', ''],
    ['', '', '', '', '', '', '', ''],
    ['', '', '', '', '', 'BK', '', ''],
    ['', '', '', '', '', '', '', ''],
    ['', '', '', '', '', '', '', ''],
    ['', '', '', '', '', '', '', ''],
]

A função calcula_movimentos_possiveis(tabuleiro, peca, linha, coluna) deve receber um tabuleiro representado como a lista de listas descrita acima, uma peça (string da tabela acima) e a sua posição (linha e coluna). A função deve:

  1. Adicionar a peça na posição indicada (linha e coluna);
  2. Preencher todas as posições para as quais a peça pode se mover com a string 'PM'.

Os movimentos de cada peça podem ser encontrados na página de movimentos.

Para o nível básico a função não precisa se preocupar com a presença de outras peças no tabuleiro. Ou seja, no nível básico você pode supor que o tabuleiro sempre virá vazio. Exemplos:

Para o tabuleiro de entrada abaixo:

[
    ['', '', '', '', '', '', '', ''],
    ['', '', '', '', '', '', '', ''],
    ['', '', '', '', '', '', '', ''],
    ['', '', '', '', '', '', '', ''],
    ['', '', '', '', '', '', '', ''],
    ['', '', '', '', '', '', '', ''],
    ['', '', '', '', '', '', '', ''],
    ['', '', '', '', '', '', '', ''],
]

Com a peça 'BB', a linha 3 e a coluna 2, sua função deve devolver a matriz (lista de listas):

[
    ['', '', '', '', '', 'PM', '', ''],
    ['PM', '', '', '', 'PM', '', '', ''],
    ['', 'PM', '', 'PM', '', '', '', ''],
    ['', '', 'BB', '', '', '', '', ''],
    ['', 'PM', '', 'PM', '', '', '', ''],
    ['PM', '', '', '', 'PM', '', '', ''],
    ['', '', '', '', '', 'PM', '', ''],
    ['', '', '', '', '', '', 'PM', ''],
]

Para o mesmo tabuleiro vazio, a peça 'WN', a linha 1 e coluna 4, sua função deve devolver a matriz:

[
    ['', '', 'PM', '', '', '', 'PM', ''],
    ['', '', '', '', 'WN', '', '', ''],
    ['', '', 'PM', '', '', '', 'PM', ''],
    ['', '', '', 'PM', '', 'PM', '', ''],
    ['', '', '', '', '', '', '', ''],
    ['', '', '', '', '', '', '', ''],
    ['', '', '', '', '', '', '', ''],
    ['', '', '', '', '', '', '', ''],
]

Nível Proficiente (B)#

Para o nível proficiente, você deve modificar a sua função calcula_movimentos_possiveis para que ela também considere a existência de outras peças no tabuleiro. Assim, os movimentos devem ser limitados em caso de oclusão ou bloqueio por outras peças.

Quando existe uma peça em seu caminho, o movimento da torre, bispo e rei são limitados (o cavalo pode se mover mesmo se houver oclusão). Neste caso, o caminho fica interrompido pela peça. Existem duas possibilidades:

  1. Quando é uma peça inimiga no caminho, ela pode ser capturada;
  2. Quando é uma peça da mesma cor, o caminho é interrompido uma casa antes.

Veja os exemplos abaixo:

O bispo branco está na posição 4,4

Bispo W Posição 4 4

Quando são encontrados os movimentos possíveis, as peças brancas interrompem seu caminho. As peças pretas podem ser capturadas por este bispo, portanto a posição do adversário é uma posição possível de alcançar.

Bispo W Possibildades de movimento

Note que na imagem abaixo quando temos um bispo preto, a situação se inverte. Este bispo pode avançar nas casas em que havia peças brancas, capturando-as.

Bispo B

Você agora também deve implementar os movimentos possíveis para o cavalo.

Nível Avançado (A)#

Você deve agora criar a função analisa_ameaças que recebe uma matriz com o tabuleiro, uma linha, uma coluna, e uma string que identifica uma peça.

Esta função devolve uma nova matriz (do tipo lista de listas) em que só há "PM" nas casas em que a peça que vai se movimentar não corre o risco de ser capturada por um inimigo na jogada seguinte.

O objetivo é calcular os movimentos possíveis daquela peça, depois remover o sinal "PM" das posições que são atacadas por peças inimigas.

Vejamos o exemplo a seguir:

Inicialmente chamamos a função calcula_movimentos_possiveis assim como nos exercícios anteriores.

Movimentos possíveis para WR 3 5

Agora encontramos as posições em que a peça não será capturada por nenhum inimigo.

Posições livres de ameaças para WR 3 5

OBS.: A posição que contém uma peça inimiga não é atacada por ela.

Essa sua função precisa fazer um uso inteligente da função calcula_movimentos_possiveis

Dica: A função calcula_movimentos_possíveis sobrescreve os conteúdos da matriz de entrada.

Se você quiser criar uma cópia antes de sobrescrever, use a função copy.deepcopy().

    import copy
    matriz_clone = copy.deepcopy(matriz)