Convierta una matriz booleana 2D en polígonos (rectilíneos)

8

Desafío

Escriba un programa que, dada una matriz booleana bidimensional (equivalentemente, un mapa de bits monocromático), emite una serie de polígonos que describen el esquema de la región que es "verdadero" (1).

La entrada se proporciona como una secuencia de caracteres '#'(hash), ' '(espacio) y \n(nueva línea). Las líneas pueden diferir en longitud, en cuyo caso se supone que las partes faltantes son espacios. La salida debe ser una lista de polígonos (separados por una nueva línea), cada polígono representado por una lista de coordenadas (separadas por comas).

Ejemplos y requisitos

  1. Las coordenadas deben estar listadas en el sentido de las agujas del reloj. Entrada:

    #
    

    Los resultados aceptables incluyen:

    (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. Las regiones disjuntas deben devolver múltiples polígonos. Entrada:

    # #
    

    Ejemplo de salida (la salida real debe constar de dos líneas):

    (0,0), (1,0), (1,1), (0,1)
    (2,0), (3,0), (3,1), (2,1)
    
  3. Los agujeros en un polígono se deben enumerar como un polígono separado, pero en orden antihorario. Entrada:

    ###
    # #
    ###
    

    Salida de ejemplo:

    (0,0), (3,0), (3,3), (0,3)
    (1,1), (1,2), (2,2), (2,1)
    
  4. Usted es libre de elegir si los vértices adyacentes en diagonal se unen o no. Entrada:

    #
     #
    

    Salida de ejemplo:

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

    o

    (0,0), (1,0), (1,1), (2,1), (2,2), (1,2), (1,1), (0, 1)
    
  5. Las listas de coordenadas no necesitan ser óptimamente cortas. Por ejemplo:

    ##
    

    Salidas aceptables:

    (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 de costumbre, el programa más corto "gana".

Timwi
fuente
1
¿Puedes explicar el corolario del punto 5? Lo encuentro altamente contradictorio.
Peter Taylor
si dijiste que en un cuadrado AB sobre CD entonces BC siempre están conectados y AD siempre están desconectados, eso sería consistente, pero parece que estás diciendo (en efecto) que los cuadrados no son realmente cuadrados y sus formas cambian sutilmente cuando convertir un # a un espacio o viceversa.
Peter Taylor
He agregado un par de etiquetas para ayudar a clasificar a esta bestia. [salida optimizada] tiene la intención de representar su condición "Las listas de coordenadas no necesitan ser óptimamente cortas" , y estaría feliz si alguien pudiera encontrar una mejor expresión para ello.
dmckee --- ex-gatito moderador
He relajado un poco las condiciones, por lo que si las cosas adyacentes en diagonal se unen o no ahora es opcional. También he eliminado mis comentarios defendiendo los viejos requisitos.
Timwi

Respuestas:

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}}

Limitaciones

Asume que la entrada no tiene más de 64 caracteres (pero su altura es en principio ilimitada). Esto puede extenderse a 512 u 8192 o cualquier otra potencia de dos cambiando las constantes relevantes, haciendo que el código solo sea un poco más largo.

Versión ligeramente más legible

Algo de crédito va a la mafia porque la primera línea es mi reutilización de su idea en los láseres . El resto es completamente mi trabajo.

# 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
    }
}

Ediciones

  • (345 → 312) Se dio cuenta de que no es necesario actualizar $xal tomar un turno porque la próxima iteración ya lo hará
  • (312 → 311) Cambiar 1= abajo, 2= izquierda a 1= izquierda, 2= abajo y actualizar a $dtravés de XOR en lugar de directamente
  • (311 → 265) Elimine la repetición de la expresión más interna y use matrices para parametrizarla
Timwi
fuente
2

Python, 607 caracteres

El código funciona manteniendo una lista de los límites descubiertos en la medida en que procesa # símbolos en orden de entrada. Los límites se almacenan en una tabla E que asigna bordes (4-tuplas de x_start, y_start, x_end, y_end) a una lista de bordes que forman el límite de un polígono. A medida que procesamos cada nuevo #, puede estar conectado a un polígono arriba (p) o a su izquierda (q). El código encuentra p y q usando E y luego combina el límite del # (r) actual con p y q, si existen. El caso donde p == q genera agujeros.

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
fuente