Un cane su una catena


31

Sto guardando fuori dalla finestra della mia soffitta nel cortile del mio vicino. Hanno un cane incatenato a un palo nel centro del cortile. Il cane corre intorno al cortile ma è sempre alla fine della sua catena, quindi finisce per lasciare una traccia nella terra. Normalmente questa pista sarebbe perfettamente circolare, ma i miei vicini hanno alcuni altri pali nel loro cortile su cui la catena del cane viene catturata. Ogni volta che la catena del cane colpisce un palo, il cane inizia a ruotare attorno al nuovo palo con la lunghezza della catena che rimane nel raggio. Poiché i poli, il cane e la catena hanno tutti una larghezza pari a zero (i miei vicini sono matematici), la catena può avvolgere indefinitamente un palo senza accorciare il raggio del cerchio. Il cane può anche passare attraverso la catena (ma non il colletto) se la catena è sul suo cammino. Dopo aver osservato questa stranezza per un po 'decido che scriverò un codice per simulare il cane del mio vicino. Il codice prenderà le posizioni di un palo centrale, a cui è incatenato il cane, le posizioni degli altri poli nel cortile dei miei vicini, la lunghezza della catena e la posizione iniziale del cane, e produrrà un diagramma che indica il percorso in cui il cane ha logorato l'erba. Si può presumere che qualsiasi combinazione di quanto segue sia costante (e quindi non considerarli come input):

  • Posizione del palo a cui è incatenato il cane

  • Lunghezza della catena

  • Posizione di partenza del cane

Il sole sta sorgendo, quindi lo spazio sul pavimento della mia soffitta illuminato dalla finestra si sta riducendo, dandomi sempre meno spazio per scrivere il mio codice. Prova a ridurre al minimo il numero di byte del tuo codice in modo che io abbia spazio per disegnarlo sul mio piano attico.

Casi test

Qui suppongo che il cane inizi 3 unità a sud dal cui polo è incatenato (il punto rosso), situato a 0,0. Ho indicato dove sono i poli con punti per maggiore chiarezza, non è necessario includerli nell'output.

Poles at 1,2 -1,2

Test 1

Poles at 0,.5

Test 2

Poles at 0,1 1,1 -2,1 -1,-.5

Test 3

Poles at 0,1 1,1

Test 4


A cosa serve l'output {0,-.5}?
Kritixi Lithos,

@KritixiLithos È l'output di {0,.5}capovolto verticalmente senza il cerchio più grande. Il cane inizia essenzialmente catturato sul secondo palo.
Wheat Wizard

A causa di problemi in virgola mobile, il mio programma traccia un cerchio intorno (1,1) nell'ultima testcase (la lunghezza della stringa è 99.99999). Va bene?
Kritixi Lithos,

Il cane corre sia in senso orario che antiorario, ma da un punto fisso?
user202729

3
"Il sole sta sorgendo lo spazio sul pavimento della mia soffitta illuminato dalla finestra si sta restringendo dandomi sempre meno spazio per scrivere il mio codice" +1 solo per questo
Leo

Risposte:


11

Python 3 usando matplotlib, 457 byte

from cmath import*
from matplotlib import pyplot as g,patches as i
def x(p):
 p+=[0];d=180/pi;a=2;h=g.gca();h.set_xlim(-5,5);h.set_ylim(-5,5)
 while a:
  a-=1;c=0;y=3;z=-pi/2
  while 1:
   s=[n for n in p if abs(n-c)<=y and n!=c]
   if not s:h.add_patch(i.Arc((c.real,c.imag),y*2,y*2));break
   n=[max,min][a](s,key=lambda n:(z-phase(n-c))%(2*pi));l,r=polar(n-c);h.add_patch(i.Arc((c.real,c.imag),y*2,y*2,[z,r][a]*d,0,[r-z,z-r][a]*d));y-=l;z=r;c=n
 g.show()

Poiché i tuoi vicini sono matematici, ho supposto che il giardino del tuo vicino occupi il dominio complesso e che qualsiasi coordinata di oggetti nel giardino sia quindi un numero complesso. Per usare questa funzione, dovresti quindi passargli un elenco di numeri complessi che indicano le posizioni dei poli nel giardino del tuo vicino. È stata scelta la rappresentazione predefinita del sistema di coordinate, dove a destra sono numeri reali positivi e verso l'alto sono numeri immaginari positivi. Ciò significa che gli esempi diventano:

x([2j+1,2j-1])
x([.5j])
x([1j,1+1j,-2+1j,-1-.5j])
x([1j,1+1j])

