Transformar matriz booleana 2D em polígonos (retilíneos)

8

Desafio

Escreva um programa que, dado um array booleano bidimensional (equivalente a um bitmap monocromático), produz uma série de polígonos que descrevem o contorno da região que é "verdadeiro" (1).

A entrada é fornecida como uma sequência de caracteres '#'(hash), ' '(espaço) e \n(nova linha). As linhas podem diferir em comprimento; nesse caso, as partes ausentes são consideradas espaços. A saída deve ser uma lista (separada por nova linha) de polígonos, cada polígono representado por uma lista de coordenadas (separada por vírgula).

Exemplos e Requisitos

  1. As coordenadas devem ser listadas no sentido horário. Entrada:

    #
    

    Saídas aceitáveis ​​incluem:

    (0,0), (1,0), (1,1), (0,1)
    
    (1,0), (1,1), (0,1), (0,0)
    
    (1,1), (0,1), (0,0), (1,0)
    
    (0,1), (0,0), (1,0), (1,1)
    
  2. Regiões separadas devem retornar vários polígonos. Entrada:

    # #
    

    Exemplo de saída (a saída real deve consistir em duas linhas):

    (0,0), (1,0), (1,1), (0,1)
    (2,0), (3,0), (3,1), (2,1)
    
  3. Os furos em um polígono devem ser listados como um polígono separado, mas na ordem anti-horária. Entrada:

    ###
    # #
    ###
    

    Exemplo de saída:

    (0,0), (3,0), (3,3), (0,3)
    (1,1), (1,2), (2,2), (2,1)
    
  4. Você é livre para escolher se os vértices adjacentes na diagonal se juntam ou não. Entrada:

    #
     #
    

    Exemplo de saída:

    (0,0), (1,0), (1,1), (0,1)
    (1,1), (2,1), (2,2), (1,2)
    

    ou

    (0,0), (1,0), (1,1), (2,1), (2,2), (1,2), (1,1), (0, 1)
    
  5. As listas de coordenadas não precisam ser otimamente curtas. Por exemplo:

    ##
    

    Saídas aceitáveis:

    (0,0), (2,0), (2,1), (0,1)
    
    // Redundant coordinates along a straight line are acceptable
    (0,0), (1,0), (2,0), (2,1), (1,1), (0,1)
    
    // Duplicate start- and end-point are acceptable
    (0,0), (2,0), (2,1), (0,1), (0,0)
    

Como sempre, o programa mais curto “vence”.

Timwi
fonte
1
Você pode explicar o corolário do ponto 5? Eu acho isso altamente contra-intuitivo.
Peter Taylor
se você disse que em um quadrado AB sobre CD, BC sempre está conectado e o AD sempre desconectado, o que seria consistente, mas você parece estar dizendo (com efeito) que os quadrados não são realmente quadrados e suas formas mudam sutilmente quando você converta um # em um espaço ou vice-versa.
Peter Taylor
Adicionei algumas tags para ajudar a categorizar esta fera. [saída otimizada] pretende representar sua condição "As listas de coordenadas não precisam ser otimamente curtas" , e eu ficaria feliz se alguém pudesse encontrar uma expressão melhor para isso.
dmckee --- ex-moderador gatinho
Eu relaxei um pouco as condições, portanto, se as coisas adjacentes na diagonal se juntam ou não, agora é opcional. Também excluí meus comentários defendendo os requisitos antigos.
Timwi

Respostas:

3

Perl, 345 311 265 caracteres

s!.!$i{$x++}=$&eq'#'!ge,$x=$r+=64for<>;sub a{$d+=@_||3;$d%=4;$y=$x%64;$z=$x>>6;$c.=", ($y,$z)";}for$x(keys%i){if($c=!$v{$x}&$i{$x}&!$i{$x-1}){$i{($x+=(1,64)[$d^3])-(64,65,1)[$d]}?$i{$x-(65,1,0,64)[$d]}?a 1:($x-=(64,1)[$d]):a while$d||!$v{$x}++;print substr$c.$/,3}}

