Một con chó trên một chuỗi


31

Tôi đang nhìn ra ngoài cửa sổ gác mái của tôi vào sân của hàng xóm. Họ có một con chó bị xích vào một cột ở giữa sân. Con chó chạy quanh sân nhưng luôn ở cuối chuỗi, vì vậy cuối cùng nó để lại dấu vết trong bụi bẩn. Thông thường bản nhạc này sẽ có hình tròn hoàn hảo, nhưng hàng xóm của tôi có một số cực khác trong sân của họ mà chuỗi của con chó bị bắt. Mỗi khi chuỗi chó chạm vào một cây cột, con chó bắt đầu xoay quanh cây cột mới với bất kỳ chiều dài chuỗi nào còn lại là bán kính của nó. Kể từ khi cực, con chó và chuỗi đều có chiều rộng bằng không (hàng xóm của tôi là nhà toán học), chuỗi có thể quấn quanh một cây cột vô thời hạn mà không cần bán kính rút ngắn vòng tròn. Con chó cũng có thể đi qua chuỗi (chỉ không phải cổ của nó) nếu chuỗi nằm trong đường đi của nó. Sau khi quan sát sự kỳ lạ này một lúc, tôi quyết định tôi sẽ viết một số mã để mô phỏng con chó của hàng xóm. Mã sẽ lấy các vị trí của một cột trung tâm, mà con chó bị xích, vị trí của các cực khác trong sân nhà hàng xóm của tôi, chiều dài của chuỗi và vị trí bắt đầu của con chó, và sẽ đưa ra một sơ đồ chỉ ra con đường nơi con chó đã mòn cỏ. Bạn có thể cho rằng bất kỳ sự kết hợp nào sau đây là không đổi (và do đó không lấy chúng làm đầu vào):

  • Vị trí của cột mà con chó bị xích

  • Chiều dài của chuỗi

  • Vị trí bắt đầu của con chó

Mặt trời đang lên, vì vậy không gian trên sàn gác mái của tôi được chiếu sáng bởi cửa sổ đang bị thu hẹp lại, khiến tôi ngày càng có ít không gian để viết mã. Vui lòng cố gắng giảm thiểu số byte của mã của bạn để tôi có không gian để phác thảo nó trên sàn gác mái của tôi.

Các trường hợp thử nghiệm

Ở đây tôi giả sử rằng con chó bắt đầu 3 đơn vị phía nam từ cực mà nó bị xích (chấm đỏ), nằm ở 0,0. Tôi đã chỉ ra nơi các cực có dấu chấm cho rõ ràng, bạn không cần đưa chúng vào đầu ra của bạn.

Poles at 1,2 -1,2

Kiểm tra 1

Poles at 0,.5

Kiểm tra 2

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

Bài kiểm tra 3

Poles at 0,1 1,1

Kiểm tra 4


Đầu ra để làm {0,-.5}gì?
Kritixi Lithos

@KritixiLithos Đó là đầu ra của {0,.5}lật theo chiều dọc mà không có vòng tròn lớn nhất. Con chó về cơ bản bắt đầu bắt trên cực thứ hai.
Thuật sĩ lúa mì

Do vấn đề về dấu phẩy động, chương trình của tôi vẽ một vòng tròn (1,1) trong bản thử nghiệm cuối cùng (độ dài chuỗi là 99.99999). Cái này có ổn không
Kritixi Lithos

Con chó chạy cả chiều kim đồng hồ và ngược chiều kim đồng hồ, nhưng từ một điểm cố định?
dùng202729

3
"Mặt trời đang mọc không gian trên sàn gác mái của tôi được chiếu sáng bởi cửa sổ đang thu hẹp lại cho tôi ngày càng ít không gian để viết mã của mình" +1 chỉ cho điều này
Leo

Câu trả lời:


11

Python 3 sử dụng 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()

Bởi vì hàng xóm của bạn là nhà toán học, tôi đã cho rằng khu vườn của hàng xóm chiếm lĩnh miền phức tạp và do đó mọi tọa độ của các vật thể trong vườn đều là những con số phức tạp. Do đó, để sử dụng chức năng này, bạn nên chuyển cho nó một danh sách các số phức biểu thị vị trí của các cực trong khu vườn của hàng xóm. Biểu diễn hệ tọa độ mặc định đã được chọn, trong đó bên phải là các số thực dương và hướng lên trên là các số ảo tưởng tượng dương. Điều này có nghĩa là các ví dụ trở thành:

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

Hơn nữa, chương trình giả định những điều sau đây: dây xích được buộc vào điểm 0, dây xích dài 3 đơn vị và diện tích lô là 10 x 10 tập trung vào khoảng 0. Đối với các tham số này, kết quả khớp chính xác với các ví dụ, và đây là kết quả trông như thế nào (ví dụ cuối cùng):

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

Thuật toán khá đơn giản, chỉ yêu cầu một điều kiện để phân biệt theo chiều kim đồng hồ và tìm kiếm ngược chiều kim đồng hồ. Trạng thái của thuật toán được xác định bởi điểm xoay hiện tại và hướng / chiều dài còn lại của dây xích khi nó chạm vào điểm xoay hiện tại. Nó hoạt động như sau:

  • Lọc các điểm ra khỏi bộ va chạm ở xa điểm xoay hiện tại hơn độ dài dây xích còn lại, cũng như điểm xoay hiện tại.
  • Nếu bộ này trống, hãy vẽ một vòng tròn có bán kính của chiều dài vành đai còn lại quanh điểm này khi đã đạt đến điểm cuối của cánh tay này.
  • Xác định điểm mà độ lệch pha giữa vectơ chênh lệch và hướng xích là tối thiểu / tối đa. Đây là điểm tiếp theo dây xích sẽ đánh theo chiều kim đồng hồ / ngược chiều kim đồng hồ tương ứng.
  • Vẽ vòng cung dựa trên các vectơ này, lấy chiều dài dây xích, trừ độ lớn của khoảng cách và đặt hướng dây xích theo hướng của vectơ chênh lệch. Cập nhật điểm xoay và tiếp tục từ đầu.

