From 9e4df72b8e5efc6bf74a1153dc6e0fbe0e98e61f Mon Sep 17 00:00:00 2001 From: Allber Fellype Soares Ferreira Date: Sat, 14 Dec 2024 18:22:12 -0300 Subject: [PATCH] =?UTF-8?q?feat:=20Conclus=C3=A3o=20do=20Desafio=2013=20-?= =?UTF-8?q?=20@JamesStewart-314=20(#1167)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Allber Fellype <133912146+JamesStewart314@users.noreply.github.com> --- desafio-13/JamesStewart-314/python/.valid | 1 + desafio-13/JamesStewart-314/python/README.md | 52 +++++++++ .../JamesStewart-314/python/solution.py | 108 ++++++++++++++++++ 3 files changed, 161 insertions(+) create mode 100644 desafio-13/JamesStewart-314/python/.valid create mode 100644 desafio-13/JamesStewart-314/python/README.md create mode 100644 desafio-13/JamesStewart-314/python/solution.py diff --git a/desafio-13/JamesStewart-314/python/.valid b/desafio-13/JamesStewart-314/python/.valid new file mode 100644 index 000000000..bc3ba71dd --- /dev/null +++ b/desafio-13/JamesStewart-314/python/.valid @@ -0,0 +1 @@ +1775e84fdf6ead5baea4cc292dc86abab diff --git a/desafio-13/JamesStewart-314/python/README.md b/desafio-13/JamesStewart-314/python/README.md new file mode 100644 index 000000000..bd543fe0b --- /dev/null +++ b/desafio-13/JamesStewart-314/python/README.md @@ -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 + Python.org + 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 `; +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. diff --git a/desafio-13/JamesStewart-314/python/solution.py b/desafio-13/JamesStewart-314/python/solution.py new file mode 100644 index 000000000..4923e091a --- /dev/null +++ b/desafio-13/JamesStewart-314/python/solution.py @@ -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)