Zincirdeki Bir Köpek


31

Tavan penceremden komşumun bahçesine bakıyorum. Avlunun ortasındaki bir direk için zincirlenmiş köpekleri var. Köpek bahçenin etrafında koşar ama her zaman zincirinin ucundadır, bu yüzden kir içinde bir iz bırakarak sona erer. Normalde bu iz mükemmel bir şekilde dairesel olur, ancak komşumların bahçelerinde köpeğin zincirinin tuttuğu başka kutuplar vardır. Köpekler zinciri bir direğe her çarptığında, köpek yarıçapı olarak kalan zincir uzunluğu ile yeni direğin etrafında dönmeye başlar. Kutuplar, köpek ve zincirin hepsi sıfır genişliğe sahip olduğundan (komşularım matematikçilerdir) zincir, dairenin yarıçapı yarıçapı olmadan süresiz olarak bir kutup etrafına sarılabilir. Zincir yolunda ise köpek aynı zamanda zincirden (sadece yakasından değil) geçebilir. Bu tuhaflığı bir süre gözlemledikten sonra komşumun köpeğini simüle etmek için bazı kodlar yazacağım. Kod, köpeğin bağlandığı bir orta direğin konumlarını, komşu bahçemdeki diğer kutupların yerlerini, zincirin uzunluğunu ve köpeğin başlangıç ​​konumunu alacaktır ve köpeğin çimleri yıprattığı yol. Aşağıdakilerden herhangi birinin bir kombinasyonunun sabit olduğunu varsayabilir (ve bu nedenle bunları girdi olarak almazsınız):

  • Köpeğin bağlandığı direğin yeri

  • Zincirinin uzunluğu

  • Köpeğin başlangıç ​​yeri

Güneş doğuyor, bu yüzden pencerenin aydınlattığı çatı katımın katındaki alan küçülüyor, kodumu yazmak için bana daha az ve daha az yer veriyor. Lütfen, kodunuzun bayt sayısını en aza indirmeye çalışın, böylece çatı katımın üzerinde taslak oluşturacak yerim olsun

Test durumları

Burada, köpeğin, bulunduğu direğin (kırmızı nokta) hangi direkten 3 birim güneyde başladığını tahmin ediyorum 0,0. Kutupların netlik için noktalarla nerede olduklarını belirttim, çıktılarınıza dahil etmenize gerek yok.

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


Çıktı ne için {0,-.5}?
Kritixi Lithos

@KritixiLithos En {0,.5}büyük daire olmadan dikey olarak döndürülmüş çıktı . Köpek esas olarak ikinci direğe takılmaya başlar.
Buğday Sihirbazı

Kayan nokta sorunlarının bir sonucu olarak programım son test senaryosunda (1,1) civarında bir daire çiziyor (dize uzunluğu 99.99999). Bu iyi mi?
Kritixi Lithos

Köpek hem saat yönünde hem de saat yönünün tersinde çalışır, ancak sabit bir noktadan?
user202729

3
Sadece bunun için 1 "Güneş pencere tarafından aydınlatılan benim tavan zeminine alanı yükseliyor küçülen benim kod yazmak için daha az boşluk bana veriyor"
Leo

Yanıtlar:


11

Matplotlib kullanarak Python 3, 457 bayt

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

Komşularınız matematikçi olduklarından, komşunuzun bahçesinin karmaşık alanı işgal ettiğini ve bahçedeki nesnelerin koordinatlarının karmaşık sayılar olduğunu varsaydım. Bu işlevi kullanmak için, komşunuzun bahçesindeki kutupların yerlerini belirten karmaşık sayıların bir listesini iletmelisiniz. Varsayılan koordinat sistemi gösterimi seçildi, sağdaki pozitif gerçek sayılar ve yukarı doğru pozitif hayali sayılar. Bu örnekler olur:

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

Ayrıca, program aşağıdakileri varsaymaktadır: tasma 0 noktasına bağlanır, tasma 3 birim uzunluğundadır ve arsa alanı 10'a 10 civarında 0'dır. Bu parametreler için sonuçlar tam olarak örneklerle eşleşir, ve bu, sonucun nasıl göründüğüdür (nihai örnek için):

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

Algoritma oldukça basittir, sadece saat yönünde ve saat yönünün tersine aramayı ayırt etmek için bir koşul gerektirir. Algoritmanın durumu, geçerli dönme noktası ve o anki dönme noktasına çarptığında tasmanın oryantasyonu / kalan uzunluğu ile tanımlanır. Aşağıdaki gibi çalışır:

  • Geçerli dönüş noktasından, kalan tasma uzunluğundan daha uzakta olan çarpışma setindeki noktaları ve mevcut dönüş noktasını filtreleyin.
  • Bu set boşsa, bu kolun sonuna gelindiğinde, bu nokta etrafında kalan kayış uzunluğunun yarıçapı ile bir daire çizin.
  • Fark vektörü ile tasma yönü arasındaki faz farkının minimum / maksimal olduğu noktayı belirleyin. Bu, kira kontratının sırasıyla saat yönünde / saat yönünün tersine çarpacağı bir sonraki nokta.
  • Bu vektörlere göre yay çizin, tasma uzunluğunu alın, mesafenin büyüklüğünü çıkarın ve tasma oryantasyonunu fark vektörünün oryantasyonuna ayarlayın. Dönme noktasını güncelle ve en baştan devam et.

