65.9K
CodeProject 正在变化。 阅读更多。
Home

锁谜题:一个令人费解的谜题

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.68/5 (24投票s)

2010年6月22日

CPOL

3分钟阅读

viewsIcon

37484

downloadIcon

871

一个烧脑的谜题,您必须转动几个相互连接的钥匙才能找到解决方案。

fig1.jpgfig2.jpg

目录

引言

这个项目是一个非常容易上瘾,并且有些催眠的谜题游戏,玩家需要尝试将所有16个钥匙都调整到垂直位置。 规则如下:

  • 锁有16个钥匙,排列成4x4的阵列;每个钥匙要么水平放置,要么垂直放置。
  • 为了打开锁,所有的钥匙都必须垂直放置。
  • 当你将一个钥匙转到另一个位置时,同一行和同一列的所有其他钥匙也会自动切换它们的位置。

背景

在这篇文章中,我想展示如何创建一个源自名为“锁难题”的数学问题的游戏。 Paul Zeitz 教授在他最近的题为“数学问题解决的艺术和技巧”的系列讲座中提出了这个问题,在看到这个问题后,我受到了启发,并使用 C# 将其转换为一个简单的谜题游戏。

关注点

好吧,让我们先组织我们的零件。棋盘有 16 个元素,排列在四行四列中。我们该如何安排,才能使点击一个钥匙也能旋转其他六个相应的钥匙? 可能有几种方法可以实现这一点,但这就是我决定尝试的方法。 我首先根据 4x4 阵列中每个元素的位置为其分配一个数字 ID。 第一个数字表示行,第二个数字表示列。

fig3.jpg

第一行第一列的元素被命名为 L11,而第三行第二个元素被命名为 L32。 让我们点击钥匙 L32。

fig4.jpg

我们的代码需要遍历 L11-L44 的元素列表,并找到哪些元素的 ID 与点击的元素相对应。 在 L32 的情况下,我们希望找到任何以 3 开头或以 2 结尾的 ID。

fig5.jpg

这是用于查找和旋转钥匙的主要方法。

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

关于Key控件的几句话:它有两个public布尔属性,名为isTurningisLockedisTurning属性告诉调用方法该钥匙正在旋转过程中。 如果isLocked属性为true,则告诉调用方法该钥匙处于水平锁定位置。

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

锁和解锁

当我们点击钥匙对象时,isTurningisLocked属性将被切换,计时器启动,并且钥匙旋转开始。 启用计时器后,会持续调用DefinePositions(),该方法设置线上最外层点的限制,使其位于 12 点、3 点、6 点或 9 点钟位置。 钥匙旋转到水平或垂直位置,然后禁用计时器并停止旋转。

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();
}

跟随路径

圆形路径的原点和表面在DefineSurfacePath方法中定义。

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;
}

渲染

OnPaint(PaintEventArgs e)重写持续调用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);
}

在定义所有绘图点和位置后,DrawKey(Graphics g)方法只是将给定的值绘制到屏幕上。

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);
}

历史

  • 2010-06-22 - 第一个版本
© . All rights reserved.