Thuật toán này sau đó được thực hiện đầu tiên theo chiều kim đồng hồ, sau đó trạng thái được đặt lại và nó được thực hiện theo hướng ngược chiều kim đồng hồ. Sự đơn giản của thuật toán có nghĩa là khoảng một nửa số lượng chương trình được dành cho các chức năng vẽ. Nếu các thói quen vẽ bị loại bỏ, nó sẽ loại bỏ 218 byte khỏi kích thước chương trình.

Sau đây là phiên bản không được mã hóa cũng chứa mã gỡ lỗi, cũng hiển thị các điểm và va chạm dây xích:

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

Đầu ra mà nó tạo ra trông như thế này:

Giống như hình ảnh trước nhưng nhiều dòng hơn


+1 cho một lời giải thích thực sự tuyệt vời và đã chơi golf cho tôi gần gấp đôi! <s> gosh Tôi ghen tị với những nội dung đó </ s>
Kritixi Lithos

7

Xử lý 3, 815 833 835 876 879 byte

Đã lưu hai byte nhờ @ZacharyT bằng cách xóa các dấu ngoặc đơn không cần thiết

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

Chạy chương trình này như vậy:

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

(hàm slấy trong a float[][]). Đây thực chất là testcase # 3, nhưng nhân với 100 để phù hợp với cửa sổ.

Một số điều cần lưu ý:

  • chương trình KHÔNG vẽ cực
  • các hình ảnh xuất hiện lộn ngược vì trong hệ tọa độ của Xử lý, trục y dương đi xuống
  • bởi vì Xử lý sử dụng số float, các phép tính không chính xác lắm, vì vậy bạn có thể thấy điều này trong các hình ảnh. Tôi đã hỏi OP nếu những lỗi dấu phẩy động này có vấn đề.
  • kích thước của cửa sổ là 600 pixel x 600 pixel
  • tọa độ đầu vào rất nhỏ sẽ làm hỏng chương trình vì ngăn xếp pushMatrix()popMatrix()hoạt động chỉ có thể chứa 32 ma trận.
  • con chó bắt đầu từ (0, -300) và chuỗi bắt đầu dài 300 pixel
  • những hình ảnh dưới đây đã được thu nhỏ để thuận tiện

Đầu ra mẫu cho các testcase trên.

nhập mô tả hình ảnh ở đây

Nếu bạn muốn xem đầu ra được chỉnh sửa, hãy thêm dòng này ngay sau translate(w,w); hàm 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);

Và điều này cho chúng ta kết quả này:

vòng tròn

Ung dung f()và giải thích

(cũng chứa mã gỡ lỗi)

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

Nói ngắn gọn, chương trình sẽ gửi hai "người tìm kiếm", một người đi ngược chiều kim đồng hồ và người kia theo chiều kim đồng hồ. Mỗi người trong số những người tìm kiếm này tìm thấy cực gần nhất và vẽ một vòng cung cho nó nếu chuỗi đủ dài, khôn ngoan khác, nó vẽ một vòng tròn. Khi nó vẽ một vòng cung, nó sẽ gửi một người tìm kiếm khác đến cực đó và quá trình tiếp tục. f()chứa quá trình của mỗi người tìm kiếm. Một lời giải thích chi tiết hơn sẽ đến ngay khi tôi chơi golf này nhiều hơn.


Bạn có cần các parens xung quanh cuối cùng L-d?
Zacharý

@ZacharyT Tôi không biết làm thế nào tôi bỏ lỡ điều đó, cảm ơn.
Kritixi Lithos

5

Logo, 305 298 297 293 byte

Hãy thử mã trên FMSLogo.

Xác định một hàm draw(được gọi là d), cho đầu vào dưới dạng danh sách tọa độ cực (ví dụ:draw [[0 100] [100 100] [-200 100] [-100 -50][0 0]] , sẽ vẽ trên màn hình kết quả.

Yêu cầu:

  1. Chiều dài dây ban đầu = 300 pixel. (vì 3 pixel quá nhỏ)
  2. [0 0]phải được bao gồm trong danh sách cực. Nếu mã gỡ lỗi (vẽ cực) được bật, thì[0 0] phải là mục cuối cùng.
  3. Con chó bắt đầu phối hợp x=0, y=-300(như trong mô tả vấn đề)

Tối ưu hóa có thể:

  1. -1 byte nếu trường hợp ngoại lệ (con chó chạy vào cột) không bắt buộc phải đúng về mặt toán học bằng cách thay thế >=bằng>

Mã đánh gôn:

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

Mã Ungolfed ( ;bắt đầu một nhận xét nội tuyến (được sử dụng để giải thích) và :bắt đầu một tên biến):

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

Kịch bản đọc danh sách các điểm từ stdin dưới dạng danh sách các số phức.

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

nhập mô tả hình ảnh ở đây

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

nhập mô tả hình ảnh ở đây

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.