Skip to content

Commit

Permalink
final commit
Browse files Browse the repository at this point in the history
  • Loading branch information
RafaelGoncalvesUA committed Dec 9, 2022
1 parent a0affda commit 7b3a5fd
Show file tree
Hide file tree
Showing 14 changed files with 22 additions and 15 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ dmypy.json
.pyre/

# Pdf
*.pdf
IA_Rush_Hour.pdf

run.sh
benchmarks/
Expand Down
6 changes: 6 additions & 0 deletions graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ def show_graphs(LEVELS_PACK):
plt.ylabel("Time (s)")
plt.title(f"Linear regression of time per level ({LEVELS_PACK})")
plt.legend()
plt.tight_layout()
plt.savefig(f"graphs/{LEVELS_PACK}/time_graph.png")
plt.show()

Expand All @@ -76,6 +77,7 @@ def show_graphs(LEVELS_PACK):
plt.ylabel("Number of expanded nodes")
plt.title(f"Linear regression of number of expanded nodes per level ({LEVELS_PACK})")
plt.legend()
plt.tight_layout()
plt.savefig(f"graphs/{LEVELS_PACK}/nodes_graph.png")
plt.show()

Expand All @@ -98,6 +100,7 @@ def show_graphs(LEVELS_PACK):
plt.ylabel("Number of moved pieces")
plt.title(f"Linear regression of number of moved pieces per level ({LEVELS_PACK})")
plt.legend()
plt.tight_layout()
plt.savefig(f"graphs/{LEVELS_PACK}/moves_graph.png")
plt.show()

Expand Down Expand Up @@ -130,6 +133,7 @@ def show_graphs(LEVELS_PACK):
plt.ylabel("Points")
plt.title("Points per strategy (levels1)")
plt.ylim(487000, 490000)
plt.tight_layout()
plt.savefig("graphs/levels1/points_graph.png")
plt.show()

Expand All @@ -154,6 +158,7 @@ def show_graphs(LEVELS_PACK):
plt.title("Points per strategy (levels2)")
plt.ylim(1039000, 1043000)
plt.ticklabel_format(style='plain', axis='y')
plt.tight_layout()
plt.savefig("graphs/levels2/points_graph.png")
plt.show()

Expand Down Expand Up @@ -240,5 +245,6 @@ def show_graphs(LEVELS_PACK):
plt.title("Points per strategy (levels)")
plt.ylim(1555000, 1559000)
plt.ticklabel_format(style='plain', axis='y')
plt.tight_layout()
plt.savefig("graphs/levels/points_graph.png")
plt.show()
Binary file modified graphs/levels/points_graph.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified graphs/levels1/moves_graph.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified graphs/levels1/nodes_graph.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified graphs/levels1/points_graph.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified graphs/levels1/time_graph.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified graphs/levels2/moves_graph.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified graphs/levels2/nodes_graph.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified graphs/levels2/points_graph.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified graphs/levels2/time_graph.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added presentation.pdf
Binary file not shown.
28 changes: 14 additions & 14 deletions report.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ Numa primeira fase do projeto, entendemos que deveríamos dividir o desenvolvime

### Conjunto de classes de suporte

- Matrix: é um nó árvore, que guarda um estado do puzzle, sendo 2 dos seus atributos:
- Matrix: é um nó da árvore, que guarda um estado do puzzle, sendo 2 dos seus atributos:
- grid: guarda o estado do puzzle. Representa uma matriz (lista 2D), porém é armazenada eficientemente, na forma de string. Sabendo as coordenadas (x,y) de uma posição da matriz, é possível obter facilmente o seu valor, através do método get(x, y).
- pieces: dicionário que mapeia cada peça do puzzle para a sua posição atual, na forma (minx, maxx, miny, maxy), que denota as suas fronteiras.
- MatrixForGreedy: é uma subclasse de Matrix, que sobrescreve o método **lt**, de modo a que a comparação de dois nós seja feita apenas com base na heurística.
- MatrixForAStar: é uma subclasse de Matrix, que sobrescreve o método **lt**, de modo a que a comparação de dois nós seja feita com base no custo total (custo acumulado do caminho do cursor + heurística).
- AI: classe não instanciável, que disponibiliza os seguintes métodos estáticos auxiliares:
- copy: copiar uma string por valor (não por referência).
- replace_char: substituir uma posição da grid, permitindo a movimentação das peças.
Expand All @@ -30,28 +31,27 @@ Numa primeira fase do projeto, entendemos que deveríamos dividir o desenvolvime
- search(): não tem em conta custos ou heurísticas. Explora os nós exaustivamente, até encontrar a solução, excluindo aqueles cujas grids já tenham sido visitadas anteriormente. É utilizada pela pesquisa em profundidade e em largura.
- search2(): tem em conta a profundidade do nó (custo sem conhecimento da posição do cursor) e a sua heurística. É utilizada pela pesquisa gulosa, que ordena os nós, apenas pela heurística, sem atender ao custo supracitado, cujo intuito é a minimização da profundidade da árvore.
- search3(): tem em conta apenas o custo acumulado do caminho do cursor. É utilizado pela pesquisa uniforme.
- search4(): tem em conta o custo acumulado do caminho do cursor e a sua heurística. É utilizado pela pesquisa A\*.
- search4(): tem em conta o custo acumulado do caminho do cursor e a sua heurística. É utilizado pela pesquisa A.

