VB.Net - WordSearch
Overview
This is a WordSearch game, intended for playing either at your computer, or alternatively you can print it out and play in the traditional fashion with pen and paper. Each new game uses twenty five random words chosen from a 46K+ word list. Any single line (horizontal, vertical, or diagonal) in a letter grid may contain more than one word but no two words in a single line will overlap. Words may be as spelled in the listbox or reversed.
The Core Classes
The Cell Class
''' <summary>
''' Cell Class -
''' Each possible line has a line index. There are 76 distinct lines used.
''' Horizontal, vertical, and 2 * diagonal. Each Cell belongs to 4 lines.
''' The Boolean available() array holds values for each cells availability
''' in any of the 4 possible directions.
''' </summary>
''' <remarks></remarks>
Public Class Cell
Public line() As Integer = {0, 0, 0, 0}
Public text As String
Public available() As Boolean = {True, True, True, True}
End Class
The Game Class
''' <summary>
''' The Game class is the singleton core class initiated on app. load and used for the duration of app. running time.
''' </summary>
''' <remarks></remarks>
Public Class Game
Dim cells As New List(Of Cell)
Dim words() As String
Dim rn As New Random
''' <summary>
''' This default constructor initializes the cells list containing the 225 Cells used throughout the game.
''' Also the 46K+ words used are read into the words array.
''' </summary>
''' <remarks></remarks>
Public Sub New()
cells.AddRange(Enumerable.Range(0, 15 * 15).Select(Function(x) New Cell))
For r As Integer = 1 To 15
For c As Integer = 1 To 15
cells((r - 1) * 15 + c - 1).line(0) = r
cells((r - 1) * 15 + c - 1).line(1) = c + 15
Next
Next
For r As Integer = 4 To 15
Dim c As Integer = 0
For r2 As Integer = r - 1 To 0 Step -1
cells((r2) * 15 + c).line(2) = 30 + r - 3
c += 1
Next
Next
For c As Integer = 1 To 11
Dim r As Integer = 14
For c2 As Integer = c To 14
cells(r * 15 + c2).line(2) = 42 + c
r -= 1
Next
Next
For r As Integer = 4 To 15
Dim c As Integer = 14
For r2 As Integer = r - 1 To 0 Step -1
cells((r2) * 15 + c).line(3) = 53 + r - 3
c -= 1
Next
Next
For c As Integer = 13 To 3 Step -1
Dim r As Integer = 14
For c2 As Integer = c To 0 Step -1
cells(r * 15 + c2).line(3) = 65 + (14 - c)
r -= 1
Next
Next
words = My.Resources._429Wild.Split(New String() {vbCr, vbLf}, StringSplitOptions.None)
End Sub
''' <summary>
''' The createNew function creates a 15 by 15 grid of single letters, comprised of 25 random words,
''' then random letters in the remaining empty cells.
''' No 2 words in the same line (horizontal, vertical, 2 * diagonal) will overlap.
''' Words on different lines can overlap.
''' </summary>
''' <param name="frm">Instance of frmProgress. Provides graphical indication of progress.</param>
''' <returns></returns>
''' <remarks></remarks>
Public Function createNew(ByVal frm As frmProgress) As NewGame
Dim wordList As New List(Of String)
Do
Dim lineIndex As Integer = rn.Next(1, 77)
Dim lineCells() As Cell = cells.Where(Function(c) c.line.Contains(lineIndex)).ToArray
Dim reversed As Boolean = CBool(rn.Next(0, 2))
Select Case lineIndex
Case 16 To 30
If Not reversed Then
lineCells = lineCells.OrderBy(Function(c) c.line(0)).ToArray
Else
lineCells = lineCells.OrderByDescending(Function(c) c.line(0)).ToArray
End If
Case Else
If Not reversed Then
lineCells = lineCells.OrderBy(Function(c) c.line(1)).ToArray
Else
lineCells = lineCells.OrderByDescending(Function(c) c.line(1)).ToArray
End If
End Select
Dim x2 As Integer = 0
Dim availableIndex As Integer = If(lineIndex >= 1 And lineIndex <= 15, 0, _
If(lineIndex >= 16 And lineIndex <= 30, 1, _
If(lineIndex >= 31 And lineIndex <= 53, 2, _
If(lineIndex >= 54 And lineIndex <= 76, 3, -1))))
Dim noSpace As Boolean = False
Dim startAt As Integer = 0
If Not lineCells(0).available(availableIndex) Then
For x As Integer = 1 To lineCells.GetUpperBound(0)
If lineCells(x).available(availableIndex) Then
startAt = x
Exit For
End If
Next
If startAt = 0 Then noSpace = True
End If
If noSpace Then Continue Do
Dim spaces As New List(Of Point)
spaces.Add(New Point(startAt, startAt))
For x As Integer = startAt To lineCells.GetUpperBound(0)
If lineCells(x).available(availableIndex) Then
spaces(spaces.Count - 1) = New Point(spaces.Last.X, x)
Else
For x2 = x + 1 To lineCells.GetUpperBound(0)
If lineCells(x2).available(availableIndex) Then
spaces.Add(New Point(x2, x2))
x = x2 + 1
Exit For
End If
Next
End If
Next
Dim maxLength As Integer = spaces.Max(Function(p) p.Y - p.X + 1)
Dim maxLengthPoint As Point = spaces.First(Function(p) p.Y - p.X + 1 = maxLength)
If maxLength < 4 Then Continue Do
Dim wordlength As Integer = rn.Next(4, Math.Min(10, maxLength + 1))
Dim startIndex As Integer = rn.Next(maxLengthPoint.X, maxLengthPoint.X + maxLength - wordlength)
Dim matches() As String = words.Where(Function(w) w.Length = wordlength AndAlso _
Enumerable.Range(startIndex, wordlength).All(Function(x) _
lineCells(x).text = "" OrElse lineCells(x).text = w(x - startIndex).ToString)).ToArray
If matches.Length > 0 Then
Dim selectedWord As String = matches(rn.Next(0, matches.Length))
If wordList.Contains(selectedWord) Then Continue Do
For x As Integer = startIndex To startIndex + wordlength - 1
lineCells(x).text = selectedWord(x - startIndex).ToString
lineCells(x).available(availableIndex) = False
Next
If startIndex > 0 Then
lineCells(startIndex - 1).available(availableIndex) = False
End If
If startIndex + wordlength <= lineCells.GetUpperBound(0) Then
lineCells(startIndex + wordlength).available(availableIndex) = False
End If
wordList.Add(selectedWord)
frm.performStep()
End If
Loop While wordList.Count < 25
Dim grid(14)() As String
For r As Integer = 0 To 14
Dim columns(14) As String
grid(r) = columns
For c As Integer = 0 To 14
If cells(r * 15 + c).text <> "" Then
grid(r)(c) = cells(r * 15 + c).text
cells(r * 15 + c).text = ""
Else
grid(r)(c) = Chr(rn.Next(97, 123)).ToString
End If
cells(r * 15 + c).available = New Boolean() {True, True, True, True}
Next
Next
Return New NewGame(grid, wordList.ToArray)
End Function
End Class
The GUI
The letter grid used in this game is an extended DataGridView. This allows DoubleBuffering and restricting user selections, so mouse input can be used for highlighting words in any direction.
Clicking on a cell, or tabbing the focus to the DataGridView doesn't show any visible focus.
Holding the left mouse button down on the DataGridView and dragging to another cell causes a yellow rubber band highlighting line to be drawn. Releasing the mousebutton will change the color of the highlighting line, and make that a permanent line, if you have selected a valid line.
If the letters you have highlighted form a word that is listed in the listbox, that item will be ticked in the listbox.
''' <summary>
''' Extended DataGridView
''' DoubleBuffered. Restricts user selection of cells to facilitate seamless highlighting line drawing.
''' </summary>
''' <remarks></remarks>
Public Class exDGV
Inherits DataGridView
Public Event WM_LBUTTONDOWN_AT(ByVal p As Point)
Dim WM_LBUTTONDOWN As Integer = &H201
Dim WM_LBUTTONDBLCLK As Integer = &H203
Dim WM_KEYDOWN As Integer = &H100
Public Sub New()
Me.DoubleBuffered = True
End Sub
Protected Overrides Sub OnRowPrePaint(ByVal e As System.Windows.Forms.DataGridViewRowPrePaintEventArgs)
e.PaintParts = e.PaintParts And Not DataGridViewPaintParts.Focus
MyBase.OnRowPrePaint(e)
End Sub
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
If m.Msg = WM_LBUTTONDOWN Then
RaiseEvent WM_LBUTTONDOWN_AT(New Point(m.LParam.ToInt32))
Return
ElseIf m.Msg = WM_LBUTTONDBLCLK OrElse m.Msg = WM_KEYDOWN Then
Return
End If
MyBase.WndProc(m)
End Sub
End Class
Conclusion
Writing a game in VB.Net is similar to writing any program in VB.Net, except where you need to be a little creative with the Controls you use.
When designing your game, you need to look at which available Controls have behaviour that you could use. If those Controls have other behaviour that is not useful, or even a hindrance in your game, you need to work out ways to remove or modify that behaviour.
This was a problem when designing this game. By default, the DataGridView responds to mouse input by highlighting a cell or a range of cells. This was a hindrance in this game. As well as being unsightly, it was dramatically reducing the drawing speed of the DataGridView which was making the game unresponsive.
So the best tip for VB.Net game developers, but also applies to any VB.Net program developer, is to ask yourself: How can you use the available Controls?
Other Resources
Download project
Other puzzle games
MSDN Code Gallery version (download)
Articles related to game programming
VB.Net - Vertex
VB.Net - Perspective
VB.Net - MasterMind
VB.Net - OOP BlackJack
VB.Net - Numbers Game
VB.Net - HangMan
Console BlackJack - VB.Net | C#
TicTacToe - VB.Net | C#
OOP Conway's Game of Life - VB.Net | C#
OOP Sudoku - VB.Net | C#
OctoWords VB.Net | C#
OOP Buttons Guessing Game VB.Net | C#
OOP Tangram Shapes Game VB.Net | C#
VB.Net - Three-card Monte
VB.Net - Split Decisions
VB.Net - Pascal's Pyramid
VB.Net - Random Maze Games
(Office) Wordsearch Creator
VB.Net - Event Driven Programming - LockWords Game
C# - Crack the Lock
VB.Net - Totris