英文:
Smarter way to calculate adjacent mines in Minesweeper using Java 8 Streams
问题
嘿,大家好,我在一年前开始编程,最近发现了流(streams)。所以我决定尽可能地使用流来完成我以前的任务,以便逐渐熟悉它们。我知道强制使用流可能不是明智的,但这只是一种练习。
我以前的一个任务是编写扫雷游戏,现在我正在尝试找到一个更好的解决方案,用于在点击一个格子时计算相邻的地雷数量。
一些细节:
我将一堆地雷存储在一个 Mine[] 数组中(arsenal.getArsenal()),每个地雷都有 x 和 y 值。每当我点击一个格子时,我需要计算所有在点击格子周围的地雷数量(从 x-1,y-1 到 x+1,y+1)。
我的当前解决方案是:
private int calculateNearby(int x, int y) {
return (int) Arrays.stream(arsenal.getArsenal())
.filter(mine ->
mine.getX() == x + 1 && mine.getY() == y ||
mine.getX() == x && mine.getY() == y + 1 ||
mine.getX() == x - 1 && mine.getY() == y ||
mine.getX() == x && mine.getY() == y - 1 ||
mine.getX() == x - 1 && mine.getY() == y - 1 ||
mine.getX() == x - 1 && mine.getY() == y + 1 ||
mine.getX() == x + 1 && mine.getY() == y - 1 ||
mine.getX() == x + 1 && mine.getY() == y + 1)
.count();
}
private int calculateNearby(int x, int y) {
return (int) Arrays.stream(arsenal.getArsenal())
.filter(mine -> {
boolean b = false;
for (int i = -1; i < 2; ++i) {
for (int j = -1; j < 2; ++j) {
if ((i != 0 || j != 0) && mine.getX() == x + i && mine.getY() == y + j) {
b = true;
}
}
}
return b;
})
.count();
}
这两个解决方案都可以正常工作,但第一个解决方案因为涉及所有的情况而显得“不太好看”,而第二个解决方案使用了循环,我基本上试图避免使用循环。
如果你能展示一个更好(最好更短)的使用流的解决方案,那就太好了!😄
如果已经有关于这个问题的讨论帖子,我很抱歉。我真的努力寻找相关内容,但要么没有找到,要么我使用了错误的关键词。
英文:
Hey guys I started programming a year ago and recently discovered streams. So I decided to complete my old tasks using streams whenever I could just to get used to them. I know it might not be smart to force using them but it's just practice.
One of my old tasks was to program Minesweeper and right now I try to find a better solution for counting adjacent mines whenever I click a field.
Some details:
I saved a bunch of mines in a Mine[] (arsenal.getArsenal()) and each of the mines has an x and y value. Whenever I click on a field I need to count all mines around the clicked field (from x-1,y-1 till x+1,y+1).
My current solutions are:
private int calculateNearby(int x, int y) {
return (int) Arrays.stream(arsenal.getArsenal())
.filter(mine -> mine.getX() == x + 1 && mine.getY() == y
|| mine.getX() == x && mine.getY() == y + 1
|| mine.getX() == x - 1 && mine.getY() == y
|| mine.getX() == x && mine.getY() == y - 1
|| mine.getX() == x - 1 && mine.getY() == y - 1
|| mine.getX() == x - 1 && mine.getY() == y + 1
|| mine.getX() == x + 1 && mine.getY() == y - 1
|| mine.getX() == x + 1 && mine.getY() == y + 1)
.count();
}
private int calculateNearby(int x, int y) {
return (int) Arrays.stream(arsenal.getArsenal())
.filter(mine ->
{
boolean b = false;
for (int i = -1; i < 2; ++i) {
for (int j = -1; j < 2; ++j) {
if ((x != 0 || y != 0) && mine.getX() == x + i && mine.getY() == y + j) {
b = true;
}
}
}
return b;
})
.count();
}
Both solutions work fine but the first looks "wrong" because of all the cases and the seconds uses for-loops which I basically tried to avoid using.
It would be nice if you could show me a better (ideally shorter) solution using streams.
I'm sorry if there's already a thread about this. I really tried to find anything related but there either isn't anything or I searched for the wrong keywords.
答案1
得分: 3
你可以简化流程条件。不需要逐个检查getX()是否等于x、x-1或x+1,只需检查getX()是否大于或等于x-1且小于或等于x+1,对于getY()也是一样的。
return (int) Arrays.stream(arsenal.getArsenal())
.filter(mine -> mine.getX() >= x - 1 && mine.getX() <= x + 1
&& mine.getY() >= y - 1 && mine.getY() <= y + 1)
.count();
你还可以创建一个方法来进行检查,以使代码更易读。
private int calculateNearby(int x, int y) {
return (int) Arrays.stream(arsenal.getArsenal())
.filter(mine -> inRange(mine.getX(), x)
&& inRange(mine.getY(), y))
.count();
}
private boolean inRange(int actual, int target) {
return actual >= target - 1 && actual <= target + 1;
}
英文:
You can simplify your stream condition. Instead of checking each case if getX() equals x, x-1 or x+1 you can just check if getX() is greater or equals than x-1 and smaller or equals x+1. The same for getY().
return (int) Arrays.stream(arsenal.getArsenal())
.filter(mine -> mine.getX() >= x - 1 && mine.getX() <= x + 1
&& mine.getY() >= y - 1 && mine.getY() <= y + 1)
.count();
You could also create a method for the check to make the code more readable.
private int calculateNearby(int x, int y) {
return (int) Arrays.stream(arsenal.getArsenal())
.filter(mine -> inRange(mine.getX(), x)
&& inRange(mine.getY(), y))
.count();
}
private boolean inRange(int actual, int target) {
return actual >= target - 1 && actual <= target + 1;
}
答案2
得分: 1
另一种方法是检查每个方向上的绝对距离是否小于或等于1:
private int calculateNearby(int x, int y) {
return (int) Arrays.stream(arsenal.getArsenal())
.filter(mine -> Math.abs(mine.getX() - x) <= 1 && Math.abs(mine.getY() - y) <= 1)
.count();
}
请注意,这也会计算位于点(x, y)的地雷,而问题中的代码不会这样做。
英文:
Another way is to check if the absolute distance in each direction is less than or equal to 1:
private int calculateNearby(int x, int y) {
return (int) Arrays.stream(arsenal.getArsenal())
.filter(mine -> Math.abs(mine.getX() - x) <= 1 && Math.abs(mine.getY() - y) <= 1)
.count();
}
Note that this also counts a mine which is at the point (x, y) which is not the case with the code in the question.
答案3
得分: 0
当一个矿坑相邻时是什么情况?当其水平或垂直方向上仅相隔一个领域。
水平距离为 Math.abs(mine.getX() - x)
,垂直距离为 Math.abs(mine.getY() - y)
。无论是 -1
还是 1
都没关系,只要相隔一个领域。
但是它不应该超过一个领域,无论是垂直还是水平方向,所以 max(dx, dy) == 1
。
Predicate<Mine> isAdjacent = mine ->
Math.max(
Math.abs(mine.getX() - x),
Math.abs(mine.getY() - y)
) == 1;
return (int) Arrays.stream(arsenal.getArsenal())
.filter(isAdjacent)
.count();
英文:
When is a mine adjacent? When its only one field horizontally or vertically away.
Horizontal distance is Math.abs(mine.getX() - x)
, vertical distance is Math.abs(mine.getY() - y)
. It doesn't matter if its -1
or 1
, just that it is one field away.
But it shouldn't be more than one field, either vertical or horizontal be away, so max(dx, dy) == 1
.
Predicate<Mine> isAdjacent = mine ->
Math.max(
Math.abs(mine.getX() - x)
Math.abs(mine.getY() - y)
) == 1;
return (int) Arrays.stream(arsenal.getArsenal())
.filter(isAdjacent)
.count();
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论