Introduction
While in the midst of making a long pursued game, it occurred to me what I could do other than games, but
similar. A couple of weeks back I had begun to learn Java and started making Java
Applets. This brought me to think about all of those word search generators online (because of Java Applet
capabilities) and I realized I wanted to explore the process of making one also.
Background
In this article I pursue the basic concepts of Custom Controls and classes, so an intermediate skill level would be recommended to understand the code, but
if you just like to browse articles
and test them, continue reading.
Using the code
To start off with a Windows Form application, I went ahead and started up a basic
Windows Form application. I had an idea of the difficulty of the actual algorithm and created pseudocode alongside
with it. (This is just the logic, we'll get to the placing and seeding for words later.)
function generate(Words)
{
gridlength := maxlength(listofwords);
while (successful)
{
notfound := false;
foreach (word in list)
{
word->Location := Calculate(wordsplaced, word);
if (Location == null)
{
notfound := true;
break;
}
if (!notfound)
{
return FormGrid(words, locations);
}
else
{
gridlength++;
}
}
}
}
Before starting the word structuring, I had to make a basic custom control. This control I decided would have access to displaying the grid, while I would assign the responsibility of generation to another class.
When I was first making custom controls I was taught to start off with just graphics, but if you want an even further double buffer, you can always use the BufferedGraphics
class file already pre-loaded. You'll see what I mean.
public partial class WordSearch : UserControl
{
public WordSearch()
{
InitializeComponent();
this.SetStyle(ControlStyles.OptimizedDoubleBuffer |
ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint, true);
}
private Graphics graphics;
protected override void OnPaint(PaintEventArgs e)
{
graphics = this.CreateGraphics();
}
}
We'll need another class file to hold place values to characters and locations. Letter
maybe?
public class Letter
{
public int X { get; set; }
public int Y { get; set; }
public char _Letter { get; set; }
public Letter(char letter, Point p )
{
this._Letter = letter;
this.X = p.X;
this.Y = p.Y;
}
}
With a start to a double buffered control you can start to turn your psuedocode into C#! Since I didn't know where to start, I decided just by making a class for it, keeps you organized, and started structuring the basic code for options
like uppercase with properties, etc. Which I already assume you know how to do, so let's continue on with our class. I started to introduce LINQ so don't get lost, there are lots of loops taking place because of the type of recursion
that presents for directions and positions in the word grid.
int length = Words.Max(t => t.Length);
while (true)
{
List<string> left = new List<string>(Words);
List<Letter[]> words = new List<Letter[]>();
bool notfound = false;
while (left.Count > 0)
{
Letter[] next = Next(words.ToArray(), left[left.Count - 1], length);
if (next == null)
{
notfound = true;
break;
}
words.Add(next);
left.RemoveAt(left.Count - 1);
}
if (!notfound)
{
char[,] without = From(words.ToArray(), length);
this.lastlength = length;
this.answers = words.ToArray();
this.lastgrid = without;
return Fill(without);
}
length++;
}
There is not much to it. Besides that all we need is some good word placing logic and organization. These last methods are the core, the seeding and placing. For these methods most of the
explanation would be better placed inside the code.
These ones took me a while. Please note, the while(list.Count > 0)
are there because we don't want to always go in a linear loop. Otherwise generation would bring the same results every time! > /p>
private Letter[] Next(Letter[][] letters, string word, int length)
{
List<Point> all = new List<Point>(AllPoints(length));
while (all.Count > 0)
{
int index = rand.Next(0, all.Count);
List<Direction> dirs = new List<Direction>(AllDirections());
while (dirs.Count > 0)
{
int index2 = rand.Next(0, dirs.Count);
Direction current = dirs[index2];
Letter[] c = Construct(all[index], current, word, length);
bool legal = true;
for (int i = 0; i < c.Length; i++)
{
if (c[i].X < 0 || c[i].X >= length || c[i].Y < 0 || c[i].Y >= length)
{
legal = false;
break;
}
else
{
for (int j = 0; j < letters.Length; j++)
{
Point[] pts = letters[j].Select<Letter, Point>(t => new Point(t.X, t.Y)).ToArray();
int _index2 = pts.ToList().FindIndex(t => t.X == c[i].X && t.Y == c[i].Y);
if (_index2 != -1)
if (c[i]._Letter != letters[j][_index2]._Letter)
legal = false;
}
}
}
if (legal)
return c;
dirs.RemoveAt(index2);
}
all.RemoveAt(index);
}
return null;
}
Things to Manipulate/Add
During the project, I realized that I could incorporate a difficulty setting. I thought about setting a difficulty based on the range of letters used to scramble.
For example the words: dog, cat with an example difficulty of two might use the letters c, d to fill with.
Points of Interest
During the project I did not learn anything except fulfilling my ideas and not give in to my weakness of deletion!
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.