Java 8ラムダ、1506 1002 972 942文字
非常に興味深いので、私はこの挑戦に打ち勝ちたかった。結果(あまりゴルフっぽくない)はここで見ることができます:
import java.util.*;f->{Set<double[]>B=new HashSet(),r,n;double a,M,m,P=Math.PI*2,z=.5;int x=0,y,v=0,i,j,c[],p,q,l=g.length;for(;x<l;x++)for(y=0;y<g[x].length;y++)if(g[x][y]>63)for(;;){c=new int[]{-1};M=2e31-1;for(i=0;i<l;i++)for(j=0;j<g[i].length;j++)if(g[i][j]==42)if((m=(p=x-i)*p+(q=y-j)*q)<M){M=m;c=new int[]{i,j};}if(c[0]<0)break;g[c[0]][c[1]]=0;double[]A={(a=Math.atan2((c[1]-=y)-z,(c[0]-=x)-z))<0?a+P:a,(a=Math.atan2(c[1]+z,c[0]-z))<0?a+P:a,(a=Math.atan2(c[1]+z,c[0]+z))<0?a+P:a,(a=Math.atan2(c[1]-z,c[0]+z))<0?a+P:a};r=new HashSet();M=-P;m=P;for(double d:A){M=d>M?d:M;m=d<m?d:m;}r.add(new double[]{m,M});for(double[]t:B){n=new HashSet();for(double[]h:r)for(double[]u:t[0]<h[0]?t[1]<h[0]?new double[][]{h}:t[1]<h[1]?new double[][]{{t[1],h[1]}}:new double[0][]:t[0]>h[1]?new double[][]{h}:t[1]>h[1]?new double[][]{{h[0],t[0]}}:new double[][]{{h[0],t[0]},{t[1],h[1]}})if(u[0]<u[1])n.add(u);r=n;}B.addAll(r);if(!r.isEmpty())v++;}return v;}
もちろん、これは非ゴルフバージョンにも存在します。
import java.util.*;
public class AngleCheck {
static int getViewableBuildingsC(char[][] grid) {
Set<double[]> blocked = new HashSet(), ranges, newRanges;
double angle, max, min, PI2 = Math.PI * 2, half = 0.5;
int x = 0, y, viewable = 0, i, j, building[], dX, dY, length = grid.length;
for (; x < length; x++) {
for (y = 0; y < grid[x].length; y++) {
if (grid[x][y] > 63) {
for (;;) {
building = new int[]{-1};
max = 2e31-1;
for (i = 0; i < length; i++) {
for (j = 0; j < grid[i].length; j++) {
if (grid[i][j] == 42) {
if ((min = (dX = x - i) * dX + (dY = y - j) * dY) < max) {
max = min;
building = new int[]{i, j};
}
}
}
}
if (building[0] < 0)
break;
grid[building[0]][building[1]] = 0;
double[] angles = {
(angle = Math.atan2((building[1] -= y) - half, (building[0] -= x) - half)) < 0 ? angle + PI2 : angle,
(angle = Math.atan2(building[1] + half, building[0] - half)) < 0 ? angle + PI2 : angle,
(angle = Math.atan2(building[1] + half, building[0] + half)) < 0 ? angle + PI2 : angle,
(angle = Math.atan2(building[1] - half, building[0] + half)) < 0 ? angle + PI2 : angle};
ranges = new HashSet();
max = -PI2;
min = PI2;
for (double d : angles) {
max = d > max ? d : max;
min = d < min ? d : min;
}
ranges.add(new double[]{min, max});
for (double[] reference : blocked) {
newRanges = new HashSet();
for (double[] currentRange : ranges) {
for (double[] subRange : reference[0] < currentRange[0] ?
reference[1] < currentRange[0] ?
// whole range after referencerange
new double[][]{currentRange}
:
reference[1] < currentRange[1] ?
// lower bound inside referencerange, but upper bound outside
new double[][]{{reference[1], currentRange[1]}}
:
// whole range inside referencerange -> nothing free
new double[0][]
:
// greater or equal lower bound
reference[0] > currentRange[1] ?
// whole range before referencerange
new double[][]{currentRange}
:
// ranges overlap
reference[1] > currentRange[1] ?
// range starts before and ends in reference range
new double[][]{{currentRange[0], reference[0]}}
:
// referencerange is in the range -> two free parts, one before, one after this
new double[][]{{currentRange[0], reference[0]}, {reference[1], currentRange[1]}}) {
if (subRange[0] < subRange[1])
newRanges.add(subRange);
}
}
ranges = newRanges;
}
blocked.addAll(ranges);
if (!ranges.isEmpty()) {
viewable++;
}
}
}
}
}
return viewable;
}
}
それは非常に難しいように見えますが、人が考えるよりもずっと簡単です。私の最初のアイデアは、交差点アルゴリズムを使用して、自分の位置から建物までの線が交差点なしで作成できるかどうかを確認することでした。これを行うために、Cohen-Sutherlandアルゴリズムを使用して、建物の四隅すべてに線を引くことにしました。これは最初のテストではかなりうまくいきましたが、最後のテストは失敗しました。すぐにわかったのは、コーナーではなくエッジの一部が見えるケースだということです。そこで、@ Blueがやったように、ある種のレイキャスティングについて考えました。少しの進歩もなかったので、私はその挑戦を片付けました。その後、ブルーの答えを見て、次の簡単なアイデアが思い浮かびました。各建物は、他には何も見えない角度をブロックしています。見えるものと、他の建物によってすでに隠されているものを追跡する必要があります。それでおしまい!
このアルゴリズムは次のように機能します。人までの距離が最も短い建物を決定します。次に、人から建物の隅まで描かれた4本の線を想像します。これらのうちの2つには極端な値があります。建物が見える最小角度と最大角度。それらを範囲として取り、それらを見ることができることがわかっている他の建物と比較します(最初はありません)。範囲は重複していても、お互いを含んでいても、まったく触れていなくてもかまいません。範囲を比較し、表示可能な建物に隠れていない建物の新しい範囲を取得します。見えている建物と比較した後に何かが残っている場合、その建物も見ることができます。残りの角度範囲を範囲のリストに追加して、次に長い距離の次の建物と比較して開始します。
場合によっては、範囲が0度の範囲で重なり合うことがあります。これらの範囲は、表示されない建物を誤って追加しないようにフィルターされます。
誰かがこの説明を理解してくれることを望みます:)
私はこのコードがあまりゴルフされていないことを知っています、私はできるだけ早くこれをします。
それは本当にやりがいのある仕事でした。うまくいく解決策を見つけたと思っていましたが、代わりにあなたはまだ遠くにいます。このソリューションはかなりうまくいくと思います。それは非常に高速ではありませんが、少なくとも動作します;)そのパズルをありがとう!
更新
私はすべてを1つの関数にゴルフする時間を見つけたので、それをラムダに変えることができます。すべての関数は一度だけ呼び出されたため、1つのメソッドに入れることができます。リストからセットに切り替えると、追加の文字が保存されます。宣言はまとめられています。比較がまとめられ、文字がASCII値に置き換えられました。範囲の比較は、多くの3項として表現できます。Double.NEGATIVE_INFINITYのような長い式を防ぐために、あちこちでいくつかのトリックが行われました。可能な場合、インライン割り当てが行われます。もう少し節約するために、角度を度で比較することからラジアンを比較することに切り替えました。変更全体で500文字以上が保存されましたが、すべて1000文字未満にしたいと考えています;)
可能な場合はジェネリックを削除し、1つの要素配列を作成して戻り値の比較を短縮し、代わりにその値を確認しました。Double.NEGATIVE_INFINITYもPI2と-PI2に置き換えました。これらは角度の上限と下限です。ついに1000文字以下になりました!
人物の場所と建物のイテレーターを見つけるためのループをマージして、いくつかのキャラクターを保存しました。残念ながら、これにはループから戻り値を移動し、今度はラベルなしでブレークを使用する必要があります。私はマージmax
しdistanceSquared
、min
そしてnewDistanceSquared
同時にそれらは必要ではないので。に変更Integer.MAX_VALUE
しました2e31-1
。またhalf = 0.5
、建物の角を計算するために使用される定数を作成しました。これは、ゴルフバージョンでは短くなります。全体で、さらに30文字を節約しました!