【Silverlight】汉诺塔游戏,带AI

先看效果

 

 

完整代码在此下载/Aimeast/SLAnyHanoi.zip

 

简单的把设计说明一下

ViewModel 和 Model 的设计如下:

 

用到了其中的动画效果用的是自己实现的行为(Behavior)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
using System;
using System.Windows;
using System.Windows.Interactivity;
using System.Windows.Media.Animation;
 
namespace AnyHanoi
{
    public class DiscFluidMoveBehavior : Behavior<FrameworkElement>
    {
        public Point Translate
        {
            get { return (Point)GetValue(TranslateProperty); }
            set { SetValue(TranslateProperty, value); }
        }
 
        // Using a DependencyProperty as the backing store for Translate.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty TranslateProperty =
            DependencyProperty.Register("Translate", typeof(Point), typeof(DiscFluidMoveBehavior), new PropertyMetadata(Translate_PropertyChangedCallback));
 
 
        private static void Translate_PropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            Point p = (Point)e.NewValue;
            DiscFluidMoveBehavior b = (DiscFluidMoveBehavior)d;
            try
            {
                b.Update(p);
            }
            catch { }
        }
 
        private void Update(Point p)
        {
            Storyboard storyboard = new Storyboard();
            DoubleAnimation x = new DoubleAnimation();
            DoubleAnimation y = new DoubleAnimation();
            x.SetValue(Storyboard.TargetPropertyProperty, new PropertyPath("(UIElement.RenderTransform).(CompositeTransform.TranslateX)"));
            y.SetValue(Storyboard.TargetPropertyProperty, new PropertyPath("(UIElement.RenderTransform).(CompositeTransform.TranslateY)"));
            x.Duration = this.Duration;
            y.Duration = this.Duration;
            x.To = p.X;
            y.To = p.Y;
            Storyboard.SetTarget(x, base.AssociatedObject);
            Storyboard.SetTarget(y, base.AssociatedObject);
            storyboard.Children.Add(x);
            storyboard.Children.Add(y);
            storyboard.Begin();
        }
 
        public Duration Duration
        {
            get { return (Duration)GetValue(DurationProperty); }
            set { SetValue(DurationProperty, value); }
        }
 
        // Using a DependencyProperty as the backing store for Duration.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty DurationProperty =
            DependencyProperty.Register("Duration", typeof(Duration), typeof(DiscFluidMoveBehavior), new PropertyMetadata(new Duration(TimeSpan.FromSeconds(0.1))));
    }
}

需要事先引用 Expression 的 System.Windows.Interactivity 程序集。

把这个行为应用到每个Items。

 

AI的设计如下:

 

方法是递归求解。

基本思想和标准状态的思想是一样的。把最大的盘子移动到某个目标柱子,需要找到一个暂存柱子。然后按照这一思想进行递归求解。直到剩下最后一个盘子,就可以直接移动。

 

递归求解的核心代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
private void RecSolve(Puzzle puzzle)
{
    int max = 0;
    Peg maxPeg = null;
 
    //找出当前状态下最大盘子所在的柱子
    foreach (Peg peg in puzzle.PegCollection)
    {
        if (peg.Count > 0 && max < peg.Buttom)
        {
            max = peg.Buttom;
            maxPeg = peg;
        }
    }
 
    //当前状态只有一个盘子,直接移动
    if (puzzle.PegA.Count + puzzle.PegB.Count + puzzle.PegC.Count == 1)
    {
        if (maxPeg.PegID != puzzle.DestPeg)
            Move(maxPeg, puzzle.GetPeg(puzzle.DestPeg));
        return;
    }
 
    //当前状态有多个盘子
    if (maxPeg.PegID == puzzle.DestPeg) //最大的盘子就在目标柱子上,不需要移动
    {
        RecSolve(puzzle.NewLevelPuzzle(maxPeg.PegID, puzzle.DestPeg));
    }
    else //最大的盘子不在目标柱子上,需要移动
    {
        //找出临时柱子,即 不是 目标柱子 也不是 最大盘子所在的柱子
        Pegs tempPagID = Pegs.A;
        if (tempPagID == maxPeg.PegID || tempPagID == puzzle.DestPeg) tempPagID = Pegs.B;
        if (tempPagID == maxPeg.PegID || tempPagID == puzzle.DestPeg) tempPagID = Pegs.C;
 
        //把当前状态 去掉最大盘子以后的新状态 继续递归处理
        //这一步把所有盘子都移动到临时柱子上
        RecSolve(puzzle.NewLevelPuzzle(maxPeg.PegID, tempPagID));
 
        //把当前最大盘子移动到目标柱子上
        Move(maxPeg, puzzle.GetPeg(puzzle.DestPeg));
 
        //把上一步处理好的状态 去掉最大的盘子以后的状态
        //即 所有盘子都在临时柱子 的 状态移动到目标状态
        RecSolve(puzzle.NewLevelPuzzle(puzzle.DestPeg, puzzle.DestPeg));
    }
}

 

欢迎大家的评论!

posted @   Aimeast  阅读(2451)  评论(13编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
点击右上角即可分享
微信分享提示