Pular para o conteúdo principal

O enunciado em linguagem simples

Esta página resume o que o enunciado do projeto (o subject) exige. Cada item diz a regra, o que ela quer dizer na prática e onde o tutorial a constrói.

1. Como o programa é chamado​

O programa se chama obrigatoriamente a_maze_ing.py e recebe um argumento: o caminho de um arquivo de texto com as opções.

python3 a_maze_ing.py config.txt

Qualquer erro (arquivo que não existe, opção inválida, labirinto impossível) precisa terminar com uma mensagem clara, nunca com o programa quebrando e mostrando um traceback do Python (a listagem de linhas de código que o Python imprime quando um erro não é tratado). Construído na parte 14.

2. O arquivo de opções​

Um arquivo de texto com uma opção por linha, no formato CHAVE=valor. Linhas que começam com # são comentários. Seis opções são obrigatórias:

OpçãoO que defineExemplo
WIDTHlargura do labirinto, em célulasWIDTH=20
HEIGHTaltura do labirinto, em célulasHEIGHT=15
ENTRYa célula de entrada, como x,yENTRY=0,0
EXITa célula de saída, como x,yEXIT=19,14
OUTPUT_FILEo nome do arquivo onde o labirinto é gravadoOUTPUT_FILE=maze.txt
PERFECTo tipo de labirinto (item 4)PERFECT=True

O enunciado deixa acrescentar opções; este projeto acrescenta SEED (item 3). O repositório precisa ter um arquivo de opções pronto, o config.txt. Explicado em detalhe na parte 1 e construído na parte 11.

3. O labirinto​

O labirinto é uma grade retangular de células (os quadradinhos). Cada célula tem quatro lados, e cada lado tem ou não uma parede.

Uma célula com os quatro lados nomeados

As regras:

  • Sorteado, mas repetível. Cada execução gera um labirinto diferente, mas deve existir um jeito de repetir exatamente o mesmo labirinto. Isso se faz com uma semente (seed): um número que fixa o sorteio. Mesma semente, mesmo labirinto.
  • Entrada e saída existem, são células diferentes e ficam dentro da grade.
  • Tudo conectado. Dá para chegar a qualquer célula partindo da entrada. A única exceção são as células que desenham o "42".
  • Borda fechada. A volta toda da grade tem parede. A entrada e a saída são células marcadas por dentro, não buracos na borda.
  • Paredes coerentes. Duas células vizinhas dividem a mesma parede, então as duas precisam concordar: se uma diz que há parede à sua direita, a vizinha da direita diz que há parede à sua esquerda.
  • Sem salões. Nenhum bloco de 3x3 células pode ficar totalmente aberto, sem nenhuma parede por dentro. Blocos de 2x3 ou 3x2 abertos são permitidos.
  • Um "42" visível, desenhado por células com as quatro paredes fechadas. Se o labirinto for pequeno demais para caber o "42", ele é gerado sem o desenho e o programa avisa.

Explicado na parte 1: a grade, as paredes e a coerência em A grade e as células, a semente em O arquivo de configuração, os salões em Sem salões e o braiding e o "42" em O padrão "42".

4. Dois tipos de labirinto​

A opção PERFECT escolhe o tipo:

  • PERFECT=True: labirinto perfeito. Existe exatamente um caminho entre a entrada e a saída; o labirinto não tem nenhum ciclo (nenhum jeito de andar em círculo e voltar ao mesmo lugar).
  • PERFECT=False: tabuleiro de Pac-Man, o jogo em que um personagem foge de fantasmas por corredores. Para ser jogável:
    • todo corredor é alcançável;
    • os quatro cantos e o centro são corredores, isto é, células livres do labirinto, que dá para alcançar andando. O motivo vem do próprio enunciado: os fantasmas e as super-pac-gums (as pastilhas especiais do jogo) ficam nos cantos, e o jogador começa no centro;
    • há pelo menos duas rotas independentes (dois ciclos), para quem foge sempre ter outra saída;
    • becos sem saída (células com uma só abertura) são raros: no máximo um ou dois.

