Собака на ланцюжку


31

Я дивлюся через горищне вікно у двір мого сусіда. У них собака прикута до посту в центрі двору. Собака бігає по двору, але завжди знаходиться на кінці свого ланцюга, тому закінчується, залишаючи слід у бруді. Зазвичай ця доріжка була б ідеально круглою, але у моїх сусідів є кілька інших жердин у своєму дворі, на які зациклюється собачий ланцюг. Щоразу, коли ланцюг собак ударяється по полюсу, собака починає обертатися навколо нового полюса, якою б довжиною ланцюга не залишалося його радіусом. Оскільки всі полюси, собака та ланцюг мають нульову ширину (мої сусіди - математики), ланцюг може накручуватися навколо стовпа нескінченно без радіусу кола. Собака також може пройти через ланцюг (тільки не її нашийник), якщо ланцюг стоїть на своєму шляху. Помітивши цю дивацтво деякий час, я вирішив написати якийсь код, щоб імітувати собаку мого сусіда. Код візьме місця центрального стовпа, до якого прикута собака, місця розташування інших жердин у моєму дворі сусідів, довжина ланцюга та вихідне розташування собаки, і виведе діаграма із зазначенням стежка, де собака зносила траву. Ви можете припустити, що будь-яка комбінація наведеного нижче є постійною (і, отже, не приймати їх як вхідні дані):

  • Розташування жердини, до якого прикута собака

  • Довжина ланцюга

  • Початкове місце розташування собаки

Сонце піднімається, тому простір на підлозі мого горища, освітлений вікном, скорочується, даючи мені все менше місця для написання коду. Спробуйте звести до мінімуму кількість байтів вашого коду, щоб у мене було місця для складання його на мансардному поверсі.

Тестові справи

Тут я припускаю, що собака починає 3 одиниці на південь від того, до якого полюса вона прикута (червона крапка), розташована в 0,0. Я вказав, де розташовані полюси з крапками для наочності, вам не потрібно включати їх у свій висновок.

Poles at 1,2 -1,2

Тест 1

Poles at 0,.5

Тест 2

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

Тест 3

Poles at 0,1 1,1

Тест 4


Для чого результат {0,-.5}?
Kritixi Lithos

@KritixiLithos Це вихід {0,.5}перевернутого вертикально без найбільшого кола. Собака по суті починає спійматися на другому полюсі.
Пшеничний майстер

У результаті проблем із плаваючою комою моя програма малює коло (1,1) в останньому тесті (довжина рядка - 99,99999). Це добре?
Kritixi Lithos

Собака біжить як за годинниковою, так і проти годинникової стрілки, але з нерухомої точки?
користувач202729

3
"Сонце піднімає простір на підлозі мого горища, освітлене вікном, скорочується, даючи мені все менше місця для написання коду" +1 лише для цього
Лев,

Відповіді:


11

Python 3, використовуючи matplotlib, 457 байт

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

Оскільки ваші сусіди - математики, я припускав, що сад вашого сусіда займає складну область і тому будь-які координати об'єктів у саду є складними числами. Щоб використовувати цю функцію, вам слід передати їй список складних чисел, що позначають розташування полюсів у саду вашого сусіда. Вибрано представлення системи координат за замовчуванням, де праворуч - додатні дійсні числа, а вгору - додатні уявні числа. Це означає, що приклади стають:

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

Крім того, програма передбачає наступні речі: повідець прив’язаний до точки 0, повідця триває 3 одиниці, а площа ділянки 10 на 10 з центром близько 0. Для цих параметрів результати точно відповідають прикладам, і ось як виглядає результат (для остаточного прикладу):

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

Алгоритм досить простий, вимагає лише одного умовного для розмежування пошуку за годинниковою стрілкою та проти годинникової стрілки. Стан алгоритму визначається поточною точкою обертання та орієнтацією / залишковою довжиною повідка, коли вона потрапила в поточну точку обертання. Він працює наступним чином:

  • Відфільтруйте точки з набору зіткнення, які знаходяться далі від поточної точки обертання, ніж відстань, що залишилася, а також поточну точку обертання.
  • Якщо цей набір порожній, намалюйте коло з радіусом залишкової довжини пояса навколо цієї точки, коли кінець цієї руки буде досягнуто.
  • Визначте точку, де різниця фаз між різницевим вектором та орієнтацією повідця мінімальна / максимальна. Це наступна точка, що повідець вдариться відповідно за годинниковою / проти годинникової стрілки.
  • Намалюйте дугу на основі цих векторів, візьміть довжину повідка, відніміть величину відстані і встановіть орієнтацію повідця на орієнтацію вектора різниці. Оновіть точку обертання і продовжуйте з початку.

