Java 8 lambda, 1506 1002 972 942 karakter
Saya ingin mengalahkan tantangan ini, karena sangat menarik. Hasilnya (tidak terlalu golf) dapat dilihat di sini:
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;}
Tentu saja ini juga ada dalam versi yang tidak di-serigala:
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;
}
}
Jadi itu terlihat sangat sulit tetapi itu jauh lebih mudah daripada yang diperkirakan. Ide pertama saya adalah menggunakan beberapa algoritma persimpangan untuk memeriksa apakah garis dari posisi saya ke bangunan dapat dibuat tanpa persimpangan. Untuk melakukan ini, saya memutuskan untuk menggunakan algoritma Cohen-Sutherland dan menggambar garis ke keempat sudut bangunan. Ini bekerja cukup baik untuk tes pertama, tetapi yang terakhir gagal. Saya segera mengetahui, bahwa itu adalah kasus di mana Anda tidak dapat melihat sudut tetapi bagian dari tepi. Jadi saya berpikir tentang semacam casting ray seperti @Blue yang melakukannya. Saya menyingkirkan tantangan itu, karena saya tidak mendapatkan kemajuan. Lalu aku melihat jawaban Blue dan ide sederhana berikut muncul di benakku: Setiap bangunan menghalangi sudut di mana tidak ada hal lain yang bisa dilihat. Saya hanya perlu melacak apa yang bisa dilihat dan apa yang sudah disembunyikan oleh bangunan lain. Itu dia!
Algoritma bekerja sebagai berikut: Ini menentukan bangunan dengan jarak terkecil ke orang. Kemudian kita membayangkan empat garis yang ditarik dari orang tersebut ke sudut-sudut bangunan. Dua di antaranya memiliki nilai ekstrem: Sudut minimum dan maksimum di mana bangunan dapat dilihat. Kami mengambilnya sebagai jangkauan dan membandingkannya dengan bangunan lain yang kami tahu bahwa mereka dapat dilihat (tidak ada di awal). Rentang mungkin tumpang tindih, termasuk satu sama lain atau tidak menyentuh sama sekali. Saya membandingkan rentang dan mendapatkan beberapa rentang baru bangunan yang tidak disembunyikan oleh bangunan yang dapat dilihat. Jika ada sesuatu yang tersisa setelah membandingkannya dengan bangunan yang terlihat, bangunan tersebut juga dapat dilihat. Kami menambahkan rentang sudut yang tersisa ke daftar rentang untuk dibandingkan dan mulai dengan bangunan berikutnya dengan jarak yang lebih jauh berikutnya.
Terkadang rentang mungkin tumpang tindih dengan cara saya berakhir dengan kisaran 0 derajat. Rentang ini akan difilter agar tidak salah menambahkan bangunan yang bahkan tidak dapat dilihat.
Saya harap seseorang mengerti penjelasan ini :)
Saya tahu kode ini tidak terlalu banyak golf, saya akan segera melakukannya.
Itu adalah tugas yang sangat menantang. Anda pikir Anda menemukan solusi yang berfungsi tetapi Anda masih jauh. Saya pikir solusi ini cukup bagus. Itu tidak terlalu cepat tetapi setidaknya itu berfungsi;) Terima kasih untuk teka-teki itu!
Memperbarui
Saya menemukan waktu untuk golf semuanya menjadi satu fungsi, yang dengan demikian dapat diubah menjadi lambda. Semua fungsi hanya dipanggil sekali dan dengan demikian dapat dimasukkan ke dalam satu metode. Saya beralih dari daftar ke set karena ini menghemat beberapa karakter tambahan. Deklarasi telah disatukan. Perbandingan telah disatukan dan karakter digantikan oleh nilai ascii yang ada. Membandingkan rentang dapat dinyatakan sebagai banyak terner. Beberapa trik di sana-sini untuk mencegah ekspresi panjang seperti Double.NEGATIVE_INFINITY dilakukan. Jika memungkinkan, inline assigment dilakukan. Untuk menghemat lebih banyak, saya beralih dari membandingkan sudut dalam derajat ke membandingkan radian. Seluruh perubahan menyelamatkan lebih dari 500 karakter, saya berharap mendapatkan semuanya di bawah 1000;)
Saya menghapus generik jika memungkinkan dan mempersingkat perbandingan pengembalian dengan membuat array elemen satu dan memeriksa nilainya. Saya juga mengganti Double.NEGATIVE_INFINITY dengan PI2 dan -PI2 karena ini adalah batas atas dan bawah sudut. Sekarang akhirnya panjangnya kurang dari 1000 karakter!
Saya menggabungkan loop untuk menemukan lokasi orang dan iterator bangunan untuk menyimpan beberapa karakter. Sayangnya ini mengharuskan kita untuk memindahkan pengembalian dari loop dan masih menggunakan jeda tetapi kali ini tanpa label. Saya bergabung max
dan distanceSquared
dan min
dan newDistanceSquared
karena mereka tidak diharuskan pada saat yang sama. Saya berubah Integer.MAX_VALUE
menjadi 2e31-1
. Saya juga membuat konstanta half = 0.5
yang digunakan untuk menghitung sudut-sudut bangunan. Ini lebih pendek dalam versi golf. Secara keseluruhan kami menyimpan 30 karakter lainnya!