C# - OctoWords
Overview
octowards is an original word puzzle game. To play, drag letters to form words. You can drag a letter in any straight line (horizontally, vertically, or diagonally), as long as the straight-line path is clear. The game won't allow you to make an invalid move, but it can't force you to play a valid move, so the onus is on you.
As each newly formed word is recognized it disappears from the grid, and your score is incremented. each word you form is worth the length of the word multiplied by the length of the word minus one. Words entirely contained in the green octagon are worth 3x as much as words partially or completely outside of the octagon.
Most words recognized by the game are two, three, or four-character words, but more skillful players can create longer words, and also creating situations where two or more words are formed by placing one character will earn you extra points.
Theoretically, words that will be recognized range from two to twenty characters, but as you'll see it's difficult to build a long word without the smaller words contained within your longer word being recognized first, so there is a large amount of skill necessary to become a proficient player.
The Game Class code
The Game class is used for creating each new game, and also for checking if there are any possible words left to find in the grid after each move at every stage of the game.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace OctoWords_cs.core
{
public class Game
{
private static string[] vowels1 = { "A", "E", "I", "O" };
private static string[] vowels2 = { "A", "E", "I", "O", "U" };
private static string[] consonants1 = { "B", "C", "D", "F", "G", "L", "M", "N", "P", "R", "S", "T" };
private static string[] consonants2 = Enumerable.Range((int)'A', 26).Select((x) => ((char)x).ToString()).Except(vowels2).ToArray();
private static string[] chars = new string[232];
public static void getLetters(Random r)
{
//90 vowels
//142 consonants
for (int x = 0; x <= 89; x++)
{
int choice = r.Next(1, 4);
if (choice < 3)
{
chars[x] = vowels1[r.Next(0, 4)];
}
else
{
chars[x] = vowels2[r.Next(0, 5)];
}
}
for (int x = 90; x <= 231; x++)
{
int choice = r.Next(1, 6);
if (choice < 5)
{
chars[x] = consonants1[r.Next(0, 12)];
}
else
{
chars[x] = consonants2[r.Next(0, 21)];
}
}
}
private static int[] getCells(Random r)
{
int [] indices = new int[232];
List< int > allIndices = Enumerable.Range(0, 484).ToList();
for (int x = 0; x <= 231; x++)
{
int i = allIndices[r.Next(0, allIndices.Count)];
allIndices.Remove(i);
indices[x] = i;
}
return indices;
}
public static string[][] newGame(Random r, string[][] grid)
{
chars = chars.OrderBy((x) => r.NextDouble()).ToArray();
int [] indices = getCells(r);
int counter = 0;
for (int y = 0; y <= 21; y++)
{
for (int x = 0; x <= 21; x++)
{
if (indices.Contains(y * 22 + x))
{
grid[y][x] = chars[counter];
counter += 1;
}
else
{
grid[y][x] = "" ;
}
}
}
return grid;
}
public static PlayState GameOver(string[][] grid)
{
List< string > letters = new List<string>();
for (int y = 0; y <= 21; y++)
{
letters.AddRange(grid[y].Where((c) => c != "" ).ToArray());
}
if (letters.Any((s) => vowels2.Contains(s)))
{
for (int x = 2; x <= 20; x++)
{
foreach (string word in Words.getWords(x))
{
List< string > newLetters = new List<string>(letters);
for (int c = 0; c < word.Length; c++)
{
if (newLetters.Contains(word.Substring(c, 1)))
{
newLetters.Remove(word.Substring(c, 1));
if (c == word.Length - 1)
{
return new PlayState(false, 0);
}
}
else
{
break ;
}
}
}
}
}
return new PlayState(true, 484 - letters.Count);
}
}
}
frmHighScores code
frmHighScores displays the five highest scores, which are saved in a Settings Specialized.StringCollection. The Form shows at the end of each game, and also has a TreeView (in a slideOutPanel) that shows how many words of which length were found in that game.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Collections;
using OctoWords_cs.core;
namespace OctoWords_cs
{
public partial class frmHighScores : Form
{
private int newScore;
private DataTable dt = new DataTable();
private int dtIndex;
public frmHighScores(int score, Dictionary<int, List<string>> wordsFound)
{
InitializeComponent();
this.FormClosing += frmHighScores_FormClosing;
this.DataGridView1.CellValueChanged += DataGridView1_CellValueChanged;
this.DataGridView1.SelectionChanged += DataGridView1_SelectionChanged;
this.DataGridView1.CellFormatting += DataGridView1_CellFormatting;
this.Shown += frmHighScores_Shown;
this.Button3.Click += Button3_Click;
this.slideOutPanel1.Resize += rePaintAll;
this.newScore = score;
if (Properties.Settings.Default.highScores == null)
{
Properties.Settings.Default.highScores = new System.Collections.Specialized.StringCollection();
for (int x = 1; x <= 5; x++)
{
Properties.Settings.Default.highScores.Add("Player" + x.ToString() + "|" + "0");
}
}
dt.Columns.Add("name");
dt.Columns.Add("score", typeof(int));
for (int x = 1; x <= 5; x++)
{
dt.Rows.Add(Properties.Settings.Default.highScores[x - 1].Split('|'));
}
DataGridView1.AutoGenerateColumns = false;
DataGridView1.Columns[0].DataPropertyName = "name";
DataGridView1.Columns[1].DataPropertyName = "score";
DataGridView1.DataSource = dt;
int[] keys = wordsFound.Keys.ToArray();
Array.Sort(keys);
TreeNode node = TreeView1.Nodes.Add("Words (" + keys.Sum((x) => wordsFound[x].Count) + ")");
foreach (int x in keys)
{
string[] allWords = Words.getWords(x);
TreeNode secondNode = node.Nodes.Add(x.ToString() + " letter words (" + wordsFound[x].Count + ")");
foreach (string s in wordsFound[x])
{
if (allWords.Contains(StringHelper.StrReverse(s)) && !allWords.Contains(s))
{
secondNode.Nodes.Add(StringHelper.StrReverse(s));
}
else
{
secondNode.Nodes.Add(s);
}
}
}
Label5.Text = "Longest word found was " + keys.Last().ToString() + " letters";
node.Expand();
}
private void frmHighScores_FormClosing(object sender, System.Windows.Forms.FormClosingEventArgs e)
{
Properties.Settings.Default.highScores.Clear();
for (int x = 0; x < dt.Rows.Count; x++)
{
Properties.Settings.Default.highScores.Add(string.Join("|", Array.ConvertAll(dt.Rows[x].ItemArray, (o) => o.ToString())));
}
Properties.Settings.Default.Save();
}
private void frmHighScores_Load(System.Object sender, System.EventArgs e)
{
DataGridView1.isEnabled = false;
this.BackColor = Color.FromArgb(255, 255, 128);
Button1.BackColor = Color.FromArgb(255, 255, 128);
Button2.BackColor = Color.FromArgb(255, 255, 128);
Button3.BackColor = Color.FromArgb(255, 255, 128);
GroupBox1.BackColor = Color.FromArgb(255, 255, 128);
slideOutPanel1.BackColor = Color.FromArgb(255, 255, 128);
TreeView1.BackColor = Color.FromArgb(255, 255, 128);
TreeView1.ForeColor = Color.DarkGray;
}
private void DataGridView1_CellValueChanged(object sender, System.Windows.Forms.DataGridViewCellEventArgs e)
{
if (e.RowIndex > -1)
{
DataGridView1.Rows[e.RowIndex].Height = 50;
DataGridView1.Rows[e.RowIndex].Cells[0].Style.BackColor = Color.FromArgb(164, 255, 164);
DataGridView1.Rows[e.RowIndex].Cells[1].Style.BackColor = Color.FromArgb(164, 255, 164);
}
}
/// <summary>
/// Necessary to handle user selections in the dgv.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
/// <remarks></remarks>
private void DataGridView1_SelectionChanged(object sender, System.EventArgs e)
{
DataGridView1.ClearSelection();
}
private void DataGridView1_CellFormatting(object sender, System.Windows.Forms.DataGridViewCellFormattingEventArgs e)
{
if (e.ColumnIndex == 1)
{
DataGridView1[e.ColumnIndex, e.RowIndex].Value = Convert.ToInt32(DataGridView1[e.ColumnIndex, e.RowIndex].Value).ToString("0000000");
}
}
private void frmHighScores_Shown(object sender, System.EventArgs e)
{
rePaintAll(this, new System.EventArgs());
int tempVar = dt.Rows.Count;
for (int x = 0; x < tempVar; x++)
{
if (newScore > dt.Rows[x].Field<int>(1))
{
DataRow row = dt.NewRow();
row.ItemArray = new object[] {"", newScore};
dt.Rows.InsertAt(row, x);
dt.Rows.RemoveAt(5);
dtIndex = x;
GroupBox1.Enabled = true;
TextBox1.Focus();
break;
}
}
}
private void Button3_Click(System.Object sender, System.EventArgs e)
{
dt.Rows[dtIndex][0] = TextBox1.Text;
}
private void rePaintAll(System.Object sender, System.EventArgs e)
{
foreach(Control c in this.Controls) {
c.Refresh();
}
}
}
}
Conclusion
Nowadays more than ever, the average person can invent, create, and market their ideas through the use of the desktop computer.
This game is a simple idea, and the code involved is easily written with a little experience. All of the controls used are standard VB controls, manipulated slightly to give the necessary functionality and overall appearance.
If you can dream it, you can code it...
Other Resources
Download here... (VB.Net | C#)
See Also
Articles related to game programming
VB.Net - Perspective
VB.Net - Vertex
VB.Net - WordSearch
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#
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