Skip to content

Commit

Permalink
feat: Conclusão do Desafio 13 - @JamesStewart-314 (#1167)
Browse files Browse the repository at this point in the history
Co-authored-by: Allber Fellype <[email protected]>
  • Loading branch information
JamesStewart-314 and Allber Fellype authored Dec 14, 2024
1 parent 3b7e24d commit 9e4df72
Show file tree
Hide file tree
Showing 3 changed files with 161 additions and 0 deletions.
1 change: 1 addition & 0 deletions desafio-13/JamesStewart-314/python/.valid
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1775e84fdf6ead5baea4cc292dc86abab
52 changes: 52 additions & 0 deletions desafio-13/JamesStewart-314/python/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Desafio 13 - Passeio do Cavalo
![Python](https://img.shields.io/badge/Python-512BD4?style=flat&logo=python&logoColor=yellow)
![VS Code](https://img.shields.io/badge/VScode-007ACC?style=flat&logo=visualstudiocode&logoColor=white)
## Descrição do Projeto:
No famigerado jogo de [Xadrez](https://pt.wikipedia.org/wiki/Xadrez), existe uma peça
chamada Cavalo que realiza movimentos em formato de "L" em todas as direções cardinais
numa malha quadriculada 8x8 que compõe o tabuleiro do jogo. O objetivo deste projeto
consiste em exibir a sequência de movimentos do cavalo, partindo de uma casa arbitrária
qualquer do tabuleiro, que passe uma única vez por todas as 64 casas.

Para alcançar este objetivo, este programa utiliza a
[Heurística de Warnsdorff](https://en.wikipedia.org/wiki/Knight%27s_tour#:~:text=Warnsdorf's%20rule%20is%20a%20heuristic,revisit%20any%20square%20already%20visited.)
para otimizar as buscas recursivas pelo primeiro caminho válido que soluciona o problema.

## Exemplo de Uso:
```bash
python ./solution.py a1
```

## Saída Esperada:
```bash
a1
c2
e1
g2
.
.
.
```

## Requisitos para Execução
- Possuir um ambiente virtual Python instalado localmente em sua máquina com a
versão `3.10` ou superior.
Para baixar esta e outras versões, visite o site
<a target="_blank" href="https://www.python.org/downloads/" style="color: lightgreen">Python.org</a>
e siga os procedimentos de instalação para o
seu sistema operacional.
Após a instalação, abra o terminal de comando em sua máquina e digite o comando
`python --version`. O comando deverá informar a versão atual do interpretador de
Python caso o download tenha sido feito corretamente. Certifique-se de possuir uma
versão igual ou superior à `3.10`, caso contrário, o código não funcionará.
## Instruções para Executar o Código
- Certificando-se de ter instalado corretamente o `Python` em sua
máquina, execute os seguintes comandos:
1. Abra o terminal e navegue até a pasta em que deseja copiar este repositório com o
comando `cd <caminho_absoluto_do_diretótio>`;
2. Em seguida, copie e cole o seguinte código:
`git clone https://github.com/OsProgramadores/op-desafios.git`;
3. Navegue até a pasta contendo o arquivo `solution.py` na árvore do repositório - se
necessário, utilize o comando `cd ".\op-desafios\desafio-13\JamesStewart-314"`
4. Execute o script `"solution.py"` com o comando `python solution.py`
e os resultados deverão ser impressos de maneira formatada na CLI.
108 changes: 108 additions & 0 deletions desafio-13/JamesStewart-314/python/solution.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import re
import sys
from typing import TypeAlias

coord_pos: TypeAlias = tuple[int, int]

def map_position_to_board(coordinate_pos: coord_pos) -> str:
return f"{chr(97 + coordinate_pos[1])}{8 - coordinate_pos[0]}"


def map_board_to_position(board_pos: str) -> coord_pos:
return (8 - int(board_pos[1]), ord(board_pos[0]) - 97)


def get_possible_knight_moves(current_position: coord_pos) -> set[coord_pos]:
set_pos_moves: set[coord_pos] = set()

if (tmp_pos_1 := current_position[0] + 2) <= 7:
if (tmp_pos_2 := current_position[1] + 1) <= 7:
set_pos_moves.add((tmp_pos_1, tmp_pos_2))
if (tmp_pos_2 := current_position[1] - 1) >= 0:
set_pos_moves.add((tmp_pos_1, tmp_pos_2))

if (tmp_pos_1 := current_position[1] + 2) <= 7:
if (tmp_pos_2 := current_position[0] + 1) <= 7:
set_pos_moves.add((tmp_pos_2, tmp_pos_1))
if (tmp_pos_2 := current_position[0] - 1) >= 0:
set_pos_moves.add((tmp_pos_2, tmp_pos_1))

if (tmp_pos_1 := current_position[0] - 2) >= 0:
if (tmp_pos_2 := current_position[1] + 1) <= 7:
set_pos_moves.add((tmp_pos_1, tmp_pos_2))
if (tmp_pos_2 := current_position[1] - 1) >= 0:
set_pos_moves.add((tmp_pos_1, tmp_pos_2))

if (tmp_pos_1 := current_position[1] - 2) >= 0:
if (tmp_pos_2 := current_position[0] - 1) >= 0:
set_pos_moves.add((tmp_pos_2, tmp_pos_1))
if (tmp_pos_2 := current_position[0] + 1) <= 7:
set_pos_moves.add((tmp_pos_2, tmp_pos_1))

return set_pos_moves


class ChessKnight:
_board_positions: set[coord_pos] = {(p1, p2) for p1 in range(8) for p2 in range(8)}

_positions_mapping: dict[coord_pos, set[coord_pos]] = dict(zip(_board_positions,
(get_possible_knight_moves(pos)\
for pos in _board_positions)))

def __init__(self, knight_position,
m_list: list[coord_pos] | None = None,
m_set: set[coord_pos] | None = None):
self.current_position: coord_pos = knight_position
self.knight_mov_history_list: list[coord_pos] = m_list or [knight_position]
self.knight_mov_history_set: set[coord_pos] = m_set or {coord_pos}

def __repr__(self) -> str:
return f"ChessKnight<{self.current_position},"\
f"{self.knight_mov_history_list},"\
f"{self.knight_mov_history_set}>"

@classmethod
def move_knight(cls, knight, new_pos: coord_pos):
new_knight = cls(new_pos,
[*knight.knight_mov_history_list, new_pos],
{*knight.knight_mov_history_set, new_pos})
return new_knight

@staticmethod
def get_next_knight_moves(knight)-> set[coord_pos]:
return ChessKnight._positions_mapping[knight.current_position] -\
knight.knight_mov_history_set

@staticmethod
def next_knight_movement_size(knight, new_pos: coord_pos) -> int:
return len(ChessKnight.get_next_knight_moves(ChessKnight.move_knight(knight, new_pos)))

@staticmethod
def get_warnsdorff_move(knight, pos_moves: set[coord_pos]) -> coord_pos:
return min(pos_moves, key=lambda x: ChessKnight.next_knight_movement_size(knight, x))


def knight_tour(knight: ChessKnight) -> list[coord_pos]:
if len(knight.knight_mov_history_list) == 64:
return knight.knight_mov_history_list

for _ in range(len(n_moves := ChessKnight.get_next_knight_moves(knight))):
current_movement: tuple[int, int] = ChessKnight.get_warnsdorff_move(knight, n_moves)
if (res := knight_tour(ChessKnight.move_knight(knight, current_movement))):
return res
n_moves.remove(current_movement)

return None


if __name__ == '__main__':
if len(sys.argv) != 2 or not re.match(r"^[a-h][1-8]$", (init_pos := sys.argv[1].lower())):
print("Error: Provide just one argument representing a valid chess board position. "\
"E.g.: \"python solution.py a3\".")
sys.exit()

initial_position: coord_pos = map_board_to_position(init_pos)
final_path: list[coord_pos] = knight_tour(ChessKnight(initial_position))

for position in map(map_position_to_board, final_path):
print(position)

0 comments on commit 9e4df72

Please sign in to comment.