ASCIIアートの非交差パスを生成する


18

2つの整数フィールドのサイズを表す入力、所与xy、出力フィールドを介してパス。

の出力例5, 4

#    
#    
# ###
### #

フィールド全体は5 x 4で、フィールドを横切るハッシュマークで構成されるパスがあります。

パスは常に左上隅から始まり、右下に行く必要があります。プログラムを実行するたびに、パス全体をランダム化する必要があります。有効なパスはすべて、可能な出力である必要があります。

パスのルールは次のとおりです。

  • ハッシュマークで作られた

  • すべてのハッシュは、他の2つのハッシュにのみ接続されます(つまり、パスはそれ自体と交差または実行されません)

ハッシュ以外のスペースは他の文字で埋めることができますが、一貫している必要があります(つまり、すべてのスペース、すべてのピリオドなど)。

例:

2, 2

##
 #

3, 4

##
 ##
  #
  #

5, 5

#####
    #
    #
    #
    #

6, 5

## ###
 # # #
## # #
# ## #
###  #

7, 9

#######
      #
####  #
#  #  #
#  #  #
#  #  #
#  ####
#
#######

この種のパスは、自己回避ランダムウォークに似ていますが、真のSAWとは異なり、それ自体に隣接することはできません。

パスの連続性とパスのタッチは両方とも対角線なしで定義されます。


RBX.Lua出力形式は有効ですか?;)
devRicher

すべての有効なパスが選択される正の確率を持っている限り、確率分布はarbitrary意的ですか?
-flawr

ええ@devRicher
Rɪᴋᴇʀ

ええ@flawr、それは正しいです
Rɪᴋᴇʀ

回答:


11

MATLAB、316 305 300 293バイト

function P=f(a,b);z(a,b)=0;P=z;c=@(X)conv2(+X,[0,1,0;1,1,1;0,1,0],'s');B=1;while B;P=z;P(1)=1;for K=1:a*b;S=z;S(a,b)=1;for L=2:a*b;S(~S&c(S)&~P)=L;end;[i,j]=find(S&c(P==K));if i;r=randi(nnz(i));else;break;end;P(i(r),j(r))=K+1;if P(a,b);break;end;end;B=any(any(c(P>0)>3));end;P(P>0)=35;P=[P,'']

@LuisMendoにさまざまな提案とたくさんのバイトをありがとう=)

オンラインでお試しください!(保証なし:Octaveで実行するには、いくつかの調整が必要であることに注意してください:まず、functionキーワードを削除して値をハードコードする必要がありました。次に、Matlabのようにスペースが正しく印刷されません。 Octaveの畳み込みコマンドを確認してください。これは異なる動作をする可能性があります。)

入力の出力例(7,10)(すでにかなり時間がかかる場合があります):

#         
#         
##        
 ##       
  #   ### 
  #   # ##
  #####  #

説明

これにより、目的の4接続性で左上から右下に順番にパスが生成され、拒否サンプリングを使用して、隣接するパーツを配置できないという基準に違反するパスが拒否されます。

