recursion - How to recursively identify cells of specific type in grid? -
i'm learning f# , i'm building minesweeper app. part of that, i'm trying have method detonates adjacent mines if mine detonated, recursively. if have grid like:
| 0 | 1 | 2 | ------------------------ 0 | e-1 | m | e-2 | 1 | e-2 | e-4 | m | 2 | m | e-3 | m |
and detonate mine in 0,1, in turn detonate mine in 1,2 , in turn detonate mine in 2,2. mine in 2,0 not detonate because isn't adjacent of others.
at point i've implemented field list:
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
what i'm having trouble how start cell 0,1 , end list of mines adjacent. so, need list (showing coordinates):
let minestodetonate = [ {row=0;column=1};{row=1;column=2};{row=2;column=2} ]
i have no trouble getting adjacent mines specific location , determining mines group.
what i'm having trouble with getting recurse somehow , go until there no mines found adjacent, giving me master list mines need detonate.
once master list of mines, can detonate them , build updated field mines becoming craters.
update @kevin's answer worked, had hard time understanding it. in case others have hard time, i'm adding function below, comments , couple of changes.
let detonateproximity (field:t) (start:cell.t) = let rec inner cells m acc = match cells | [] -> acc | x::xs -> match x.content |mine -> match proximity m.location x.location // continue don't accumulate | self -> inner xs m acc | near -> // see if current cell has been found match acc |> list.tryfind (fun t -> t = x) // if has, we're done. pass out // empty list ends recursion. |some _ -> [] // if wasn't found (this parts hurts brain)... // calls function once rest field , // using new starting point on whole field. // efficient @ all? |none -> list.concat [(inner xs m (x::acc));(inner field x (x::acc))] // don't accumulate, continue rest of mines. | far -> inner xs m acc // not mine, keep going, don't accumulate |_ -> inner xs m acc // set ensures no duplicates set.oflist (inner field start [])
the proximity
function (not shown) wraps logic determines if tested mine reference mine, close or far it. e.g. self
returned if distance between current cell , mine 0 {row=0, column=0}.
this return set of detonated cells including 1 started chain reaction.
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 | [] -> acc | x::xs -> match x.content |mine ->match subtract m.location x.location |{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) |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 [])
if want list of locations in question easy convert so:
detonate board {content=mine;location={row=0;column=1}} |> set.map (fun t -> t.location) |> set.tolist
Comments
Post a Comment