< Continued from page 1
The first thing I decided was that I would get nowhere without the ability to see the result of different methods of writing the code. So, before writing even one line of code to solve the main problem, I wrote a program that would generate and display the "map" with "X" and "." characters. And, just for more fun, I used a WPF Grid control to display it.
<Grid Name="Map"
Width="600"
Height="750"
ShowGridLines="True">
</Grid>
I put all this in a StackPanel control along with two Button controls, one to create the map and another to count the islands. Other than that, everything else is generated dynamically in the code-behind VB.NET partial class.
I used Const statements to make the code more readable, and to make it easy to change the size of the map for different tests. For example, the map size is controled by these two.
Friend Const LastRow As Int16 = 49
Friend Const LastCol As Int16 = 49
Then I can declare the array of bytes used to create the map this way:
Dim MapArray( _
(LastRow + 1) * (LastCol + 1) - 1) _
As Byte
I declared the array as a one dimensional Byte array to allow me to generate the contents randomly with this very convenient code:
Dim rnd As New Random
rnd.NextBytes(MapArray)
This one dimensional Byte array is converted to a two dimensional array of "X" and "." characters with couple of nested loops.
For i As Int16 = 0 To LastRow
For j As Int16 = 0 To LastCol
If MapArray(i * (LastRow + 1) + j) Then MapChars(i, j) = "." _
Else MapChars(i, j) = "X"
Next
Next
Now the WPF fun begins! A Grid in WPF initially consists of just one big cell. If you need more, you have to add them. I do that with two loops - one for the rows and one for the columns. (Only the columns are shown below.)
For j As Int16 = 0 To LastCol
Dim gridCol = New ColumnDefinition
Map.ColumnDefinitions.Add(gridCol)
Next
In XAML, this would look like this.
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
... repeated thirty or forty times ...
</Grid.ColumnDefinitions>
But in code, I can change two numbers and generate maps of different sizes easily!
The last step in generating the map is to add TextBlock controls to each of the cells. Another couple of nested loops does this:
For i As Int16 = 0 To LastRow
For j As Int16 = 0 To LastCol
Dim tb As New TextBlock
tb.FontFamily = _
New FontFamily("Courier New")
tb.Text = MapChars(i, j)
Grid.SetRow(tb, i)
Grid.SetColumn(tb, j)
Grid.SetZIndex(tb, 1)
Map.Children.Add(tb)
Next
Next
Row, Column, and ZIndex are examples of what are called attached properties in WPF. That means that they're really properties of the parent (Grid in this case) but they're set in the Child. In XAML, this would look like this:
<TextBlock
Grid.Row="1"
Grid.Column="1"
Panel.ZIndex="1"
FontFamily="Courier New">
Any Text
</TextBlock>
In other words, this TextBlock should be in Row 1 Column 1 and the ZIndex should be set to 1. ZIndex is required in this case because later, I set the background of a Canvas control. If the ZIndex of the TextBlock control isn't greater - 0 is the default - the Canvas control overlays the TextBlock. Also, ZIndex is actually registered to the Panel control, not the Grid. But since Grid inherits from Panel, I can write the code as though it's being set in the Grid control. Confused yet? I am. Welcome to the brave new world of WPF.
It was easier for me to visualize what the code is doing by keeping in mind that, conceptually at least, a TextBlock element like that is being added inside the Grid for each cell.
Once all this is done, the code creates a map of any size with randomly generated 'X' and '.' contents. Here's a 5 by 5 map. (This one has 3 "islands".)
--------
Click Here to display the illustration
Click the Back button on your browser to return
--------
This much of the code is WPF and XAML. The rest is pure programming to solve the original problem: Counting the "islands". That starts on the next page.
previous post