Inoltre, il programma assume le seguenti cose: il guinzaglio è legato al punto 0, il guinzaglio è lungo 3 unità e l'area del diagramma è 10 per 10 centrata attorno a 0. Per questi parametri, i risultati corrispondono esattamente agli esempi, e questo è come appare il risultato (per l'esempio finale):

x ([1j, 1 + 1j])

L'algoritmo è abbastanza semplice e richiede solo una condizione per differenziare la ricerca in senso orario e antiorario. Lo stato dell'algoritmo è definito dal punto di rotazione corrente e dall'orientamento / lunghezza rimanente del guinzaglio quando colpisce il punto di rotazione corrente. Funziona come segue:

  • Filtrare i punti dal set di collisione che sono più lontani dal punto di rotazione corrente rispetto alla lunghezza del guinzaglio rimanente, nonché dal punto di rotazione corrente.
  • Se questo set è vuoto, traccia un cerchio con il raggio della lunghezza della cintura rimanente attorno a questo punto quando è stata raggiunta la fine di questo braccio.
  • Determinare il punto in cui la differenza di fase tra il vettore di differenza e l'orientamento del guinzaglio è minima / massima. Questo è il punto successivo in cui il guinzaglio colpirà rispettivamente in senso orario / antiorario.
  • Disegna l'arco in base a questi vettori, prendi la lunghezza del guinzaglio, sottrai l'intensità della distanza e imposta l'orientamento del guinzaglio sull'orientamento del vettore differenza. Aggiorna il punto di rotazione e continua dall'inizio.

Questo algoritmo viene quindi eseguito prima in senso orario, dopodiché lo stato viene resettato e viene eseguito in senso antiorario. La semplicità dell'algoritmo significa che circa la metà del programma viene utilizzato per le funzioni di disegno. Se le routine di disegno fossero eliminate, rimuoverebbero 218 byte dalla dimensione del programma.

Quella che segue è una versione non golfata che contiene anche codice di debug, che mostra anche punti e collisioni al guinzaglio:

from cmath import pi, rect, polar, phase
from matplotlib import pyplot, patches
def x_ungolfed(points):
    degrees = 180/pi # conversions

    # add the center point to the collision points
    points.append(0.0)

    # configure plot area
    axes=pyplot.gca()
    axes.set_xlim(-5,5)
    axes.set_ylim(-5,5)

    # plot the points
    x, y =zip(*((p.real, p.imag) for p in points))
    axes.scatter(x, y, 50, "b")

    # first iteration is clockwise, second counterclockwise
    clockwise = 2
    while clockwise:
        clockwise -= 1

        # initial conditions
        center = 0 + 0j;
        leash_size = 3
        leash_angle = -pi / 2

        # initial leash plot
        leash_start = rect(leash_size, leash_angle)
        axes.plot([center.real, leash_start.real], [center.imag, leash_start.imag], "r")

        # search loop
        while 1:
            # find possible collission candidates
            candidates = [n for n in points if abs(n - center) <= leash_size and n != center]
            # if we reached the end, draw a circle
            if not candidates:
                axes.add_patch(patches.Arc(
                    (center.real, center.imag), 
                    leash_size*2, leash_size*2
                ))
                break
            # find the actual collision by comparing the phase difference of the leash angle vs the difference between the candidate and the current node
            new = (min if clockwise else max)(candidates, key=lambda n: (leash_angle - phase(n - center)) % (2 * pi))

            # convert the difference to polar coordinates
            distance, new_angle = polar(new - center)
            # draw the arc
            if clockwise:
                axes.add_patch(patches.Arc(
                    (center.real, center.imag),
                    leash_size * 2, leash_size * 2,
                    new_angle * degrees,
                    0,
                    (leash_angle-new_angle) * degrees
                ))
            else:
                axes.add_patch(patches.Arc(
                    (center.real, center.imag),
                    leash_size * 2, leash_size * 2,
                    leash_angle * degrees,
                    0,
                    (new_angle - leash_angle) * degrees
                ))
            # draw intermediate lines
            edge = rect(leash_size, new_angle) + center
            axes.plot([center.real, edge.real], [center.imag, edge.imag], "g")

            # perform updates: decrease remaining leash size, set new leash angle, move rotation center to the collision
            leash_size -= distance
            leash_angle = new_angle
            center = new

    # show the graph
    pyplot.show()

L'output che produce è simile al seguente:

Come l'immagine precedente ma più linee


