12,999,157 members (43,069 online)
Add your own
alternative version

#### Stats

21.1K views
730 downloads
30 bookmarked
Posted 22 Jun 2010

# Lock Puzzle: A Perplexing Puzzle

, 22 Jun 2010
 Rate this:
Please Sign up or sign in to vote.
A brain melting puzzle in which you must turn several interconnected keys to unlock the solution

## Introduction

This project is a dangerously addictive and somewhat hypnotic puzzle game in which the player tries to situate all 16 keys to the vertical position. The rules are as follows:

• The lock has 16 keys arranged in a 4x4 array; each key oriented either horizontally or vertically.
• In order to open the lock, all the keys must be vertically oriented.
• When you turn a key to another position, all the other keys in the same row and column automatically switch their positions too.

## Background

In this article, I wish to show how to create a game derived from the mathematical problem named "The Lock Problem." Professor Paul Zeitz presented this problem in his recent lecture series titled "The Art and Craft of Mathematical Problem Solving" and after seeing this problem, I became inspired to transform it into a simple puzzle game using C#.

## Points of Interest

Well, let us organize our pieces first. The board has 16 elements in four rows and four columns. How would we arrange everything so that clicking on one key also turns the other six respective keys? There may be several methods of achieving this but this is what I decided to try. I first assign each element of the 4x4 array with a numerical ID based on its position in the array. The first number represents the row and the second number represents the column.

The element in the first row of the first column is named L11, whereas, the second element in the third row is named L32. Let us click on key L32.

Our code needs to step through a list of elements from L11-L44 and find which ones have an ID that correspond with the clicked element. In the case of L32, we want to find any ID that begins with 3 OR ends with 2.

Here is the main method for finding and turning the keys.

```private void ToggleKeyStates(object sender, EventArgs e)
{
// Get Sender cell coordinates and assign them to X and X char arrays
// This is the Key that was clicked
char[] XY = ((Key)sender).Name.ToCharArray(1, 2);
char X = XY[0];
char Y = XY[1];

// Using the clicked object as a reference,
// Cycle through controls and find the corresponding key objects
foreach (Key l in this.panel1.Controls)
{
// split the names of the key objects into char array
char[] xy = l.Name.ToCharArray(1, 2);
char x = xy[0];
char y = xy[1];

// Change state of rows and columns only
if (X == x || Y == y)
{
if(l.IsTurning == false)
l.TurnKey();
}
}
}```

## Using the Code

A few words about the `Key` control: It has two `public `Boolean properties named `isTurning` and `isLocked`. The `isTurning` property tells the calling method that the key is in the process of rotating. The `isLocked` property, if `true`, tells the calling method that the key is in the horizontal locked position.

```public bool IsLocked
{
get{return m_isLocked;}
set {m_isLocked = value;}
}
public bool IsTurning
{
get{return m_isTurning;}
set{m_isTurning = value;}
}```

### Lock and Unlock

When we click the key object, the `isTurning` and `isLocked` properties are toggled, the timer starts, and the key rotation is set into motion. The timer, when enabled, continuously calls `DefinePositions()`which sets the limits for the outermost points on the lines to situate in either the 12, 3, 6, or 9 o'clock positions. The key rotates to either a horizontal or a vertical position, the timer is then disabled and the rotation stops.

```private void DefinePositions()
{
// Rotate and stop at the 3, 6, 9, and 12 o'clock positions.
if (Degrees == 360)
{
Degrees = 0;                    // reset
this.timer1.Enabled = false;    // stop timer
m_isTurning = !m_isTurning;     // toggle turning flag
m_isLocked = false;             // toggle locked flag
}
else if (Degrees == 90)
{
this.timer1.Enabled = false;
m_isTurning = !m_isTurning;
m_isLocked = true;
Degrees++;
}
else if (Degrees == 180)
{
this.timer1.Enabled = false;
m_isTurning = !m_isTurning;
m_isLocked = false;
Degrees++;
}
else if (Degrees == 270)
{
this.timer1.Enabled = false;
m_isTurning = !m_isTurning;
m_isLocked = true;
Degrees++;
}
else  // keep moving and let us know that you are moving.
{
Degrees++;
m_isTurning = true;
}

// Update the form and controls.
this.Refresh();
}```

### Following the Path

The origin and surface of the circular path are defined in the `DefineSurfacePath` method.

```private void DefineSurfacePath(out int x_center, out int y_center,
out float x_path, out float y_path, out float x_path2, out float y_path2)
{
int Radius = 30;
x_center = this.Width / 2;
y_center = this.Height / 2;

// define outer rotation path for first line
x_path = (GetCos(360 + Degrees + x_center + degreeOffset) * Radius) + x_center;
y_path = (GetSin(360 + Degrees + y_center + degreeOffset) * Radius) + y_center;

// define outer rotation path for second line
x_path2 = (GetCos(360 + Degrees + 180 + x_center + degreeOffset) * Radius) + x_center;
y_path2 = (GetSin(360 + Degrees + 180 + y_center + degreeOffset) * Radius) + y_center;
}```

### Rendering

The `OnPaint(PaintEventArgs e)` override continuously calls `DrawKey(Graphics g)`.

```protected override void OnPaint(PaintEventArgs e)
{
// give .net the first try
base.OnPaint(e);

// then take over
Graphics g = e.Graphics;
DrawKey(g);
}```

After all the Drawing Points and Positions are defined, the `DrawKey(Graphics g)` method simply draws the given values to the screen.

```public void DrawKey(Graphics g)
{
// ake sure all graphics are smooth, not blocky.
g.SmoothingMode = SmoothingMode.AntiAlias;
g.CompositingQuality = CompositingQuality.HighQuality;
g.InterpolationMode = InterpolationMode.HighQualityBicubic;

// Declare Brush and pen objects
Brush brshREC = new SolidBrush(Color.White);
Pen p = new Pen(brshREC, 10);

// Declare radius and origin of circle
int x_center;
int y_center;
float x_path;
float y_path;
float x_path2;
float y_path2;
DefineSurfacePath(out x_center, out y_center, out x_path, out y_path,
out x_path2, out y_path2);

// draw previously defined points
g.DrawLine(p, x_center, y_center, x_path, y_path);
g.DrawLine(p, x_center, y_center, x_path2, y_path2);
}```

## History

• 06-22-2010 - First version

## License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

## About the Author

 Software Developer (Senior) United States
Technical professional with 11 years of experience in designing, developing and implementing sophisticated software solutions.

## Comments and Discussions

 First Prev Next
 My vote of 5 fredatcodeproject24-Oct-12 11:15 fredatcodeproject 24-Oct-12 11:15
 My vote of 5 Dr. Jones DK29-Jun-10 9:43 Dr. Jones DK 29-Jun-10 9:43
 Simple yet addictive game. Great article.
 My vote of 5 alain_dionne29-Jun-10 2:18 alain_dionne 29-Jun-10 2:18
 Easy solution supercat923-Jun-10 13:46 supercat9 23-Jun-10 13:46
 Noooooo!!! Now I'm addicted. mrsnipey22-Jun-10 20:03 mrsnipey 22-Jun-10 20:03
 Last Visit: 31-Dec-99 18:00     Last Update: 24-Jun-17 1:16 Refresh 1

General    News    Suggestion    Question    Bug    Answer    Joke    Praise    Rant    Admin

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.170622.1 | Last Updated 22 Jun 2010
Article Copyright 2010 by Christopher Denmark
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid