Java 8 lambda, 1506 1002 972 942 karakter
Bu meydan okumayı yenmek istedim, çünkü çok ilginç. Sonuç (çok golf değil) burada görülebilir:
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;}
Tabii ki bu da ungolfed versiyonunda var:
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;
}
}
Bu yüzden çok zor görünüyor ama birinin düşündüğünden çok daha kolay. İlk fikrim, konumumdan binaya bir hattın herhangi bir kavşak olmadan yapılıp yapılamayacağını kontrol etmek için bazı kavşak algoritmaları kullanmaktı. Bunu yapmak için Cohen-Sutherland algoritmasını kullanmaya ve binanın dört köşesine de çizgiler çizmeye karar verdim. Bu ilk testler için oldukça işe yaradı, ancak sonuncusu başarısız oldu. Kısa süre sonra, köşeleri göremediğiniz bir kenarın bir kısmını görebileceğiniz bir durum olduğunu öğrendim. Ben de @Blue gibi bir tür ray döküm düşündüm. İlerlemediğim için bu zorluğu ortadan kaldırdım. Sonra Blue'nun cevabını gördüm ve aklıma şu basit fikir geldi: Her bina, başka hiçbir şeyin göremeyeceği bir açı engelliyor. Sadece nelerin görülebildiğini ve diğer binalar tarafından neyin gizlendiğini takip etmem gerekiyor. Bu kadar!
Algoritma şu şekilde çalışır: Kişiye en kısa mesafedeki binayı belirler. Sonra kişiden binanın köşelerine dört çizgi çizildiğini hayal ediyoruz. Bunlardan ikisinin aşırı bir değeri vardır: Binanın görülebileceği minimum ve maksimum açı. Onları bir menzil olarak alıyoruz ve görülebileceğini bildiğimiz diğer binalarla karşılaştırıyoruz (başlangıçta yok). Aralıklar üst üste gelebilir, birbirini içerebilir veya hiç dokunmaz. Aralıkları karşılaştırıyorum ve görüntülenebilir binalar tarafından gizlenmeyen bazı yeni aralıklar elde ediyorum. Görünürdeki binalar ile karşılaştırıldıktan sonra kalan bir şey varsa, bina da görülebilir. Kalan açı aralığını karşılaştırılacak aralıklar listesine ekliyoruz ve bir sonraki binaya bir sonraki daha uzun mesafeyle başlıyoruz.
Bazen aralıklar, 0 derecelik bir aralıkla sonuçlanacak şekilde çakışabilir. Bu aralıklar, görüntülenemeyen bir binayı yanlışlıkla eklemeyecek şekilde filtrelenecektir.
Umarım birisi bu açıklamayı anlar :)
Bu kod çok golf değil biliyorum, ben asap yapacağım.
Bu gerçekten zor bir işti. İşe yarayan bir çözüm bulduğunuzu düşündünüz, ancak bunun yerine çok uzaktasınız. Bence bu çözüm gayet iyi çalışıyor. Çok hızlı değil ama en azından işe yarıyor;) Bu bulmaca için teşekkürler!
Güncelleme
Her şeyi tek bir fonksiyona indirmenin zamanını buldum, bu da bir lambdaya dönüştürülebilir. Tüm fonksiyonlar sadece bir kez çağrıldı ve böylece tek bir yönteme dahil edilebilir. Bazı ek karakterleri kaydettiğinden listelerden setlere geçtim. Beyanlar bir araya getirildi. Karşılaştırmalar bir araya getirildi ve karakterlerin yerine ascii değeri geldi. Aralık karşılaştırması birçok üçlü olarak ifade edilebilir. Double.NEGATIVE_INFINITY gibi uzun ifadeleri önlemek için burada ve bazı püf noktaları yapıldı. Mümkün olan hallerde satır içi değerlendirmeler yapılır. Biraz daha fazla tasarruf etmek için açıları derece ile karşılaştırıp radyanları karşılaştırmaya geçtim. Tüm değişiklik 500'den fazla karakter kurtardı, umarım hepsini 1000'in altında almayı umuyorum;)
Mümkün olduğunca jenerikleri kaldırdım ve bir eleman dizisi oluşturarak dönüş karşılaştırmasını kısalttım ve bunun yerine değerini kontrol ettim. Ayrıca açıların üst ve alt sınırları olduğu için Double.NEGATIVE_INFINITY'yi PI2 ve -PI2 ile değiştirdim. Şimdi nihayet 1000 karakter uzunluğunda!
Kişilerin yerini bulmak için döngüleri birleştirdim ve bazı karakterleri kaydetmek için bina yineleyicisini kullandım. Ne yazık ki bu, dönüşü döngüden çıkarmamızı ve hala bir mola vermemizi gerektiriyor, ancak bu sefer etiketsiz. Ben birleşti maxve distanceSquaredve minve newDistanceSquaredaynı anda gerekmez olarak. Değiştim Integer.MAX_VALUEiçin 2e31-1. Ayrıca half = 0.5binanın köşelerini hesaplamak için kullanılan bir sabit oluşturdum . Bu, golf versiyonunda daha kısadır. Genel olarak 30 karakter daha kaydettik!