

Title  Puzzle: find the equilateral triangles in Visual Basic .NET 
Description  This example describes a puzzle to find the equilateral triangles in Visual Basic .NET. 
Keywords  algorithms, games, graphics, mathematics, example, example program, Windows Forms programming, Visual Basic .NET, VB.NET 
Categories  Graphics, Graphics, Algorithms, Puzzles and Games 


This is a puzzle that was used recently on National Public Radio (NPR). How many equilateral triangles can you make with corners on the points drawn by the program? For this blog, the puzzle has two parts. First see if you can solve the puzzle "manually." Then write a program that solves the puzzle.
The code available for download with this post draws the points and will draw the solution after you fill in the appropriate code. You only need to fill in a small piece of code that initializes the solution, or better yet discovers the solution for you.
The rest of this post shows how parts of the program that I've written for you work. The following code executes when the form loads. (This is where you need to add your code.)


' The points.
Private Points() As PointF
' The solutions.
Private Solutions As List(Of Integer())
' The solution we should display.
Private CurrentSolution As Integer = 100
' Define the points and solutions.
Private Sub Form1_Load(ByVal sender As System.Object, ByVal _
e As System.EventArgs) Handles MyBase.Load
DoubleBuffered = True
' Define the points.
Dim dy As Single = CSng(ClientSize.Height / 4)
Dim dx As Single = CSng(dy / Math.Sqrt(3))
Dim top_x As Single = CSng(ClientSize.Width / 2)
Dim top_y As Single = dy / 2
Points = New PointF() _
{ _
New PointF(top_x  dx, top_y + dy), _
New PointF(top_x + dx, top_y + dy), _
New PointF(top_x  2 * dx, top_y + 2 * dy), _
New PointF(top_x  0 * dx, top_y + 2 * dy), _
New PointF(top_x + 2 * dx, top_y + 2 * dy), _
New PointF(top_x  3 * dx, top_y + 3 * dy), _
New PointF(top_x  1 * dx, top_y + 3 * dy), _
New PointF(top_x + 1 * dx, top_y + 3 * dy), _
New PointF(top_x + 3 * dx, top_y + 3 * dy), _
New PointF(top_x  2 * dx, top_y + 4 * dy), _
New PointF(top_x  0 * dx, top_y + 4 * dy), _
New PointF(top_x + 2 * dx, top_y + 4 * dy) _
}
' Define the solutions.
Solutions = New List(Of Integer())()
' Insert your code here...
End Sub


This code creates the Points array that defines the points. The points are numbered from top to bottom and left to right so the points in the first row have indices 0 and 1, the points in the second row have indices 2, 3, and 4, and so forth.
This code also creates an empty Solutions list. Your code should add arrays of three integers to the list to indicate the indices of points that make up the triangles. For example, the following statement adds a triangle to the list that uses points 0, 2, and 3.


Solutions.Add(new int() { 0, 2, 3 })


The program uses the following Paint event handler to draw the points and the solution triangle indicated by the CurrentSolution variable.


' Draw the circles and any triangles currently defined.
Private Sub Form1_Paint(ByVal sender As Object, ByVal e As _
System.Windows.Forms.PaintEventArgs) Handles Me.Paint
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias
' Draw the points.
Const radius As Single = 5
For Each pt As PointF In Points
e.Graphics.FillEllipse(Brushes.Blue, _
pt.X  radius, pt.Y  radius, _
2 * radius, 2 * radius)
Next pt
' Draw the current solution.
If (CurrentSolution < 0) Then
' Draw all solutions.
For i As Integer = 0 To Solutions.Count  1
DrawSolution(e.Graphics, i)
Next i
Else
' Draw the current solution.
DrawSolution(e.Graphics, CurrentSolution)
End If
End Sub


This code loops through the points and draws them. Then if CurrentSolution is less than 0, the program loops through all of the solutions and calls DrawSolution for each. If CurrentSolution is not less than 0, the program calls DrawSolution to draw just that solution.
The following code shows how the DrawSolution method draws a particular solution.


' Draw a solution.
Private Sub DrawSolution(ByVal gr As Graphics, ByVal _
solution_num As Integer)
If (solution_num >= Solutions.Count) Then Return
' Make a list of the points in this solution.
Dim pts As New List(Of PointF)()
For Each i As Integer In Solutions(solution_num)
pts.Add(Points(i))
Next i
gr.DrawPolygon(Pens.Red, pts.ToArray())
End Sub


If the solution number passed to the method is not a valid solution index, the method returns without doing anything.
If the solution number is valid, the code creates a List of PointF. It loops through the indices stored in the indicated solution and adds their points to the list. It then calls the Graphics object's DrawPolygon method to draw the points. DrawPolygon works with arrays not lists so the code calls the list's ToArray method to convert it into an array.
The rest of the program's code is reasonably straightforward.


' Start showing solutions.
Private Sub btnShowSolutions_Click(ByVal sender As _
System.Object, ByVal e As System.EventArgs) Handles _
btnShowSolutions.Click
' Disable the button.
btnShowSolutions.Enabled = False
' Start at the first solution.
CurrentSolution = 0
' Start the timer.
tmrChangeSolution.Enabled = True
' Redraw.
Refresh()
End Sub
' Show the next solution.
Private Sub tmrChangeSolution_Tick(ByVal sender As _
System.Object, ByVal e As System.EventArgs) Handles _
tmrChangeSolution.Tick
' Increment CurrentSolution. If the result is greater
' than the
' last solution's index, disable the timer.
CurrentSolution += 1
tmrChangeSolution.Enabled = (CurrentSolution < _
Solutions.Count)
' If we're done drawing, enable the button.
btnShowSolutions.Enabled = (Not _
tmrChangeSolution.Enabled)
' Redraw.
Refresh()
End Sub
' Show all of the solutions.
Private Sub btnShowAllSolutions_Click(ByVal sender As _
System.Object, ByVal e As System.EventArgs) Handles _
btnShowAllSolutions.Click
CurrentSolution = 1
tmrChangeSolution.Enabled = False
btnShowSolutions.Enabled = True
Refresh()
End Sub


When you click the Show Solutions button, the program disables that button so you can't press it again. It then sets CurrentSolution = 0 to display the first solution, enables the tmrChangeSolution Timer, and refreshes the form to redraw. The Paint event handler draws the selected solution (number 0). If there are no solutions, then the DrawSolution method draws nothing because the index 0 is not a valid index in the Solutions list.
When the timer ticks, the program increments CurrentSolution. It sets the Timer's Enabled property to true if the new value is still a valid solution index so the Timer runs until the last solution has been displayed. If the timer is no longer enabled, the code reenables the Show Solutions button. The Tick event handler finishes by refreshing the form to redraw it.
Finally if when you click the Show All button, the program sets CurrentSolution to 1 and refreshes to redraw the solution. If you look back at the Paint event handler, you'll see that it calls DrawSolution for every solution if CurrentSolution < 0.
Download the example program and try to solve the puzzle. You can explicitly enter code to create solutions that you figured out yourself, or you can write code to find all of the solutions. Here's a small hint: There are more solutions than you will probably think of at first.
If you find a solution, zip up your project and email it to me. I'll post my solutions (manually solved and automatically solved) and any others that I receive on Monday and Tuesday, November 28 and 29, 2011.
Related links:








