チェーン上の犬


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}何ですか?
クリチキシリソス

@KritixiLithosその{0,.5}最大の円のない垂直反転の出力。犬は基本的に2番目のポールに引っかかり始めます。
小麦ウィザード

浮動小数点の問題の結果、私のプログラムは最後のテストケースで(1,1)の周りに円を描きます(文字列の長さは99.99999です)。これでいいですか?
クリティキシリソス

犬は時計回りと反時計回りの両方に走りますが、固定点からですか?
user202729

3
「太陽が窓に照らされた屋根裏部屋の床のスペースを縮小しているので、コードを書くためのスペースがどんどん減っています」これのためだけに+1
レオ

回答:


11

matplotlibを使用したPython 3、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単位の長さであり、プロット領域は0を中心とした10 x 10です。これらのパラメーターの場合、結果は例と正確に一致します。そして、これは結果がどのように見えるかです(最後の例のために):

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

アルゴリズムは非常に単純で、時計回りの検索と反時計回りの検索を区別するための条件を1つだけ必要とします。アルゴリズムの状態は、現在の回転点と、リーシュが現在の回転点に達したときの方向/残りの長さによって定義されます。次のように機能します。

  • 衝突セットから、現在の回転ポイントだけでなく、残りのリーシュ長よりも現在の回転ポイントから遠く離れているポイントを除外します。
  • このセットが空の場合、このアームの端に到達したときに、このポイントの周りに残りのベルト長の半径で円を描きます。
  • 差分ベクトルとリーシュの向きの位相差が最小/最大になる点を決定します。これは、リーシュが時計回り/反時計回りにそれぞれヒットする次のポイントです。
  • これらのベクトルに基づいて円弧を描き、リーシュの長さを取得し、距離の大きさを減算し、リーシュの向きを差分ベクトルの向きに設定します。回転点を更新し、最初から続行します。

このアルゴリズムは、最初に時計回りに実行され、その後、状態がリセットされ、反時計回りに実行されます。アルゴリズムが単純なため、プログラムのバイトカウントの約半分が描画関数に費やされます。描画ルーチンを削除すると、プログラムサイズから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()

生成される出力は次のようになります。

前の画像と同じですが、より多くの行


本当に素晴らしい説明と、私をほぼ2倍ゴルフしてくれた+1 <S>おやっ私はそれらの組み込みコマンドがうらやましい</ S>
KritixiのLithos

7

処理3、815 833 835 876 879バイト

不要な括弧を削除して@ZacharyTのおかげで2バイト節約

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倍されます。

注意するべきいくつかの事柄:

  • プログラムは極を描画しません
  • Processingの座標系では、正のY軸が下がっているため、画像が上下逆になっているように見えます
  • Processingはfloatを使用するため、計算はあまり正確ではないため、画像でこれを確認できます。これらの浮動小数点エラーが重要かどうかをOPに尋ねました。
  • ウィンドウのサイズは600ピクセルx 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);
    }
}

簡単に言うと、プログラムは2つの「シーカー」を送信します。1つは反時計回りに、もう1つは時計回りに移動します。これらのシーカーはそれぞれ、最も近い極を見つけて、鎖が十分に長い場合はそれに弧を描き、それ以外の場合は円を描きます。円弧を描くと、別のシーカーをその極に送信し、プロセスが続行されます。f()各シーカーのプロセスが含まれています。私がこれをもっとゴルフするとすぐに、より詳細な説明が来るでしょう。


最後の周りに括弧が必要L-dですか?
ザカリー

@ZacharyTどうしてそれを見逃したかわかりません、ありがとう。
クリティキシリソス

5

ロゴ、305 298 297 293バイト

FMSLogoでコードを試してください。

極座標のリストとして入力が与えられた関数draw(としてゴルフ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

ゴルフされていないコード(;説明に使用されるインラインコメントを:開始し、変数名を開始します):

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

ここに画像の説明を入力してください

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.