Java 8 lambda, 1506 1002 972 942 ký tự
Tôi muốn đánh bại thử thách này, vì nó rất thú vị. Kết quả (không phải là rất golf) có thể được nhìn thấy ở đây:
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;}
Tất nhiên điều này cũng tồn tại trong phiên bản chưa được chỉnh sửa:
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;
}
}
Vì vậy, nó có vẻ rất khó khăn nhưng nó dễ hơn mọi người nghĩ. Ý tưởng đầu tiên của tôi là sử dụng một số thuật toán giao cắt để kiểm tra xem một đường từ vị trí của tôi đến tòa nhà có thể được thực hiện mà không có bất kỳ giao lộ nào không. Để làm điều này, tôi quyết định sử dụng thuật toán Cohen-Sutherland và vẽ các đường thẳng cho tất cả bốn góc của tòa nhà. Điều này làm việc khá tốt cho các thử nghiệm đầu tiên, nhưng thử nghiệm cuối cùng đã thất bại. Tôi sớm phát hiện ra rằng đó là trường hợp bạn không thể nhìn thấy các góc mà là một phần của cạnh. Vì vậy, tôi đã nghĩ về một số loại đúc tia như @Blue đã làm nó. Tôi đặt thử thách đó đi, vì tôi không có chút tiến bộ nào. Sau đó, tôi thấy câu trả lời của Blue và ý tưởng đơn giản sau đây xuất hiện trong đầu tôi: Mỗi tòa nhà chặn một số góc mà không có gì khác có thể nhìn thấy. Tôi chỉ cần theo dõi những gì có thể nhìn thấy và những gì đã bị ẩn bởi các tòa nhà khác. Đó là nó!
Thuật toán hoạt động như sau: Nó xác định tòa nhà có khoảng cách nhỏ nhất với người. Sau đó, chúng tôi tưởng tượng bốn đường được vẽ từ người đến các góc của tòa nhà. Hai trong số này có một giá trị cực trị: Góc tối thiểu và tối đa mà tòa nhà có thể được nhìn thấy. Chúng tôi xem chúng như một phạm vi và so sánh chúng với các tòa nhà khác mà chúng tôi biết rằng chúng có thể được nhìn thấy (không có gì ở đầu). Các phạm vi có thể chồng lấp, bao gồm lẫn nhau hoặc không chạm vào tất cả. Tôi so sánh các phạm vi và nhận được một số phạm vi mới của tòa nhà không bị ẩn bởi các tòa nhà có thể xem được. Nếu có một cái gì đó còn lại sau khi so sánh nó với các tòa nhà trong tầm nhìn, tòa nhà cũng có thể xem được. Chúng tôi thêm phạm vi góc còn lại vào danh sách các phạm vi để so sánh và bắt đầu với tòa nhà tiếp theo với khoảng cách dài hơn tiếp theo.
Đôi khi các phạm vi có thể trùng nhau theo cách mà tôi kết thúc với phạm vi 0 độ. Các phạm vi này sẽ được lọc để không nhầm lẫn thêm một tòa nhà thậm chí không thể xem được.
Tôi hy vọng ai đó hiểu lời giải thích này :)
Tôi biết mã này không được đánh gôn nhiều, tôi sẽ làm điều này càng sớm càng tốt.
Đó là một nhiệm vụ thực sự thách thức. Bạn nghĩ rằng bạn đã tìm thấy một giải pháp hiệu quả nhưng thay vào đó bạn vẫn ở xa. Tôi nghĩ rằng giải pháp này hoạt động khá tốt. Nó không nhanh lắm nhưng ít nhất là nó hoạt động;) Cảm ơn vì câu đố đó!
Cập nhật
Tôi đã tìm thấy thời gian để chơi golf toàn bộ thành một chức năng duy nhất, do đó có thể biến thành lambda. Tất cả các hàm chỉ được gọi một lần và do đó có thể được đưa vào một phương thức. Tôi đã chuyển từ danh sách sang bộ vì điều này sẽ lưu một số ký tự bổ sung. Các tờ khai đã được đặt cùng nhau. Các so sánh đã được đặt cùng nhau và các ký tự được thay thế bằng giá trị ascii. Phạm vi so sánh có thể được thể hiện như nhiều chim nhạn. Một số thủ thuật ở đây và ở đó để ngăn chặn các biểu thức dài như Double.NEGECT_INFINITY đã được thực hiện. Trường hợp có thể khẳng định nội tuyến được thực hiện. Để tiết kiệm hơn một chút, tôi chuyển từ so sánh các góc theo độ sang so sánh radian. Toàn bộ thay đổi đã lưu hơn 500 ký tự, tôi hy vọng sẽ có được tất cả dưới 1000 ký tự;)
Tôi đã loại bỏ các tổng quát khi có thể và rút ngắn so sánh trả về bằng cách tạo một mảng một phần tử và thay vào đó kiểm tra giá trị của nó. Tôi cũng đã thay thế Double.NEGECT_INFINITY bằng PI2 và -PI2 vì đây là giới hạn trên và dưới của các góc. Bây giờ cuối cùng nó cũng dài dưới 1000 ký tự!
Tôi đã hợp nhất các vòng lặp để tìm vị trí người và trình vòng lặp tòa nhà để lưu một số ký tự. Thật không may, điều này đòi hỏi chúng ta phải di chuyển trở lại khỏi vòng lặp và vẫn sử dụng thời gian nghỉ nhưng lần này không có nhãn. Tôi sáp nhập max
và distanceSquared
và min
và newDistanceSquared
khi họ không phải cùng một lúc. Tôi đổi Integer.MAX_VALUE
thành 2e31-1
. Ngoài ra tôi đã tạo ra một hằng số half = 0.5
được sử dụng để tính toán các góc của tòa nhà. Đây là ngắn hơn trong phiên bản golf. Nhìn chung, chúng tôi đã lưu thêm 30 ký tự!