O enunciado chama o modo Pac-Man de "o padrão" (default, em inglês). No arquivo de opções isso não dispensa a chave: PERFECT continua obrigatória, e o config.txt do projeto traz PERFECT=False. Na classe que gera o labirinto (MazeGenerator, da parte 9), "padrão" vale ao pé da letra: quem não informa o parâmetro perfect recebe perfect=False.

O "42" fica no meio da grade, mas isso não tira o centro do labirinto: o desenho deixa uma coluna livre entre o "4" e o "2", e o centro da grade cai sempre nela. Numa grade de 20 colunas por 15 linhas, contando de 0, o meio fica na linha 7, entre as colunas 9 e 10; a célula da coluna 9 é livre, entre os dois dígitos.

Explicado na parte 1.

5. O arquivo de saída​

O labirinto é gravado no arquivo de OUTPUT_FILE: um caractere por célula, uma linha do arquivo por linha da grade. Cada caractere é um dígito hexadecimal (de 0 a 9 e de a a f, ou seja, de 0 a 15) que diz quais das quatro paredes da célula estão fechadas. Depois da grade vêm uma linha vazia, a entrada, a saída e o caminho mais curto, escrito com as letras N, E, S e W (um passo para cima, para a direita, para baixo ou para a esquerda).

O enunciado fornece um programa, o maze_analyzer.py, que lê esse arquivo e confere se o labirinto cumpre as regras. Explicado na parte 1.

6. O desenho e o menu​

O programa mostra o labirinto na tela, com as paredes, a entrada, a saída e o caminho bem visíveis, no terminal (com caracteres e cores) ou numa janela gráfica. Enquanto ele roda, o usuário precisa poder:

  • gerar outro labirinto;
  • mostrar ou esconder o caminho mais curto;
  • trocar a cor das paredes.

Este projeto desenha no terminal. Explicado na parte 1.

7. O gerador reutilizável​

A geração do labirinto fica numa única classe (aqui, MazeGenerator), dentro de um módulo que outro projeto consegue importar. Esse módulo, com a documentação de uso, é distribuído como um pacote Python: um arquivo instalável com pip install, cujo nome começa com mazegen- (por exemplo, mazegen-1.0.0-py3-none-any.whl), guardado na raiz do repositório. O enunciado aceita dois formatos para esse arquivo, .whl ou .tar.gz; este projeto entrega os dois. O repositório também precisa ter tudo o que é necessário para reconstruir o pacote. Construído na parte 9.

8. Qualidade de código​

  • Python 3.10 ou mais novo.
  • O código passa no flake8, a ferramenta que confere o estilo (tamanho das linhas, espaços, imports sem uso), e no mypy, a ferramenta que confere os tipos. Para isso, toda função tem anotações de tipo, como em def soma(a: int, b: int) -> int:.
  • Toda função e classe tem uma docstring (o texto entre """ que documenta o que ela faz).
  • Erros são tratados com try/except, e arquivos são abertos com with.
  • Um Makefile (um arquivo de atalhos de comandos) com os atalhos obrigatórios install, run, debug, clean e lint, e o opcional lint-strict, que o enunciado recomenda fortemente e este projeto inclui.

Construído na parte 3.

9. O que mais entra no repositório​

  • README.md: a apresentação do projeto, com seções obrigatórias (descrição, instruções, formato do arquivo de opções, algoritmo escolhido e o porquê, o que é reutilizável, como a equipe trabalhou, como a IA foi usada). Construído na parte 15.
  • LICENSE.md: a licença que permite a outros projetos reutilizarem o gerador. Construído na parte 9.
  • .gitignore: a lista de arquivos gerados que não entram no repositório. Construído na parte 3.

10. Bônus​

Itens opcionais que valem nota extra. O deste projeto é o tabuleiro de Pac-Man sem nenhum beco sem saída. Explicado na parte 16.