### Otimizações

- Quando um nó é criado, herda alguns dados do nó pai. Aplicando o padrão de software Flyweight, é possível reduzir, não só a memória utilizada, como o tempo de execução, uma vez que se promove a reutilização de objetos, em detrimento da criação de cópias. Por exemplo, os atributos horizontal_pieces e vertical_pieces são constantes, pelo que podem ser copiados por referência. O pieces, por outro lado, é manipulado em cada nó, pelo que é necessário copiá-lo por valor.
- Numa primeira versão do search, a estrutura grid_visited era uma lista. Após discutirmos estratégias de otimização, com o grupo "102536_102778", concluímos que seria mais eficiente utilizar um conjunto (set), uma vez que a operação de verificação de pertença (lookup) apresenta uma complexidade O(1), enquanto que, nas listas, é O(n). Em versões posteriores, mantivemos esta estrutura. Contudo, há que salientar que a complexidade do lookup com chave, nos dicionários, é também O(1).
- Tendo por base conhecimentos prévios de Algoritmos e Estruturas de Dados, ponderámos a utilização de uma min-heap, como estrutura para guardar os nós por explorar. Para esse efeito, aproveitámos o módulo heapq, nativo do Python. Os resultados foram bastante positivos, dado que cada inserção garante a ordenação da heap, não sendo necessária qualquer operação ulterior, com vista a obter o valor mínimo. O critério de ordenação é definido pelo método **lt** da classe de objetos que compõem a heap, neste caso, a Matrix ou a MatrixForGreedy.

### HEURÍSTICA
- HEURÍSTICA: número de peças a bloquear o caminho da peça 'A' até à saída.

### Agente

O agente em cada iteração sensoriza o jogo através das funções detectCrazy e detectStuck.

Como estudado nesta unidade curricular, um agente é "uma entidade com capacidade de obter
informação sobre o seu ambiente (através de 'sensores') e de executar ações em função dessa informação.
Em cada iteração do seu ciclo principal, o agente capta o estado do puzzle, através dos seus sensores:
- código de deteção de alteração da grid;
- detectCrazy - deteta a ocorrência de um crazy car;
- detectStuck - deteta a ocorrência de uma anómalia que não foi identificada como crazy car, mas impediu a realização com sucesso do comando enviado para o jogo.

O agente escolhe a estratégia de resolução do problema consoante o tamanho da grid, sendo que, utiliza:
- detectStuck - deteta a ocorrência de uma anomalia que não foi identificada como crazy car, mas impediu a realização com sucesso do comando enviado para o jogo.

- pesquisa gulosa quando a grid tem dimensão superior a 6x6;
- pesquisa uniforme quando a grid tem dimensão igual ou inferior a 6x6;
De seguida, o agente escolhe a estratégia de resolução do problema, consoante o tamanho da grid:
- pesquisa gulosa, quando a grid tem dimensão superior a 6x6;
- pesquisa uniforme, quando a grid tem dimensão igual ou inferior a 6x6;

O agente após receber a solução do tree_search converte-a em movimentos do cursor através da função moveCursor e simulando o estado do jogo.
A solução do tree_search é convertida em movimentos do cursor, por intermédio da função moveCursor. Esta simula o comportamento do cursor, ao selecionar uma peça a partir da sua fronteira mais próxima (está otimizada, nesse sentido).

O tempo que demora a encontrar a solução e a convertê-la é cronometrado, sendo utilizado para repor a sincronização com o servidor através do envio de comandos vazios quando o tempo total é maior do que a taxa de aceitação de comandos por parte do servidor.
O tempo que demora a encontrar a solução e a convertê-la é cronometrado, para assegurar a sincronização com o servidor. Quando esse tempo total é maior do que a taxa de aceitação de comandos por parte do servidor, são enviados comandos vazios.
1 change: 1 addition & 0 deletions tree_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ def cost(state: Matrix, action):

def heuristic(state: Matrix):
_, maxx, miny, _ = state.pieces["A"]
# number of pieces that block the way from A to the exit
return sum(1 for x in range(maxx+1, state.n) if state.get(x, miny) != "o")

def actions(state: Matrix):
Expand Down

0 comments on commit 7b3a5fd

Please sign in to comment.