2 people like it.
Like the snippet!
Find largest mass in 2D array
This is a modification of the flood fill algorithm to find the largest contiguous block of items in a 2D array. Also includes a simple flood fill finder given a canvas and the target point
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
143:
144:
145:
146:
147:
148:
149:
150:
151:
152:
153:
154:
155:
156:
157:
158:
159:
160:
161:
162:
163:
164:
165:
166:
167:
168:
169:
170:
171:
172:
173:
174:
|
type Board<'T> = 'T[,]
type X = int
type Y = int
type Position = X * Y
type Processed = Position list ref
type Earth =
| Land
| Water
let board = array2D [[Land; Land; Land; Water;];
[Water; Land; Land; Water;];
[Land; Water; Water; Water;];
[Water; Land; Land; Water;]]
(*
Helper methods to move the position around
*)
let moveRight position =
let (x,y) = position
(x + 1, y)
let moveLeft position =
let (x,y) = position
(x - 1, y)
let moveUp position =
let (x,y) = position
(x, y + 1)
let moveDown position =
let (x,y) = position
(x, y - 1)
(*
Size helpers
*)
let xSize board = Array2D.length1 board
let ySize board = Array2D.length2 board
let offBoard position board =
let (x,y) = position
x < 0 || y < 0 || x >= (xSize board) || y >= (ySize board)
(*
Alias to push elements onto a list
*)
let markPosition position previousSpots = position::previousSpots
(*
Determines if the position on the board equals the target
*)
let positionOnTarget position board target =
if offBoard position board then
false
else
let (x, y) = position
(Array2D.get board x y) = target
(*
Alias to find if we already processed a position
*)
let positionExists position list =
List.exists(fun pos -> pos = position) list
(*
Iterate over each element in a 2d array, passing the x and y
coordinate and the board, to the supplied function
which can return an item. The items are all cons together
and the function returns a new list
*)
let forEachElement (applier:(X -> Y -> Board<'a> -> 'b)) (twoDimArray:Board<'a>) =
let mutable items = []
for x in 0..(xSize board) do
for y in 0..(ySize board) do
items <- (applier x y twoDimArray)::items
items
(*
Looks for a specified contigoius block
and keeps track of processed positions using a
reference cell of a list of positions (supplied by the caller)
*)
let findMassStartingAt (position:Position) (board:Board<'A>) (target:'A) (processed:Processed) =
let rec findMassStartingAt' position currentMass =
// if you move off the board return
if offBoard position board then
currentMass
// if you already processed this position then don't do anything
else if positionExists position !processed then
currentMass
else
// branch out left, up, right, and down and see what you can find
let up = moveUp position
let down = moveDown position
let left = moveLeft position
let right = moveRight position
let found = positionOnTarget position board target
processed := position::!processed;
match found with
| true ->
position::currentMass
|> findMassStartingAt' up
|> findMassStartingAt' down
|> findMassStartingAt' left
|> findMassStartingAt' right
| false ->
// if you didn't find anything return the masses that you
// found prevoiusly
currentMass
findMassStartingAt' position []
(*
Finds all contiguous blocks of the specified type
and returns a list of lists (each list is the points for a specific
block)
*)
let getContiguousBlocks board target =
// keep track of all processed positions across the recursions
let processed = ref []
// go through each board element and find masses starting at the
// the current position
// filter out any positions that found no masses
let massFinder x y board = findMassStartingAt (x, y) board target processed
forEachElement massFinder board
|> List.filter (fun i -> not (List.isEmpty i))
(*
Returns a list of points representing a contigious block
of the type that the point was at.
*)
let floodFillArea (point:Position) (canvas:Board<'T>) =
let (x, y) = point
let itemAtPoint = Array2D.get canvas x y
let processed = ref []
findMassStartingAt point canvas itemAtPoint processed
(*
Test functions to run it
*)
let masses = getContiguousBlocks board Land
let largestList = List.maxBy(List.length) masses
let massAt = floodFillArea (2, 2) board
System.Console.WriteLine("Largest mass is " + (List.length largestList).ToString());
|
type X = int
Full name: Script.X
Multiple items
val int : value:'T -> int (requires member op_Explicit)
Full name: Microsoft.FSharp.Core.Operators.int
--------------------
type int = int32
Full name: Microsoft.FSharp.Core.int
--------------------
type int<'Measure> = int
Full name: Microsoft.FSharp.Core.int<_>
type Y = int
Full name: Script.Y
type Position = X * Y
Full name: Script.Position
type Processed = Position list ref
Full name: Script.Processed
type 'T list = List<'T>
Full name: Microsoft.FSharp.Collections.list<_>
Multiple items
val ref : value:'T -> 'T ref
Full name: Microsoft.FSharp.Core.Operators.ref
--------------------
type 'T ref = Ref<'T>
Full name: Microsoft.FSharp.Core.ref<_>
type Earth =
| Land
| Water
Full name: Script.Earth
union case Earth.Land: Earth
union case Earth.Water: Earth
val board : Earth [,]
Full name: Script.board
val array2D : rows:seq<#seq<'T>> -> 'T [,]
Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.array2D
val moveRight : int * 'a -> int * 'a
Full name: Script.moveRight
val position : int * 'a
val x : int
val y : 'a
val moveLeft : int * 'a -> int * 'a
Full name: Script.moveLeft
val moveUp : 'a * int -> 'a * int
Full name: Script.moveUp
val position : 'a * int
val x : 'a
val y : int
val moveDown : 'a * int -> 'a * int
Full name: Script.moveDown
val xSize : board:'a [,] -> int
Full name: Script.xSize
val board : 'a [,]
module Array2D
from Microsoft.FSharp.Collections
val length1 : array:'T [,] -> int
Full name: Microsoft.FSharp.Collections.Array2D.length1
val ySize : board:'a [,] -> int
Full name: Script.ySize
val length2 : array:'T [,] -> int
Full name: Microsoft.FSharp.Collections.Array2D.length2
val offBoard : int * int -> board:'a [,] -> bool
Full name: Script.offBoard
val position : int * int
val markPosition : position:'a -> previousSpots:'a list -> 'a list
Full name: Script.markPosition
val position : 'a
val previousSpots : 'a list
val positionOnTarget : int * int -> board:'a [,] -> target:'a -> bool (requires equality)
Full name: Script.positionOnTarget
val board : 'a [,] (requires equality)
val target : 'a (requires equality)
val get : array:'T [,] -> index1:int -> index2:int -> 'T
Full name: Microsoft.FSharp.Collections.Array2D.get
val positionExists : position:'a -> list:'a list -> bool (requires equality)
Full name: Script.positionExists
val position : 'a (requires equality)
Multiple items
val list : 'a list (requires equality)
--------------------
type 'T list = List<'T>
Full name: Microsoft.FSharp.Collections.list<_>
Multiple items
module List
from Microsoft.FSharp.Collections
--------------------
type List<'T> =
| ( [] )
| ( :: ) of Head: 'T * Tail: 'T list
interface IEnumerable
interface IEnumerable<'T>
member Head : 'T
member IsEmpty : bool
member Item : index:int -> 'T with get
member Length : int
member Tail : 'T list
static member Cons : head:'T * tail:'T list -> 'T list
static member Empty : 'T list
Full name: Microsoft.FSharp.Collections.List<_>
val exists : predicate:('T -> bool) -> list:'T list -> bool
Full name: Microsoft.FSharp.Collections.List.exists
val pos : 'a (requires equality)
val forEachElement : applier:(X -> Y -> Board<'a> -> 'b) -> twoDimArray:Board<'a> -> 'b list
Full name: Script.forEachElement
val applier : (X -> Y -> Board<'a> -> 'b)
type Board<'T> = 'T [,]
Full name: Script.Board<_>
val twoDimArray : Board<'a>
val mutable items : 'b list
val x : int32
val y : int32
val findMassStartingAt : X * Y -> board:Board<'A> -> target:'A -> processed:Processed -> (int * int) list (requires equality)
Full name: Script.findMassStartingAt
val position : Position
val board : Board<'A> (requires equality)
val target : 'A (requires equality)
val processed : Processed
val findMassStartingAt' : (int * int -> (int * int) list -> (int * int) list)
val currentMass : (int * int) list
val up : int * int
val down : int * int
val left : int * int
val right : int * int
val found : bool
val getContiguousBlocks : board:Board<'a> -> target:'a -> (int * int) list list (requires equality)
Full name: Script.getContiguousBlocks
val board : Board<'a> (requires equality)
val processed : Position list ref
val massFinder : (X -> Y -> Board<'a> -> (int * int) list) (requires equality)
val x : X
val y : Y
val filter : predicate:('T -> bool) -> list:'T list -> 'T list
Full name: Microsoft.FSharp.Collections.List.filter
val i : (int * int) list
val not : value:bool -> bool
Full name: Microsoft.FSharp.Core.Operators.not
val isEmpty : list:'T list -> bool
Full name: Microsoft.FSharp.Collections.List.isEmpty
val floodFillArea : X * Y -> canvas:Board<'T> -> (int * int) list (requires equality)
Full name: Script.floodFillArea
val point : Position
val canvas : Board<'T> (requires equality)
val itemAtPoint : 'T (requires equality)
val masses : (int * int) list list
Full name: Script.masses
val largestList : (int * int) list
Full name: Script.largestList
val maxBy : projection:('T -> 'U) -> list:'T list -> 'T (requires comparison)
Full name: Microsoft.FSharp.Collections.List.maxBy
val length : list:'T list -> int
Full name: Microsoft.FSharp.Collections.List.length
val massAt : (int * int) list
Full name: Script.massAt
namespace System
type Console =
static member BackgroundColor : ConsoleColor with get, set
static member Beep : unit -> unit + 1 overload
static member BufferHeight : int with get, set
static member BufferWidth : int with get, set
static member CapsLock : bool
static member Clear : unit -> unit
static member CursorLeft : int with get, set
static member CursorSize : int with get, set
static member CursorTop : int with get, set
static member CursorVisible : bool with get, set
...
Full name: System.Console
System.Console.WriteLine() : unit
(+0 other overloads)
System.Console.WriteLine(value: string) : unit
(+0 other overloads)
System.Console.WriteLine(value: obj) : unit
(+0 other overloads)
System.Console.WriteLine(value: uint64) : unit
(+0 other overloads)
System.Console.WriteLine(value: int64) : unit
(+0 other overloads)
System.Console.WriteLine(value: uint32) : unit
(+0 other overloads)
System.Console.WriteLine(value: int) : unit
(+0 other overloads)
System.Console.WriteLine(value: float32) : unit
(+0 other overloads)
System.Console.WriteLine(value: float) : unit
(+0 other overloads)
System.Console.WriteLine(value: decimal) : unit
(+0 other overloads)
More information