Verwandeln Sie ein boolesches 2D-Array in (geradlinige) Polygone

8

Herausforderung

Schreiben Sie ein Programm, das bei einem zweidimensionalen booleschen Array (äquivalent eine monochromatische Bitmap) eine Reihe von Polygonen ausgibt, die den Umriss des Bereichs beschreiben, der „wahr“ ist (1).

Die Eingabe erfolgt als Folge von '#'(Hash), ' '(Leerzeichen) und \n(Zeilenumbruch) Zeichen. Linien können unterschiedlich lang sein. In diesem Fall werden die fehlenden Teile als Leerzeichen angenommen. Die Ausgabe sollte eine (durch Zeilenumbrüche getrennte) Liste von Polygonen sein, wobei jedes Polygon durch eine (durch Kommas getrennte) Liste von Koordinaten dargestellt wird.

Beispiele & Anforderungen

  1. Die Koordinaten müssen im Uhrzeigersinn aufgelistet sein. Eingang:

    #
    

    Akzeptable Ausgaben umfassen:

    (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. Disjunkte Regionen müssen mehrere Polygone zurückgeben. Eingang:

    # #
    

    Beispielausgabe (die tatsächliche Ausgabe muss aus zwei Zeilen bestehen):

    (0,0), (1,0), (1,1), (0,1)
    (2,0), (3,0), (3,1), (2,1)
    
  3. Löcher in einem Polygon müssen als separates Polygon aufgeführt werden, jedoch gegen den Uhrzeigersinn. Eingang:

    ###
    # #
    ###
    

    Beispielausgabe:

    (0,0), (3,0), (3,3), (0,3)
    (1,1), (1,2), (2,2), (2,1)
    
  4. Sie können frei wählen, ob sich diagonal benachbarte Scheitelpunkte verbinden oder nicht. Eingang:

    #
     #
    

    Beispielausgabe:

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

    oder

    (0,0), (1,0), (1,1), (2,1), (2,2), (1,2), (1,1), (0, 1)
    
  5. Die Koordinatenlisten müssen nicht optimal kurz sein. Zum Beispiel:

    ##
    

    Akzeptable Ergebnisse:

    (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)
    

Wie üblich „gewinnt“ das kürzeste Programm.

Timwi
quelle
1
Können Sie die Konsequenz aus Punkt 5 erklären? Ich finde es sehr eingängig.
Peter Taylor
Wenn Sie sagten, dass in einem Quadrat AB über CD immer BC verbunden und AD immer getrennt sind, wäre dies konsistent, aber Sie scheinen (tatsächlich) zu sagen, dass die Quadrate keine echten Quadrate sind und ihre Formen sich subtil ändern, wenn Sie Konvertieren Sie ein # in ein Leerzeichen oder umgekehrt.
Peter Taylor
Ich habe ein paar Tags hinzugefügt, um dieses Biest zu kategorisieren. [optimierte Ausgabe] soll Ihre Bedingung "Die Koordinatenlisten müssen nicht optimal kurz sein" darstellen , und ich würde mich freuen, wenn jemand einen besseren Ausdruck dafür finden könnte.
dmckee --- Ex-Moderator Kätzchen
Ich habe die Bedingungen ein wenig gelockert, daher ist es jetzt optional, ob sich diagonal benachbarte Dinge verbinden oder nicht. Ich habe auch meine Kommentare gelöscht, um die alten Anforderungen zu verteidigen.
Timwi

Antworten:

3

Perl, 345 311 265 Zeichen

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

Einschränkungen

Angenommen, die Eingabe ist nicht breiter als 64 Zeichen (ihre Höhe ist jedoch im Prinzip unbegrenzt). Dies kann durch Ändern der relevanten Konstanten auf 512 oder 8192 oder eine andere Zweierpotenz erweitert werden, wodurch der Code nur geringfügig länger wird.

Etwas besser lesbare Version

Ein gewisser Kredit geht an Mob, weil die erste Zeile meine Wiederverwendung seiner Idee in Lasern ist . Der Rest ist ganz meine Arbeit.

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

Bearbeitungen

  • (345 → 312) Es wurde erkannt, dass $xbeim Abbiegen keine Aktualisierung erforderlich ist, da die nächste Iteration dies bereits ausführt
  • (312 → 311) Ändern Sie 1= down, 2= left to 1= left, 2= down und aktualisieren Sie $düber XOR anstatt direkt
  • (311 → 265) Entfernen Sie die Wiederholung des innersten Ausdrucks und verwenden Sie Arrays, um ihn zu parametrisieren
Timwi
quelle
2

Python, 607 Zeichen

Der Code führt eine Liste der erkannten Grenzen, sofern # Symbole in der Eingabereihenfolge verarbeitet werden. Die Grenzen werden in einer Tabelle E gespeichert, die Kanten (4-Tupel von x_start, y_start, x_end, y_end) einer Liste von Kanten zuordnet, die die Grenze eines Polygons bilden. Während wir jedes neue # verarbeiten, kann es mit einem Polygon über (p) oder links davon (q) verbunden sein. Der Code findet p und q mit E und führt dann die Grenze des aktuellen # (r) mit p und q zusammen, falls vorhanden. Der Fall, in dem p == q Löcher erzeugt.

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
quelle