12,689,801 members (25,869 online)
alternative version

19.8K views
30 bookmarked
Posted

# Lock Puzzle: A Perplexing Puzzle

, 22 Jun 2010 CPOL
 Rate this:
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)
{
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

## Share

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

## You may also be interested in...

 Pro

 First Prev Next
 My vote of 5 fredatcodeproject24-Oct-12 12:15 fredatcodeproject 24-Oct-12 12:15
 My vote of 5 Dr. Jones DK29-Jun-10 10:43 Dr. Jones DK 29-Jun-10 10:43
 My vote of 5 alain_dionne29-Jun-10 3:18 alain_dionne 29-Jun-10 3:18
 Easy solution supercat923-Jun-10 14:46 supercat9 23-Jun-10 14:46
 If I'm understanding the problem correctly, it may be readily solved by simply clicking any square where the total number of "locked" squares in the same row or column is an odd number. As a simple example, if the puzzle is one click away from solution, the square that needs to be clicked will have 7 locked squares in the same row or column (an odd number). Squares in the same row or column will have four locked squares (an even number). Other squares will have two (an even number--one in the same row, and one in the same column).To understand why this works, figure that clicking a key will flip seven squares that are "visible" to it, four squares that are visible to other squares on the same row or column, and two squares that are visible to any other square. Thus, the only key whose parity is affected will be the one that was clicked. When the puzzle is in a solved state, the parity of every square is even. Clicking every square that has odd parity will result in every square having even parity.It might be possible to make the puzzle more interesting by requiring that only keys that are in the "locked" state may be clicked on (keys which are in either state may be affected by other keys in the same row or column). This will make it necessary to click some keys twice (e.g. if the puzzle is in the state:```UULU ULUL UULU UUUU```the even/odd parity map will be:```OEEE EEEE OEEE EOEO```but it will not be possible to click any of the squares with odd parity. One would have to start by clicking one of the locked squares. This would allow some other squares in that row or column to be clicked. I'm not sure if the puzzle would in fact become interesting, but it might be worth trying.
 Noooooo!!! Now I'm addicted. mrsnipey22-Jun-10 21:03 mrsnipey 22-Jun-10 21:03
 Last Visit: 31-Dec-99 19:00     Last Update: 16-Jan-17 8:22 Refresh 1