我正在学习F#而我正在构建一个扫雷应用程序。作为其中的一部分,我试图有一种方法,如果矿井被引爆,引爆所有相邻的地雷。所以,如果我有一个像:
的网格 | 0 | 1 | 2 |
------------------------
0 | E-1 | M | E-2 |
1 | E-2 | E-4 | M |
2 | M | E-3 | M |
我在0,1引爆了矿井,它将在1,2中引爆矿井,然后在2,2引爆矿井。 2.0中的矿井不会引爆,因为它与任何其他矿井都不相邻。
此时我实际上已将该字段实现为列表:
module CellContents =
type T =
| Empty of int
| Mine
| Crater
module Location =
type T = { Row: int; Column: int }
module Cell =
type T =
{ Content : CellContents.T
Location : Location.T }
module Field =
type T = Cell.T list
我遇到的问题是如何从单元格0,1开始,最后列出所有相邻的地雷。所以,我需要一个像(只显示坐标)的列表:
let minesToDetonate = [ {Row=0;Column=1};{Row=1;Column=2};{Row=2;Column=2} ]
我可以毫不费力地获取特定地点的相邻地雷,然后确定该地区的地雷。
我遇到的问题就是以某种方式进行递归,直到找不到相邻的地雷,给我一个我需要引爆的地雷的主列表。
一旦我获得了地雷的主要清单,我就可以引爆它们并建立一个更新的地区,这些地雷将成为陨石坑。
更新 @Kevin的回答有效,但我很难理解它。如果其他人也很难,我正在添加下面的功能,注释和一些更改。
let detonateProximity (field:T) (start:Cell.T) =
let rec inner cells m acc =
match cells with
| [] -> acc
| x::xs ->
match x.Content with
|Mine ->
match proximity m.Location x.Location with
// Continue but don't accumulate
| Self -> inner xs m acc
| Near ->
// See if current cell has already been found
match acc |> List.tryFind (fun t -> t = x) with
// If it has, we're done. Pass out
// an empty list which ends recursion.
|Some _ -> []
// If it wasn't found (this parts hurts my brain)...
// calls function once for rest field and then
// using new starting point on whole field.
// Is this efficient at all?
|None -> List.concat [(inner xs m (x::acc));(inner field x (x::acc))]
// Don't accumulate, continue with rest of mines.
| Far -> inner xs m acc
// Not a mine, keep going, don't accumulate
|_ -> inner xs m acc
// The set ensures no duplicates
Set.ofList (inner field start [])
proximity
函数(未显示)简单地包含了逻辑,该逻辑确定测试的矿是否是参考矿,靠近它或远离它。例如。如果当前单元格与矿山之间的距离为零,则返回Self
{Row = 0,Column = 0}。
答案 0 :(得分:2)
这将返回一组所有引爆的细胞,包括引发连锁反应的细胞。
module Location =
type T = {Row: int; Column: int }
let subtract l1 l2 =
{Row=l1.Row - l2.Row;Column=l1.Column-l2.Colum
let detonate (field:Field.T) (start:Cell.T) =
let rec inner cells m acc =
match cells with
| [] -> acc
| x::xs -> match x.Content with
|Mine ->match subtract m.Location x.Location with
|{Row = 0;Column = 0} -> inner xs m acc
|loc when abs (loc.Row) < 2 && abs (loc.Column) < 2 ->
match acc |> List.tryFind (fun t -> t = x) with
|Some _ -> []
|None -> List.concat [(inner xs m (x::acc));(inner field x (x::acc))]
| _ -> inner xs m acc
|_ -> inner xs m acc
Set.ofList (inner field start [])
如果你只想要一个像问题一样的位置列表,那么它很容易转换:
detonate board {Content=Mine;Location={Row=0;Column=1}}
|> Set.map (fun t -> t.Location)
|> Set.toList
答案 1 :(得分:-2)
好的,所以我不知道F#,所以我要用Python写这个。
def getDetonationList ( startingWithThisCell ) :
seen = set()
current = set ( [startWithThisCell] )
while current :
# Get all the mine cells that are adjacent to every ones
# we are currently exploring. Then remove the ones we're
# currently exploring and the ones we've seen, just so
# we don't duplicate effort later.
nearby = allMinesAdjacentToCells(current) - seen - current
# Mark that we've seen the current ones (add the current
# ones to the overall seen ones
seen.update ( current )
# Now we start over, starting from the newly found mines
current = nearby
# Note that if this is empty, this list will end, which
# means that we will have seen as many as we could have
# seen.
return seen