更智能的方式使用Java 8流计算扫雷中相邻的地雷

huangapple go评论75阅读模式
英文:

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 -&gt; mine.getX() == x + 1 &amp;&amp; mine.getY() == y
|| mine.getX() == x &amp;&amp; mine.getY() == y + 1
|| mine.getX() == x - 1 &amp;&amp; mine.getY() == y
|| mine.getX() == x &amp;&amp; mine.getY() == y - 1
|| mine.getX() == x - 1 &amp;&amp; mine.getY() == y - 1
|| mine.getX() == x - 1 &amp;&amp; mine.getY() == y + 1
|| mine.getX() == x + 1 &amp;&amp; mine.getY() == y - 1
|| mine.getX() == x + 1 &amp;&amp; mine.getY() == y + 1)
.count();
}
private int calculateNearby(int x, int y) {
return (int) Arrays.stream(arsenal.getArsenal())
.filter(mine -&gt;
{
boolean b = false;
for (int i = -1; i &lt; 2; ++i) {
for (int j = -1; j &lt; 2; ++j) {
if ((x != 0 || y != 0) &amp;&amp; mine.getX() == x + i &amp;&amp; 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. 更智能的方式使用Java 8流计算扫雷中相邻的地雷

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 -&gt; mine.getX() &gt;= x - 1 &amp;&amp; mine.getX() &lt;= x + 1 
&amp;&amp; mine.getY() &gt;= y - 1 &amp;&amp; mine.getY() &lt;= 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 -&gt; inRange(mine.getX(), x)
&amp;&amp; inRange(mine.getY(), y))
.count();
}
private boolean inRange(int actual, int target) {
return actual &gt;= target - 1 &amp;&amp; actual &lt;= 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 -&gt; Math.abs(mine.getX() - x) &lt;= 1 &amp;&amp; Math.abs(mine.getY() - y) &lt;= 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&lt;Mine&gt; isAdjacent = mine -&gt; 
    Math.max(
        Math.abs(mine.getX() - x)
        Math.abs(mine.getY() - y)
    ) == 1;

return (int) Arrays.stream(arsenal.getArsenal())
        .filter(isAdjacent) 
        .count();

huangapple
  • 本文由 发表于 2020年9月29日 21:37:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/64120784.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定