Потім цей алгоритм виконується спочатку за годинниковою стрілкою, після чого стан скидається і виконується у напрямку проти годинникової стрілки. Простота алгоритму означає, що близько половини програмного рахунку витрачається на функції малювання. Якщо підпрограми малювання були виключені, це видалило б 218 байт з програмного розміру.

Далі наведена версія, що не має волі, яка також містить код налагодження, який також відображає точки та зіткнення повідця:

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

Вихід, який він виробляє, виглядає приблизно так:

Те саме, що і попереднє зображення, але більше рядків


+1 за дійсно чудове пояснення і за те, що я пограв у мене майже вдвічі! <s> боже, я заздрю ​​цим вбудованим </s>
Kritixi Lithos

7

Обробка 3, 815 833 835 876 879 байт

Збережено два байти завдяки @ZacharyT, видаливши непотрібні дужки

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

Запустіть цю програму так:

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

(функція sбере а float[][]). Це по суті тест №3, але помножений на 100, щоб помістити вікно.

Кілька речей, які слід зазначити:

  • програма НЕ малює полюсів
  • зображення виглядають перевернутими догори дном, тому що в системі координат Обробки позитивна вісь y знижується
  • оскільки обробка використовує поплавці, обчислення не дуже точні, тому ви можете побачити це на зображеннях. Я запитав ОП, чи мають значення ці помилки з плаваючою комою.
  • розмір вікна - 600 пікселів на 600 пікселів
  • дуже малі координати введення будуть обробляти програму, оскільки стек pushMatrix()та popMatrix()функціонування може містити лише 32 матриці.
  • собака починається від (0, -300), а ланцюг починається з 300 пікселів
  • зображення нижче були зручні для зручності

Вибірка зразка для вищевказаного тесту.

введіть тут опис зображення

Якщо ви хочете побачити попередньо отриманий вихід, додайте цей рядок відразу після функції translate(w,w);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);

І це дає нам такий результат:

коло

Необурені f()і пояснення

(містить також код налагодження)

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

Якщо коротко, програма надсилає двох «шукачів», один йде проти годинникової стрілки, а другий за годинниковою стрілкою. Кожен з цих шукачів знаходить найближчий полюс і малює до нього дугу, якщо ланцюг достатньо довгий, інші мудрі малюють коло. Як тільки він малює дугу, він надсилає іншого шукача на цей полюс і процес триває. f()містить процес кожного шукача. Більш детальне пояснення надійде, як тільки я пограю ще більше.


Чи потрібні вам парони навколо останнього L-d?
Zacharý

@ZacharyT Я не знаю, як я пропустив це, дякую.
Kritixi Lithos

5

LOGO, 305 298 297 293 байт

Спробуйте код на FMSLogo.

Визначте функцію draw(golfed as d), яка, задана введенням як список координат полюсів (наприклад draw [[0 100] [100 100] [-200 100] [-100 -50][0 0]], виведе на екран результат.

Вимоги:

  1. Початкова довжина мотузки = 300 пікселів. (оскільки 3 пікселя занадто мало)
  2. [0 0]повинні бути включені до списку полюсів. Якщо код налагодження (малювати полюси) увімкнено, то він [0 0]повинен бути останнім елементом.
  3. Собака починається з координати x=0, y=-300(як в описі проблеми)

Можливі оптимізації:

  1. -1 байт, якщо винятковий випадок (собака набігає на жердину) не повинен бути математично правильним заміною >=на>

Код для гольфу:

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 код ( ;запускає вбудований коментар (використовується для пояснення) та :починає ім'я змінної):

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 байт

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

Сценарій читає список точок зі stdin як список складних чисел.

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

введіть тут опис зображення

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

введіть тут опис зображення

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.