Bu algoritma daha sonra ilk önce saat yönünde gerçekleştirilir, daha sonra durum sıfırlanır ve saat yönünün tersine uygulanır. Algoritmanın basitliği, program bayt sayısının yaklaşık yarısının çizim fonksiyonlarına harcanması anlamına gelir. Çizim rutinleri çıkarıldıysa program boyutundan 218 bayt çıkarırdı.

Aşağıdakiler, aynı zamanda noktaları ve tasma çarpmalarını da gösteren hata ayıklama kodunu da içeren, eski bir versiyondur:

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

Ürettiği çıktı şöyle görünür:

Önceki görüntüyle aynı, ancak daha fazla satır


Gerçekten harika bir açıklama için +1 ve beni neredeyse iki kat daha fazla golf oynadı! <s> Tanrım, bu
yapıtaşları

7

İşleme 3, 815 833 835 876 879 bayt

Gereksiz parantezleri kaldırarak @ ZacharyT sayesinde iki bayt kurtardı

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

Bu programı şöyle çalıştırın:

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

(fonksiyon a salır float[][]). Bu, esasen 3 numaralı test çantasıdır, ancak pencereye sığması için 100 ile çarpılır.

Dikkat edilecek birkaç şey:

  • program kutup çizmez
  • görüntüler ters çevrilmiş görünüyor, çünkü İşleme'nin koordinat sisteminde, pozitif y ekseni aşağı iniyor
  • İşleme yüzer kullanır, hesaplamalar çok doğru değildir, bu yüzden görüntülerde bunu görebilirsiniz. OP'ye bu kayan nokta hatasının önemli olup olmadığını sordum.
  • pencerenin boyutu 600 piksele 600 pikseldir
  • Çok küçük giriş koordinatları programı etkileyecektir, çünkü yığın pushMatrix()ve popMatrix()çalışma sadece 32 matrisi tutabilir.
  • köpek (0, -300) ile başlar ve zincir 300 piksel uzunluğunda başlar
  • Aşağıdaki resimler kolaylık sağlamak için küçültülmüş

Yukarıdaki test çantası için örnek çıktı.

görüntü tanımını buraya girin

Hazırlanan çıktıyı görmek istiyorsanız, translate(w,w);in fonksiyonundan hemen sonra bu satırı ekleyin 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);

Ve bu bize şu sonucu veriyor:

daire

Ungolfed f()ve açıklama

(ayrıca hata ayıklama kodunu içerir)

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

Kısaca, program iki tane "gönderen" gönderir, biri saat yönünün tersine, diğeri saat yönünde gider. Bu arayışçıların her biri en yakın direği bulur ve zincir yeterince uzunsa, bir daire çizse bile, ona bir yay çizer. Bir yay çizdiğinde, bu direğe başka bir arayıcı gönderir ve süreç devam eder. f()Her arayıcının sürecini içerir. Bunu daha fazla golf oynadığım anda daha ayrıntılı bir açıklama gelecek.


En son etraftaki parenlere ihtiyacınız var L-dmı?
Zacharý

@ ZacharyT Bunu nasıl kaçırdığımı bilmiyorum, teşekkürler.
Kritixi Lithos

5

LOGO, 305 298 297 293 bayt

FMSLogo'daki kodu deneyin.

Bir fonksiyon tanımlama draw(şekilde golfed d) (örneğin koordinat kutbunun bir listesi olarak, belirli bir girdi draw [[0 100] [100 100] [-200 100] [-100 -50][0 0]], ekran sonucu çekecektir.

Gereksinimler:

  1. İlk ip uzunluğu = 300 piksel. (3 piksel çok küçük olduğu için)
  2. [0 0]kutup listesine dahil edilmelidir. Hata ayıklama kodu (çizim kutupları) açıksa [0 0], son öğe olmalıdır.
  3. Köpek koordinatta başlar x=0, y=-300(problem tanımındaki gibi)

Mümkün iyileştirmeler:

  1. İstisnai bir durumun (köpeğin bir direğe çarpması) -1 >=ile değiştirilmesi gerekliyse matematiksel olarak doğru olması gerekmez.>

Golf kodu:

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

Ungolfed kod ( ;satır içi bir yorum başlatır (açıklama için kullanılır) ve :bir değişken adı başlatır):

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 bayt

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

Betik, stdin'den gelen noktaların listesini karmaşık sayıların bir listesi olarak okur.

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

görüntü tanımını buraya girin

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

görüntü tanımını buraya girin

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.