GNU Prolog、コードページ850で622 634 668バイト
更新:以前のバージョンのプログラムでは、交差がきつく作られていて、適切にレンダリングされないことがあり、仕様に違反していました。それを防ぐために追加のコードを追加しました。
更新:どうやらPPCGルールは、プログラムを終了し、開始時の状態を正確に復元するために追加のコードを必要とするようです。これにより、プログラムが多少長くなり、アルゴリズム上の関心が追加されなくなりますが、ルールへの準拠のために変更しました。
ゴルフプログラム
GNU Prologを使用する理由は、ポータブルPrologの算術構文よりもわずかに短い制約ソルバー構文があり、数バイトを節約できるためです。
y(A,G):-A=1;A= -1;A=G;A is-G.
z(A/B,B,G):-y(A,G),y(B,G),A=\= -B.
v(D,E,G):-E=1,member(_-_,D),p(D);F#=E-1,nth(F,D,M),(M=[_];M=L-S/R,z(L,O,G),J is F+O,nth(J,D,I/U-T/Q),(I=O,Q#=R-1,S=T;K is J+O,R=0,n(S-T-V),y(U,G),U\=O,U=\= -O,I=U,nth(K,D,O/_-V/_))),v(D,F,G).
i([H|K],S):-K=[]->assertz(n(S-H-0));T#=S+1,assertz(n(S-H-T)),i(K,T).
t([],1,_):-!.
t(D,R,G):-S#=R-1,t(E,S,G),H#=G-1,length(F,H),append(F,[[x]|E],D).
s(1,2).
s(-1,1).
s(S,T):-S>1->T=3;T=0.
r(I/O-_,C):-s(I,J),s(O,P),N#=J*4+P+1,nth(N,"│┐┌?└─?┌┘?─┐?┘└│",C).
r([X],C):-X\=y->C=10;C=32.
p([]).
p([H|T]):-r(H,C),put_code(C),!,p(T).
g(4).
g(G):-g(H),G#=H+1.
m(K):-i(K,0),g(G),t(D,G,G),length(D,L),v(D,L,G),abolish(n/1).
アルゴリズム
これは、開始方法を知るのが難しい問題の1つです。与えられた表記法からノットの形状をどのように計算するかは明らかではありません。なぜなら、与えられた位置で線を左に曲げるか右に曲げるかを知らないからです。表記があいまいになる場合があります)。私の解決策は、古いゴルフスタンバイを効果的に使用することでした。可能なすべての出力を生成し、入力と一致するかどうかを確認する非常に非効率なプログラムを作成します。(ここで使用されるアルゴリズムは、Prologが時折行き止まりになる可能性があるため、わずかに効率的ですが、計算の複雑さを改善するのに十分な情報がありません。)
出力はターミナルアート経由です。GNU Prologは、ASCIIと整合性のある1バイト文字セットを必要としているように見えますが、どちらが使用されるかは気にしません(高ビットセットの文字を不透明として扱うため)。その結果、IBM850を使用しました。IBM850は広くサポートされており、必要な線画文字を備えています。
出力
プログラムは、バウンディングボックスのサイズの順にすべての可能な結び目画像を検索し、最初に見つかったときに終了します。出力は次のようになりm([0]).
ます。
┌┐
┌│┘
└┘
これは私のコンピューターで実行するのに290.528秒かかりました。プログラムはあまり効率的ではありません。で2時間実行したままにしてm([0,1])
、次のようになりました。
┌┐┌┐
└─│┘
└┘
コメント付きの未ゴルフバージョン
Stack Exchangeの構文ハイライターには、Prologのコメントシンボルが間違っているように見えるため、%
コメント(Prologが実際に使用している)の代わりに、この説明では% #
コメントを使用します(もちろん、で始まるため、同等に%
強調表示されますが、正しく強調表示されます)。
% # Representation of the drawing is: a list of:
% # indelta/outdelta-segment/distance (on path)
% # and [x] or [_] (off path; [x] for border).
% # A drawing is valid, and describes a knot, if the following apply
% # (where: d[X] is the Xth element of the drawing,
% # k[S] is the Sth element of the input,
% # n[S] is S + 1 modulo the number of sections):
% # d[X]=_/O-S-R, R>1 implies d[X+O]=O/_-S-(R-1)
% # d[X]=_/O-S-0 implies d[X+O]=_/_-k[S]-_
% # and d[X+O*2]=O/_-n[S]-_
% # all outdeltas are valid deltas (±1 row/column)
% # not technically necessary, but makes it possible to compile the
% # code (and thus makes the program faster to test):
:- dynamic([n/1]).
% # legal delta combinations:
y(A,G):-A=1;A= -1; % # legal deltas are 1, -1,
A=G;A is-G. % # grid size, minus grid size
z(A/B,B,G):-y(A,G),y(B,G), % # delta components are valid
A=\= -B. % # doesn't U-turn
% # z returns the outdelta for convenience (= byte savings) later on
% # We use v (verify) to verify the first E-1 elements of a drawing D.
% # nth is 1-indexed, so we recurse from length(D)+1 down to 2, with
% # 1 being the trivial base case. After verifying, the grid is printed.
% # This version of the program causes v to exit with success after
% # printing one grid (and uses p to do the work of deciding when that is).
v(D,E,G):-E=1, % # base case:
member(_-_,D), % # ensure the grid is nonempty
p(D); % # print the grid (and exit)
% # otherwise, recursive case:
F#=E-1,nth(F,D,M), % # check the last unchecked element
(
M=[_]; % # either it's not on the path; or
M=L-S/R, % # it's structured correctly, and
z(L,O,G), % # it has a valid delta;
J is F+O, % # find the outdelta'd element index
nth(J,D,I/U-T/Q), % # and the outdelta'd element
(
I=O,Q#=R-1,S=T; % # if not an endpoint, points to next pixel
K is J+O, % # else find segment beyond the path:
R=0, % # it's an endpoint,
n(S-T-V), % # it points to the correct segment,
y(U,G), % # ensure we can do NOT comparisons on U
U\=O,U=\= -O, % # the line we jump is at right angles
I=U, % # the line we jump is straight
nth(K,D,O/_-V/_) % # the pixel beyond has a correct indelta,
% # and it has the correct segment number
)
),
v(D,F,G). % # recurse
% # We use i (init) to set up the k, n tables (k and n are fixed for
% # any run of the program, at least). S is the number of elements that
% # have been removed from K so far (initially 0). To save on characters,
% # we combine k and n into a single predicate n.
i([H|K],S):-K=[]-> % # if this is the last element,
assertz(n(S-H-0)); % # section 0 comes after S;
T#=S+1, % # otherwise, add 1 to S,
assertz(n(S-H-T)), % # that section comes after S,
i(K,T). % # and recurse.
% # We use t (template) to create a template drawing. First argument is
% # the drawing, second argument is the number of rows it has plus 1,
% # third argument is the number of columns it has plus 1.
t([],1,_):-!.
t(D,R,G):-S#=R-1,t(E,S,G), % # recurse,
H#=G-1,length(F,H), % # F is most of this row of the grid
append(F,[[x]|E],D). % # form the grid with F + border + E
% # We use s (shrink) to map a coordinate into a value in the range 0, 1, 2, 3.
s(1,2).
s(-1,1).
s(S,T):-S>1->T=3;T=0.
% # We use r (representation) to map a grid cell to a character.
r(I/O-_,C):-s(I,J),s(O,P),N#=J*4+P+1,nth(N,"│┐┌?└─?┌┘?─┐?┘└│",C).
r([X],C):-X\=y->C=10;C=32.
% # We use p (print) to pretty-print a grid.
% # The base case allows us to exit after printing one knot.
p([]).
p([H|T]):-r(H,C),put_code(C),!,p(T).
% # We use g (gridsize) to generate grid sizes.
g(4).
g(G):-g(H),G#=H+1.
% # Main program.
m(K):-i(K,0), % # initialize n
g(G), % # try all grid sizes
t(D,G,G), % # generate a square knot template, size G
length(D,L), % # find its length
v(D,L,G), % # verify and print one knot
% # Technically, this doesn't verify the last element of L, but we know
% # it's a border/newline, and thus can't be incorrect.
abolish(n/1). % # reset n for next run; required by PPCG rules