4 pontos por GN⁺ 2024-04-25 | 1 comentários | Compartilhar no WhatsApp
  • Piet é uma linguagem de programação esotérica criada para fazer o código parecer arte abstrata, e seu nome vem de Piet Mondrian, pioneiro da arte abstrata geométrica
  • O programa é um gráfico feito com 20 cores reconhecidas, e o interpretador se move entre blocos de cor, interpretando a variação de cor como comandos
  • Todos os dados existem apenas como inteiros e são armazenados em uma pilha, e o tamanho dos blocos de cor é um valor, mas não vai automaticamente para a pilha sem o comando push
  • O controle de fluxo é definido pelo Direction Pointer e pelo Codel Chooser, além das regras para blocos pretos, bordas e blocos brancos, e alguns comportamentos podem variar entre implementações
  • Há exemplos e um ecossistema de ferramentas externas, mas não existe um interpretador oficial com autoridade, e o tratamento de erros e a interpretação de cores fora do padrão continuam dependentes da implementação

Ideia básica do Piet

  • Piet é uma linguagem de programação em que o código do programa parece arte abstrata
  • O nome vem de Piet Mondrian, pioneiro da arte abstrata geométrica
  • Queriam usar o nome Mondrian, mas como já existia uma linguagem de script com esse nome, passaram a usar Piet
  • Depois da escrita da especificação, surgiu uma pequena comunidade que vem criando programas, interpretadores, IDEs e compiladores
  • Não existe um interpretador oficial com autoridade, e as implementações disponíveis podem interpretar a especificação de formas ligeiramente diferentes
  • Alguns esclarecimentos foram adicionados à especificação, mas é possível que parte das implementações existentes não os siga

Cores e unidade de código

  • Piet usa ao todo 20 cores
    • 18 cores fazem parte do ciclo de matiz e do ciclo de luminosidade
    • Branco e preto não fazem parte desses dois ciclos
  • O ciclo de matiz segue a ordem red -> yellow -> green -> cyan -> blue -> magenta -> red
  • O ciclo de luminosidade segue a ordem light -> normal -> dark -> light
    • light também é considerado um passo além de dark, e o inverso também vale
  • Cores fora do padrão, como laranja ou marrom, podem ser usadas, mas seu efeito depende da implementação
    • No caso mais simples, cores fora do padrão são tratadas como branco
    • Outra possibilidade é serem tratadas como preto

Codel e blocos de cor

  • O código Piet é um gráfico composto por cores reconhecíveis
  • Como os pixels individuais do código têm significado na linguagem, em programas ampliados para facilitar a visualização o pixel único do código é chamado de codel
  • A unidade básica de execução é o bloco de cor
    • Um bloco de cor é uma região de codels da mesma cor conectados vertical ou horizontalmente
    • Blocos que se tocam apenas na diagonal não são considerados conectados
    • Um bloco de cor pode ter qualquer formato e pode conter buracos internos de outras cores
    • Buracos internos não fazem parte daquele bloco

Pilha e representação de valores

  • Piet armazena todos os valores de dados em uma pilha
  • Os valores de dados existem apenas como inteiros
    • Dependendo do comando, eles podem ser lidos ou exibidos como valores de caracteres Unicode
  • A pilha é conceitualmente infinita em profundidade, mas uma implementação pode impor um tamanho máximo finito para a pilha
  • Se ocorrer overflow em uma pilha finita, isso é um erro de tempo de execução, e o tratamento depende da implementação
  • Blocos de cor que não sejam pretos nem brancos representam um valor inteiro igual ao número de codels no bloco
    • Não é possível representar diretamente inteiros que não sejam positivos
    • Mas eles podem ser gerados por operadores
    • O valor de um bloco de cor não é automaticamente enviado para a pilha; é necessário um comando push explícito
  • O tamanho dos inteiros também é conceitualmente infinito, mas uma implementação pode impor um tamanho máximo finito
    • Overflow de inteiro é um erro de tempo de execução, e o tratamento depende da implementação