+1 per una spiegazione davvero fantastica e per avermi giocato a golf di quasi il doppio! <s> Accidenti, invidio quei builtin </s>
Kritixi Lithos,

7

Elaborazione 3, 815 833 835 876 879 byte

Salvato due byte grazie a @ZacharyT rimuovendo le parentesi non necessarie

void settings(){size(600,600);}int i,w,x,n;float l,d,t,a,f,g,m,R,U;float[][]N,T;float[]S,p;void s(float[][]t){N=new float[t.length+1][2];N[0][0]=N[0][1]=i=0;for(float[]q:t)N[++i]=q;translate(w=300,w);noFill();pushMatrix();f(N,0,-w,w,1,0);popMatrix();f(N,0,-w,w,0,0);}float p(float a,float b){for(a+=PI*4;a>b;)a-=PI*2;return a;}void f(float[][]P,float x,float y,float L,int c,int I){l=2*PI;d=i=0;S=null;for(;i<P.length;i++){float[]p=P[i];g=atan2(y,x);m=atan2(p[1],p[0]);if(p(f=(c*2-1)*(g-m),0)<l&(t=dist(0,0,p[0],p[1]))<=L&I!=i){l=p(f,0);S=new float[]{g,m};d=t;n=i;}}if(S==null)ellipse(0,0,2*(L-d),2*(L-d));else{arc(0,0,L*2,L*2,p(S[c],S[1-c]),S[1-c]);R=cos(a=S[1]);U=sin(a);translate(d*R,d*U);T=new float[P.length][2];for(int i=0;i<T.length;T[i][1]=P[i][1]-d*U,i++)T[i][0]=P[i][0]-d*R;f(T,(L-d)*R,(L-d)*U,L-d,c,n);}}

Esegui questo programma in questo modo:

void setup() {
    s(new float[][]{{0,100},{100,100},{-200,100},{-100,-50}});
}

(la funzione saccetta a float[][]). Questo è essenzialmente testcase n. 3, ma moltiplicato per 100 per adattarsi alla finestra.

Diverse cose da notare:

  • il programma NON disegna i poli
  • le immagini appaiono capovolte perché nel sistema di coordinate di Processing l'asse y positivo scende
  • poiché Processing utilizza float, i calcoli non sono molto precisi, quindi potresti vederlo nelle immagini. Ho chiesto all'OP se questi errori in virgola mobile sono importanti.
  • la dimensione della finestra è di 600 pixel per 600 pixel
  • coordinate di input molto piccole interromperanno il programma perché lo stack pushMatrix()e il popMatrix()funzionamento su possono contenere solo 32 matrici.
  • il cane inizia da (0, -300) e la catena inizia da 300 pixel di lunghezza
  • le immagini sottostanti sono state minimizzate per comodità

Esempio di output per il testcase sopra.

inserisci qui la descrizione dell'immagine

Se si desidera visualizzare l'output predefinito, aggiungere questa riga subito dopo la translate(w,w);funzione in s.

background(-1);scale(1,-1);fill(255,0,0);ellipse(0,0,25,25);fill(0);for(float[]q:N)ellipse(q[0],q[1],25,25);

E questo ci dà questo risultato:

cerchio

Ungolfed f()e spiegazione

(contiene anche codice di debug)

void f(float[][]points, float x, float y, float len, int c, int pindex) {
    print(asd+++")");
    float closest = 2*PI;
    float d=0,t;
    float[]stuff = null;
    int index = 0;
    for(int i=0;i<points.length;i++) {
        if(pindex != i) {
            float[]p = points[i];
            float originAngle = atan2(y, x);
            float tempAngle = atan2(p[1], p[0]);
            //println(x,y,p[0],p[1]);
            float diff = c<1?tempAngle-originAngle:originAngle-tempAngle;
            println("@\t"+i+"; x=\t"+x+"; y=\t"+y+"; tx=\t"+p[0]+"; ty=\t",p[1], diff, originAngle, tempAngle);
            if(p(diff) < closest && (t=dist(0,0,p[0],p[1])) < len) {
                println("+1");
                closest = p(diff);
                stuff = new float[]{originAngle, tempAngle};
                d=t;
                index = i;
            }
        }
    }
    if(stuff == null) {
        ellipse(0,0,2*(len-d),2*(len-d));
        println("mayday");
    } else {
        println("d angles",d,p(stuff[c],stuff[1-c],c), stuff[1-c]);
        //println(points[0]);
        arc(0, 0, len*2, len*2, p(stuff[c],stuff[1-c],c), stuff[1-c]);
        float angle = stuff[1];
        translate(d*cos(angle), d*sin(angle));
        println("Translated", d*cos(angle), d*sin(angle));
        println("angle",angle);
        float[][]temp=new float[points.length][2];
        for(int i=0;i<temp.length;i++){
            temp[i][0]=points[i][0]-d*cos(angle);
            temp[i][1]=points[i][1]-d*sin(angle);
            println(temp[i]);
        }
        println(d*sin(angle));
        pushMatrix();
        println();
        f(temp, (len-d)*cos(angle), (len-d)*sin(angle), (len-d), c, index);
        popMatrix();
        //f(temp, (len-d)*cos(angle), (len-d)*sin(angle), (len-d), 0, index);
    }
}