function P=f(a,b);
z(a,b)=0;                                 % a matrix of zeros of the size of th efield
P=z;                                    
c=@(X)conv2(+X,[0,1,0;1,1,1;0,1,0],'s');  % our convolution function, we always convolute with the same 4-neighbourhood kernel
B=1;
while B;                                  % while we reject, we generate paths:
    P=z;
    P(1)=1;                               % P is our path, we place the first seed
    for K=1:a*b;                          % in this loop we generate the all shortest paths (flood fill) from the bottom right, withot crossing the path to see what fiels are reachable from the bottom left
        S=z;
        S(a,b)=1;                         % seed on the bottom left
        for L=2:a*b;
            S(~S&c(S)&~P)=L;              % update flood front
        end;
        [i,j]=find(S&c(P==K));            % find a neighbour of the current end of the path, that is also reachable from the bottom left
        if i;                             % if we found some choose a random one
            r=randi(nnz(i));
        else;
            break;                        % otherwise restart (might not be necessary, but I'm too tired to think about it properly=)
        end;
        P(i(r),j(r))=K+1;                 % update the end of the current path
        if P(a,b);                        % if we finished, stop continuing this path
            break;
        end;
    end;
    B=any(any(c(P>0)>3));                 % check if we actually have a valid path
end;
P(P>0)=35;                                % format the path nicely
P=[P,''];

ああ、いつものように:

畳み込みは成功の鍵です。


19

Befunge、344バイト

&v>>>#p_:63p:43g`\!+v>/*53g+\01g:2%2*1-\2/!*63g+\0\:v
 40$ v++!\`g14:p35:\<^2\-1*2%2p10::%4+g00:\g36\g35-1_v
#11^$_83p73v >1+:41g`!#v_$,1+:43g`!#v_@>->2>+00p+141^_
<p1^     vp< ^,g+7g36:<<<<1+55p36:<<<< ^1?0^#7g36g35*
8&p|!++!%9#2g+7g10\*!-g38g10!-g37:g00!!*<>3^
443>:!#v_>>1-::3%1-:53g+00p\3/1-:63g+01p^
^>^>>$#<"#"53g63g7+p41g53g-43g63g-+!#^_

オンラインでお試しください!

MATLABの回答で@flawrが言及したように、フィールドサイズが重要なサイズでない場合、これには時間がかかる場合があります。実際、時間の終わりまで待つ可能性が非常に高いので、完了するのを待つのは本当に価値がない状況に陥るのは非常に簡単です。

これが起こる理由を理解するには、Befungeの多くの「ビジュアルデバッガ」の1つで実行されているプログラムを表示すると役立ちます。Befungeではデータとコードは同じものであるため、時間とともに変化するパスを確認できます。たとえば、次の短いアニメーションは、遅いパスでの実行の一部がどのように見えるかを示しています。

パス構築自体がコーナーで立ち往生しているアニメーション

アルゴリズムがフィールド境界の下部でその運命的な左折を決定すると、それは本質的に無目的の放浪の生涯を非難しました。その時点から、フェンスで囲まれたエリアで可能な限りすべてのパスをたどらなければ、バックアウトして右に曲がることができません。そして、これらの場合の潜在的なパスの数は、天文学的に簡単になります。

結論:時間がかかっているように見える場合は、実行を中止して再起動することをお勧めします。

説明

これは基本的に再帰アルゴリズムであり、フィールドを通るすべての可能なパスを試行し、スタックするたびに既に実行されているステップを巻き戻します。Befungeには関数の概念がないため、再帰関数は問題外ですが、スタックの状態を追跡することでプロセスをエミュレートできます。

これは、追跡する可能性のある潜在的な座標をスタックに追加することで機能します。次に、スタックから1つのセットを取得し、それが適切かどうかを確認します(つまり、範囲内で、既存のパスと重複しない)。良い場所になったら#、その場所のプレイフィールドにaを書き込み、後でバックトラックする必要がある場合に備えてそれらの詳細をスタックに追加します。

次に、この新しい場所から可能なパスを示す追加の4セットの座標を(ランダムな順序で)スタックにプッシュし、ループの先頭に戻ります。可能性のあるパスがどれも実現可能でない場合、#書き出した場所を保存したスタック上のポイントに到達するので、そのステップを取り消して、1ステップ前から潜在的な座標を試し続けます。

これは、さまざまなコンポーネント部分が強調表示されたコードの外観です。

実行パスが強調表示されたソースコード

*フィールドの幅と高さを読み取り、開始0マーカーをタイプマーカーと共に押して、バックトラッキングの場所ではなく潜在的なパスを示します。プレイフィールドにスペースを書き戻すのに必要な正確な形式で保存されているため、単純なコマンドで元に戻さ
*れるバックトラッキングの場所(1タイプマーカーで示される)を確認しpます。
*座標がまだプレイフィールド内にあるかどうかを確認します。それらが範囲外にある場合、スタックからそれらをドロップし、次の潜在的な座標を試すためにループバックします。
*範囲内にある場合は、スタックから次の2つの値を取得します。これは、前の手順の場所です(これに続くテストで必要です)。
*座標がパスの既存のセグメントと接触するかどうかを確認します。前のステップの場所は、このチェックでは明らかに無視されます。
*すべてのテストが成功したら#、プレイフィールドにa を書き込み、目的の場所に到達したかどうかを確認します。
*ある場合は、最終パスを書き出して*終了します。
*それ以外の場合は1、後のバックトラッキングのために、タイプマーカーを使用して座標をスタックに保存します。
*これは、すぐに必要になる乱数計算で中断されます。
*現在の場所から到達できる4つの潜在的な目的地をプッシュします。乱数は、プッシュされる順序を決定し、したがって、それらが追跡される順序を決定します。
* メインループの先頭に戻り、スタック上の次の値を処理します。


2
聖なる牛。説明?
Rɪᴋᴇʀ

@EasterlyIrk報奨金をありがとう。それは大歓迎です。
ジェームズホルダーネス

よかったです!
Rɪᴋᴇʀ

2

QBasic、259バイト

本当に大好きGOTOです。

RANDOMIZE TIMER
INPUT w,h
DO
CLS
x=1
y=1
REDIM a(w+3,h+3)
2a(x+1,y+1)=1
LOCATE y,x
?"#"
d=INT(RND*4)
m=1AND d
x=x+m*(d-2)
y=y-d*m+m+d-1
c=a(x,y+1)+a(x+2,y+1)+a(x+1,y)+a(x+1,y+2)=1
IF(x=w)*c*(y=h)GOTO 9
IF(x*y-1)*x*y*c*(x<=w)*(y<=h)GOTO 2
LOOP
9LOCATE y,x
?"#"

基本戦略:各ステップで、a #を現在の場所に印刷し、ランダムな方向に移動します。a0と1 の配列は、私たちが行った場所を追跡します。移動が正当であり、エンドポイントに移動する場合、GOTO 9ループを終了してfinalを出力し#ます。それ以外の場合、移動が合法である場合は、別の手順を実行します。それ以外の場合は、画面をクリアして最初からやり直します(バックトラッキングアルゴリズムをコーディングするよりもはるかにゴルファーです!)。

QB64のラップトップでテストした結果、通常9, 95秒以内に結果が得られます。の実行に10, 10は3〜45 秒かかりました。理論的には、すべての有効なパスの確率はゼロではありませんが、曲線が大きいパスの確率はゼロになります。ただし、1つまたは2つの小さな曲線のあるパスを見たことがありますが、

サンプルパス

リクエストに応じて、ゴルフのないバージョンや詳細な説明を入手できます。


2

R、225バイト

function(x,y){library(igraph);a=matrix(rep(" ",x*y),c(y,x));g=make_lattice(c(y,x));w=runif(ecount(g));for (i in 1:2){v=shortest_paths(g,1,x*y,weights=w)$vpath[[1]];w[]=1;w[E(g)[v%--%v]]=0;};a[v]="#";cat(rbind(a,"\n"),sep="")}

説明:

エッジにランダムな幅を持つ通常の(格子)[x * y]無向グラフを生成し、開始から終了までの最短経路を見つけます。ただし、生成されたパスには、たとえば2つ以上の隣接するセルがある場合があります。

#
#
####
  ##
  #
  ###

したがって、最短パスアルゴリズムを2回適用する必要があります。2回目には、0に設定された現在見つかったパスにあるものを除き、すべての重みを1に設定します。

結果

#
#
### 
  # 
  #
  ###

ゴルフをしていない:

function(x,y){
    library(igraph);#igraph library should be installed
    a=matrix(rep(" ",x*y),c(y,x));#ASCII representation of the graph
    g=make_lattice(c(y,x));# regular graph
    w=runif(ecount(g));#weights
    for (i in 1:2){
        #find vertices that are in the path
        v=shortest_paths(g,1,x*y,weights=w)$vpath[[1]];
        #set all weights to 1 except those that are in the current found path that set to 0
        w[]=1;
        w[E(g)[v%--%v]]=0;
    }
    a[v]="#";
    cat(rbind(a,"\n"),sep="")
}

1

JavaScript(ES7)、333 331 330 329 324 318 312バイト

f=
(h,w,c=[...Array(h)].map(_=>Array(w).fill` `),g=a=>{for(z of b=[[[h-1,w-1]]])a.map(([x,y])=>b.every(([[i,j]])=>i-x|j-y)&(z[0][0]-x)**2+(z[0][1]-y)**2<2&&b.push([[x,y],...z]));return b.find(([[x,y]])=>!x&!y)||g([...a,[h,w].map(n=>Math.random()*n|0)])})=>g([]).map(([x,y])=>c[x][y]=`#`)&&c.map(a=>a.join``).join`
`
Height: <input type=number min=1 id=h>Width: <input type=number min=1 id=w><input type=button value="Generate!" onclick=o.textContent=f(+h.value,+w.value)><pre id=o>

拡張:#幅優先探索を使用してフィールドを通るパスが見つかるまで、配列にランダムに配置されます。最初の、したがって最短のパスが出力されます。これにより、パスがそれ自体と交差しないことが保証されます。特に大きなフィールドでは、パスが見つかる前にJSエンジンのスタックを超える可能性があることに注意してください。ゴルフをしていない:

function r(n) {
    return Math.floor(Math.random() * n);
}
function f(h, w) {
    var a = []; // array of placed #s
    var b; // breadth-first search results
    var c;
    do {
        a.push([r(h), r(w)]); // place a # randomly
        b = [[[h - 1, w - 1]]]; // start from bottom right corner
        for (z of b) // breadth-first search
            for ([x, y] of a) // find possible next steps
                if (!b.some(([[i, j]]) => i == x && j == y))
                    if ((z[0][0] - x) ** 2 + (z[0][1] - y) ** 2 < 2)
                        if (x || y)
                            b.push([[x, y], ...z]); // add to search
                        else if (!c)
                            c = [[x, y], ...z]; // found path
    } while (!c);
    a = [...Array(h)].map(() => Array(w).fill(' '));
    for ([x, y] of c) // convert path to output
        a[x][y] = '#';
    return a.map(b => b.join('')).join('\n');
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.