Fluxo de execução

  • O interpretador começa a executar no bloco de cor que contém o codel do canto superior esquerdo do programa
  • Durante a execução, dois estados são mantidos
    • Direction Pointer (DP): inicialmente aponta para a direita e pode apontar para direita, esquerda, baixo ou cima
    • Codel Chooser (CC): inicialmente aponta para a esquerda e pode apontar para a esquerda ou para a direita
  • O próximo destino de movimento é determinado pela borda do bloco de cor atual e pela combinação de DP e CC
    • Encontra-se a borda do bloco de cor atual mais distante na direção do DP
    • Nessa borda, escolhe-se o codel mais distante na direção do CC, tomando como referência a direção de avanço do DP
    • A partir desse codel, move-se para o bloco de cor ao qual pertence o próximo codel imediato na direção do DP
  • Esse processo se repete até que uma condição de término seja alcançada, encerrando o programa

Blocos pretos, bordas e blocos brancos

  • Blocos de cor pretos e a borda do programa funcionam como barreiras que bloqueiam o fluxo de execução
  • Se o interpretador tentar mover-se para um bloco preto ou sair da borda, ele para e alterna o CC
  • Se a segunda tentativa também falhar, o DP gira um passo no sentido horário
  • Alternando CC e DP, se depois de 8 tentativas ainda não for possível sair do bloco de cor atual, o programa é encerrado
  • Movimento em blocos brancos

    • Blocos de cor brancos são áreas livres pelas quais o interpretador passa sem impedimento
    • Ao mover-se de um bloco de cor para uma área branca, o interpretador segue em linha reta na direção do DP até alcançar um bloco de cor que não seja branco
    • Ao passar por um bloco branco e entrar em uma nova cor, nenhum comando é executado
    • Por essa característica, blocos brancos são úteis para escrever loops, pois permitem mudar a cor atual sem executar comandos
    • O movimento em blocos brancos não usa o procedimento de escolha de saída aplicado a blocos de cor não brancos; ele faz apenas movimento em linha reta
  • Quando há bloqueio em um bloco branco

    • Se, ao atravessar um bloco branco em linha reta, encontrar um bloco preto ou uma borda, isso é tratado como uma restrição
    • Nesse caso, o CC é alternado, mas como a posição de destino não muda, o DP gira imediatamente um passo no sentido horário
    • Em seguida, a partir do codel branco atual, o interpretador tenta novamente mover-se em linha reta na nova direção do DP
    • Cada vez que encontra uma restrição dentro de um bloco branco, repete a alternância do CC e a rotação do DP
    • Se entrar em um bloco de cor, a execução continua; se começar a refazer o caminho dentro do bloco branco, não há saída possível e a execução termina

Sistema de comandos

  • Os comandos de Piet são determinados pela mudança de cor ao passar de um bloco de cor para o seguinte
  • Quantos passos foram dados no ciclo de matiz e no ciclo de luminosidade define o comando
  • Em transições de cor feitas por meio de blocos brancos, nenhum comando é executado
  • Comandos de pilha e aritmética

    • push: coloca na pilha o valor do bloco de cor do qual acabou de sair
    • pop: remove e descarta o valor no topo da pilha
    • add: soma os dois valores do topo e coloca o resultado de volta na pilha
    • subtract: coloca na pilha o resultado de subtrair o valor do topo do segundo valor
    • multiply: multiplica os dois valores do topo
    • divide: faz a divisão inteira do segundo valor pelo valor do topo
    • Divisão por 0 é um erro dependente da implementação, e recomenda-se ignorar o comando
    • mod: coloca na pilha o resto da divisão do segundo valor pelo valor do topo
    • O resultado tem o mesmo sinal do divisor, isto é, do valor no topo da pilha
    • Se o valor do topo for 0, isso é erro de divisão por 0, e recomenda-se ignorar o comando
    • Para dividendos negativos, mod segue o mesmo comportamento da floored division descrita no verbete de modulus operation da Wikipedia
  • Comandos de comparação, ponteiro e entrada/saída

    • not: se o valor no topo da pilha não for 0, transforma-o em 0; se for 0, transforma-o em 1
    • greater: coloca 1 na pilha se o segundo valor for maior que o valor do topo; caso contrário, coloca 0
    • pointer: remove o valor do topo da pilha e gira o DP no sentido horário esse número de vezes
    • Se for negativo, gira no sentido anti-horário
    • switch: remove o valor do topo da pilha e alterna o CC esse número de vezes
    • Se for negativo, alterna o valor absoluto desse número de vezes
    • duplicate: coloca uma cópia do valor no topo da pilha
    • roll: remove os dois valores do topo e rotaciona parte da pilha restante de acordo com a profundidade e o número de rotações especificados
    • Se a profundidade for negativa, isso é um erro e o comando é ignorado
    • Um roll que exceda a profundidade máxima de pilha dependente da implementação é um erro dependente da implementação, e recomenda-se ignorar o comando
    • in: lê um número ou caractere da STDIN e o coloca na pilha
    • Se não houver entrada, ou se uma entrada inteira não fornecer um inteiro, isso é um erro e o comando é ignorado
    • out: escreve o valor no topo da pilha na STDOUT como número ou caractere
    • Operações que não podem ser executadas por falta de valores na pilha são ignoradas, e a execução segue para o próximo comando

