如何以递归方式识别网格中特定类型的单元格?

时间:2015-06-13 01:18:26

标签: recursion f# tail-recursion

我正在学习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}。

2 个答案:

答案 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