Limitações

Supõe que a entrada não tenha mais que 64 caracteres (mas sua altura é, em princípio, ilimitada). Isso pode ser estendido para 512 ou 8192 ou qualquer outra potência de duas, alterando as constantes relevantes, tornando o código apenas um pouco mais longo.

Versão um pouco mais legível

Algum crédito vai para a multidão porque a primeira linha é a minha reutilização de sua ideia em lasers . O resto é inteiramente meu trabalho.

# Read “%i”nput (this line is essentially mob’s idea, see above)
s!.! $i{$x++} = $& eq '#' !ge, $x = $r += 64 for <>;

# Subroutine to add a vertex to the current polygon and change the current “$d”irection
sub a
{
    $d += @_ || 3;
    $d %= 4;
    $y = $x % 64;
    $z = $x >> 6;
    $c .= ", ($y,$z)";
}

for $x (keys %i)
{
    # Go to the next “upward-pointing” edge that we haven’t already “%v”isited.
    if ($c = !$v{$x} & $i{$x} & !$i{$x-1})
    {
        # We’re going in the “$d”irection of 0=up, 1=left, 2=down, 3=right
        $i{($x += (1,64)[$d^3]) - (64,65,1)[$d]}
        ?  $i{$x - (65,1,0,64)[$d]}
           ?  a 1               # take a 90° turn to the left
           : ($x -= (64,1)[$d]) # move straight ahead
        : a                     # take a 90° turn to the right

        # Only stop if the current “$d”irection is “up” and we encounter a “%v”isited edge
        # (which necessarily is the one we started from)
        while $d || !$v{$x}++;

        # Remove “1, ” and output this polygon
        print substr $c.$/, 3
    }
}

Editar% s

  • (345 → 312) Percebi que não há necessidade de atualizar $xao dar uma volta, porque a próxima iteração já o fará
  • (312 → 311) Altere 1= para baixo, 2= da esquerda para 1= para a esquerda, 2= para baixo e atualize $dvia XOR em vez de diretamente
  • (311 → 265) Remova a repetição da expressão mais interna e use matrizes para parametrizá-la
Timwi
fonte
2

Python, 607 caracteres

O código funciona mantendo uma lista dos limites descobertos, na medida em que processa os símbolos # na ordem de entrada. Os limites são armazenados em uma tabela E que mapeia arestas (4-tuplas de x_start, y_start, x_end, y_end) para uma lista de arestas que formam o limite de um polígono. À medida que processamos cada novo #, ele pode ser conectado a um polígono acima (p) ou à esquerda (q). O código localiza p e q usando E e depois mescla os limites do atual # (r) com p e q, se eles existirem. O caso em que p == q gera furos.

import sys
x=y=N=0
E={}
def P(L):
 if L:print ', '.join(map(lambda z:str(z[:2]),L))
while 1:
 r,a,b,c=[(x,y,x+1,y),(x+1,y,x+1,y+1),(x+1,y+1,x,y+1),(x,y+1,x,y)],(x+1,y,x,y),(x,y,x,y\
+1),sys.stdin.read(1)
 if not c:break
 p,q=E.get(a),E.get(b)
 if'#'==c:
  if p:
   i=p.index(a)
   if q:
    j=q.index(b)
    if p==q:
     if i<j:P(p[i+1:j]);p[i:j+1]=r[1:3]
     else:P(p[i+1:]+p[:j]);p[i:]=r[1:3];p[0:j+1]=[]
    else:p[i:i+1]=r[1:3]+q[j+1:]+q[:j]
   else:p[i:i+1]=r[1:]
  elif q:j=q.index(b);q[j:j+1]=r[:3];p=q
  else:p=r
  for e in p:E[e]=p
 x+=1
 if'\n'==c:y+=1;x=0
for L in E.values():
 if L:P(L);L[:]=[]
Keith Randall
fonte