Exemplos e ferramentas

1 comentários

 
GN⁺ 2024-04-25
Opiniões no Hacker News
  • O último programa da página de exemplos é realmente surpreendente: dizem que uma pessoa chamada Piet viu uma obra de arte que lembrava a linguagem Piet e tentou executá-la
    Ela rodou e talvez seja o primeiro caso da história em que um artista gráfico desenhou por acaso um programa de computador funcional
    https://www.dangermouse.net/esoteric/piet/samples.html
    https://gitlab.fabcity.hamburg/hofalab/piet-get-together

    • Se a condição de “funcional” for suficientemente flexível, já foi mostrado que a maioria dos respingos de tinta é um programa Perl válido
      https://www.mcmillen.dev/sigbovik/
    • Piet J. estava vendo uma obra de arte em uma pequena galeria e achou que ela parecia um programa Piet; o artista disse que não conhecia a linguagem
      Piet fotografou a obra, converteu-a em um arquivo de imagem organizado com cores próximas à paleta do Piet e, ao executá-la, ela realmente rodou; o código era um loop infinito que lia caracteres ASCII e imprimia seus respectivos valores numéricos ASCII
      Isso é realmente difícil de acreditar
    • O exemplo que calcula π também foi bom
      A explicação de que “naturalmente, usando um programa maior, é possível obter um valor mais preciso” me pareceu um tipo de piada que eu nunca tinha visto antes
    • Infelizmente, isso depende de uma diferença entre o npiet e a especificação atual do Piet
      Pela especificação, o interpretador deve começar a deslizar do codel branco atual na nova direção do DP e continuar até entrar em um bloco colorido ou encontrar outra restrição
      Mas o interpretador npiet olha para dentro do espaço em branco e depois retrocede para a posição do último codel colorido. Um dia quero colocar esse comportamento como opção no lexer do meu compilador Piet, mas ainda não mexi nisso
      Seguindo a especificação, esse programa vira um loop simples que não termina, porque os cantos extremos de quase todos os blocos ficam adjacentes ao branco. Escrever programas Piet complexos para vários interpretadores e compiladores é bem difícil, e todos têm diferenças sutis de interpretação que não são documentadas
      Acho que a saída do meu backend Piet, em geral, depende menos do interpretador, mas só investiguei a fundo uns três ou quatro outros interpretadores
      https://github.com/boothby/repiet/
    • Fico curioso para saber qual é a probabilidade de desenhos simples como esses, isto é, com alguns grandes blocos retangulares, serem programas válidos
      Folheando a documentação, por causa da condição de que “operações que não podem ser executadas, como uma operação que tenta desempilhar quando não há valores suficientes na pilha, são simplesmente ignoradas e a execução continua para o próximo comando”, parece que todas essas imagens poderiam executar sem erros
      Mas quantas dessas imagens aleatórias de fato fazem algo “significativo” é outra questão
  • Piet é um experimento marcante entre as linguagens de programação esotéricas, mas acho que não chega ao objetivo de fazer um programa parecer uma pintura de Mondrian, a menos que o desenvolvedor realmente tenha essa intenção
    Seria bom se a própria estrutura da linguagem fosse projetada para que, não importa o que se “escrevesse”, parecesse uma pintura de Mondrian

    • É verdade, mas Mondrian, na prática, usava principalmente apenas cores primárias, então teria sido algo bastante limitado
  • Sempre me vem à cabeça esta pergunta: qual é a aparência de um algoritmo?
    Será que dá para criar, no mundo real, algo parecido com o que aparece no romance The Glass Bead Game, de Herman Hesse? O título original é Magister Ludi
    Como uma pessoa orientada ao visual, quero acreditar que sim, e de fato já experimentei ferramentas desse tipo
    https://community.carbide3d.com/uploads/default/original/3X/5/b/5b0872a5666fec9b7bb6fd623c431de03263372d.jpeg
    Mas, sem uma resposta clara para a pergunta acima, essas ferramentas sempre correm o risco de acabar virando algo assim
    https://blueprintsfromhell.tumblr.com/
    https://scriptsofanotherdimension.tumblr.com/
    Também é difícil equilibrar expressividade visual e modularidade; se você força a modularidade, acaba voltando com facilidade demais para a barreira do texto que queria evitar

    • Escrever Piet à mão tem a graça de explorar “qual é a aparência de um algoritmo”
      Sergei Lewis e eu criamos, cada um, ferramentas que geram código Piet. O assembler do Sergei produz código muito mais bonito que o meu backend de Piet
      No output do meu compilador, o que dá para ver de fato é basicamente que eu usei trampolim de um jeito bem preguiçoso
      http://www.toothycat.net/wiki/wiki.pl?MoonShadow/Piet
      https://github.com/boothby/repiet/
      https://en.wikipedia.org/wiki/Trampoline_(computing)
    • Acredito que qualquer algoritmo, e até qualquer conceito mental, tenha uma relação 1:1 com uma representação visual
      Foi uma ideia que tive lendo um livro de Steven Pinker: palavras abstratas podem ser decompostas em palavras mais simples e, no fim, acabam descrevendo alguma relação espacial. Por exemplo, “rekindle” pode ser visto como “trazer duas coisas juntas de novo”
      De modo parecido, um loop for também é o conceito mental de “uma coisa passando por cima de várias outras”, e isso tem uma representação visual como “100” -> “010” -> “001”
      Então fico curioso se daria para criar uma linguagem que defina esses componentes como transformações visuais puras
    • Para um programa simples, também dá para imaginar implementar uma máquina de Turing em que os símbolos sejam cores
  • Isso combinaria com uma cena de thriller policial em que aparece algo assim para travar o protagonista ou os investigadores, até alguém perceber que aquilo é código
    E eu achando que só QR code era útil

  • Alguém fez um quine em Piet: http://mamememo.blogspot.com/2009/10/piet-quine.html?m=1
    A imagem do post está quebrada, mas há uma cópia aqui: https://codegolf.stackexchange.com/a/23255/103045

  • O momento em que você descobre Piet é especial, uma mistura de reverência, confusão e surpresa
    No meu caso, ficou registrado nesta conversa do podcast de ciência da computação “The CS Primer Show”, com meu amigo Oz: https://show.csprimer.com/episodes/e2-dont-let-a-gpt-have-all-the-fun

  • Na faculdade, havia uma pequena disciplina sobre linguagens de programação esotéricas
    Cada um tinha que escolher uma linguagem como Brainfuck, Piet etc. e brincar com ela; eu escolhi Piet e me diverti bastante
    Para ser sincero, o pequeno app de exemplo que fiz não era lá muito bom esteticamente, e acho que, para criar arte com Piet, é preciso virar especialista em Piet

  • A página de exemplos é excelente
    Dá para ver a tela evoluindo e ficando cada vez mais sofisticada e bonita
    https://www.dangermouse.net/esoteric/piet/samples.html

  • “light é considerado um nível mais escuro que dark” — isso é bem profundo

  • Seria legal conseguir criar um autoencoder que aprendesse a receber código em Python ou em outra linguagem menos esotérica e gerar Piet como saída
    Aí talvez desse até para gerar algoritmos aleatórios, meio como o Stable Diffusion