Java 8 lambda, 1506 1002 972 942 caratteri
Volevo vincere questa sfida, in quanto è molto interessante. Il risultato (non molto da golf) può essere visto qui:
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;}
Ovviamente questo esiste anche nella versione ungolfed:
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;
}
}
Quindi sembra molto difficile ma è molto più facile di quanto si possa pensare. La mia prima idea è stata quella di utilizzare un algoritmo di intersezione per verificare se una linea dalla mia posizione all'edificio può essere fatta senza intersezioni. Per fare questo ho deciso di utilizzare l'algoritmo Cohen-Sutherland e tracciare linee per tutti e quattro gli angoli dell'edificio. Questo ha funzionato abbastanza bene per i primi test, ma l'ultimo ha fallito. Ho presto scoperto che è un caso in cui non puoi vedere gli angoli ma una parte di un bordo. Quindi ho pensato a una sorta di ray casting come ha fatto @Blue. Ho messo via quella sfida, poiché non ho ottenuto alcun progresso. Poi ho visto la risposta di Blue e mi è venuta in mente la seguente semplice idea: ogni blocco di costruzione ha un angolo in cui nient'altro può essere visto. Devo solo tenere traccia di ciò che può essere visto e ciò che è già nascosto da altri edifici. Questo è tutto!
L'algoritmo funziona come segue: determina l'edificio con la distanza minima dalla persona. Quindi immaginiamo quattro linee tracciate dalla persona agli angoli dell'edificio. Due di questi hanno un valore estremo: l'angolo minimo e massimo in cui è possibile vedere l'edificio. Li prendiamo come un intervallo e li confrontiamo con altri edifici di cui sappiamo che possono essere visti (nessuno all'inizio). Gli intervalli possono sovrapporsi, includersi a vicenda o non toccare affatto. Confronto le gamme e ottengo alcune nuove gamme dell'edificio che non sono nascoste dagli edifici visualizzabili. Se rimane qualcosa dopo averlo confrontato con gli edifici in vista, anche l'edificio è visibile. Aggiungiamo l'intervallo di angoli rimanenti all'elenco di intervalli da confrontare e iniziamo con l'edificio successivo con la distanza più lunga successiva.
A volte gli intervalli possono sovrapporsi in modo da finire con un intervallo di 0 gradi. Questi intervalli verranno filtrati per non aggiungere erroneamente un edificio che non è nemmeno visualizzabile.
Spero che qualcuno abbia capito questa spiegazione :)
So che questo codice non è molto giocato a golf, lo farò al più presto.
È stato un compito davvero impegnativo. Pensavi di aver trovato una soluzione che funzionasse ma invece sei ancora lontana. Penso che questa soluzione funzioni abbastanza bene. Non è molto veloce ma almeno funziona;) Grazie per quel puzzle!
Aggiornare
Ho trovato il tempo di giocare a golf in un'unica funzione, che può essere trasformata in lambda. Tutte le funzioni sono state chiamate solo una volta e quindi possono essere inserite in un metodo. Sono passato dagli elenchi ai set in quanto ciò salva alcuni caratteri aggiuntivi. Le dichiarazioni sono state messe insieme. I confronti sono stati messi insieme e i personaggi sono stati sostituiti da lì valore ASCII. Il confronto dei range può essere espresso come molti ternari. Alcuni trucchi qua e là per impedire espressioni lunghe come Double.NEGATIVE_INFINITY sono state fatte. Laddove possibile, vengono eseguite le assegnazioni in linea. Per risparmiare un po 'di più sono passato dal confronto degli angoli in gradi al confronto dei radianti. L'intero cambiamento ha salvato oltre 500 caratteri, spero di ottenere tutto meno di 1000;)
Ho rimosso i generici ove possibile e ho abbreviato il confronto di ritorno creando un array di un elemento e verificando invece il suo valore. Ho anche sostituito Double.NEGATIVE_INFINITY con PI2 e -PI2 in quanto questi sono i limiti superiore e inferiore degli angoli. Ora è finalmente meno di 1000 caratteri!
Ho unito i loop per trovare la posizione delle persone e l'iteratore dell'edificio per salvare alcuni personaggi. Sfortunatamente questo ci impone di spostare il ritorno fuori dal ciclo e di usare ancora una pausa, ma questa volta senza un'etichetta. Mi sono unito max
e distanceSquared
e min
e newDistanceSquared
poiché non sono richiesti allo stesso tempo. Ho cambiato Integer.MAX_VALUE
in 2e31-1
. Inoltre ho creato una costante half = 0.5
che viene utilizzata per calcolare gli angoli dell'edificio. Questo è più breve nella versione golf. Nel complesso abbiamo salvato altri 30 personaggi!