Per dirla in breve, il programma invia due "cercatori", uno va in senso antiorario e l'altro in senso orario. Ognuno di questi cercatori trova il polo più vicino e disegna un arco su di esso se la catena è abbastanza lunga, altri saggi disegna un cerchio. Una volta che disegna un arco, invia un altro cercatore a quel polo e il processo continua. f()contiene il processo di ciascun ricercatore. Una spiegazione più dettagliata arriverà non appena avrò più questo golf.


Hai bisogno dei genitori intorno all'ultimo L-d?
Zacharý,

@ZacharyT Non so come mi sia perso, grazie.
Kritixi Lithos,

5

LOGO, 305 298 297 293 byte

Prova il codice su FMSLogo.

Definire una funzione draw(come golf d) che, dato input come un elenco di coordinate polari (ad esempio draw [[0 100] [100 100] [-200 100] [-100 -50][0 0]], disegnerà sullo schermo il risultato.

Requisiti:

  1. Lunghezza iniziale della corda = 300 pixel. (poiché 3 pixel sono troppo piccoli)
  2. [0 0]deve essere incluso nella lista dei pali. Se il codice di debug (disegna i poli) è attivato, allora [0 0]deve essere l'ultimo elemento.
  3. Il cane inizia alle coordinate x=0, y=-300(come nella descrizione del problema)

Possibili ottimizzazioni:

  1. -1 byte se il caso eccezionale (il cane si imbatte in un palo) non deve essere matematicamente corretto sostituendolo >=con>

Codice golfizzato:

to f
op(if ?=pos 360 modulo :m*(180+heading-towards ?)360)
end
to x :m[:1 300]
home
forever[make 2 filter[:1>=u ?](sort :p[(u ?)<u ?2])invoke[pd
arc -:m*f :1
pu
if 360=f[stop]make 1 :1-u ?
lt :m*f
setpos ?]reduce[if f<invoke[f]?2[?][?2]]:2]
end
to d :p
copydef "u "distance
foreach[1 -1]"x
end

Codice non golfato ( ;avvia un commento incorporato (utilizzato per la spiegazione) e :avvia un nome di variabile):

to f
    op ifelse ? = pos 360 modulo :m*(180 + heading - towards ?) 360
end

to x
    home
    foreach :poles [pu setpos ? pd circle 5] ; debug code
    make "length 300 ; initial length of rope
    forever [
        make "tmp filter [:length >= distance ?] ; floating point error makes > and >= similar,  ~
            ; but >= is correct mathematically ~
            (sort :poles [(distance ?) < distance ?2])
         ; the last = longest element will be rotated
        invoke [
            pd
            arc -:m*f :length
            pu
            if 360=f [stop]
            make "length :length - distance ?
            lt :m*f
            setpos ?
        ] reduce [
            if f < invoke[f]?2 [?] [?2]
        ] :tmp ; apply to use ? instead of :pos
    ]
end

to draw :poles
    foreach [1 -1] [[m]
        x
    ]
end

1

Python 2 + PIL, 310 byte

from PIL import Image
from cmath import*
I,_,X,P=Image.new('1',(300,300),'white'),abs,polar,input()
def r(s):
 a,C,l=0,0,3
 while _(a)<99:
  c=C+l*exp(1j*a);I.load()[c.real*30+150,150-c.imag*30]=0
  for p in P+[0]:
   N,E=X(C-c);n,e=X(C-p)
   if n<=N and _(E-e)<.1:l-=_(p-C);C=p
  a+=s
r(.01)
r(-.01)
I.show()

Lo script legge l'elenco di punti da stdin come un elenco di numeri complessi.

printf '[complex(0,0.5)]' | python2 snippet.py

inserisci qui la descrizione dell'immagine

printf '[complex(0,1), complex(1,1)]' | python2 snippet.py

inserisci qui la descrizione dell'immagine

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.