先看效果
完整代码在此下载
简单的把设计说明一下
ViewModel 和 Model 的设计如下:
用到了其中的动画效果用的是自己实现的行为(Behavior)。
using System;using System.Windows;using System.Windows.Interactivity;using System.Windows.Media.Animation;namespace AnyHanoi{ public class DiscFluidMoveBehavior : Behavior{ 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的设计如下:
方法是递归求解。
基本思想和标准状态的思想是一样的。把最大的盘子移动到某个目标柱子,需要找到一个暂存柱子。然后按照这一思想进行递归求解。直到剩下最后一个盘子,就可以直接移动。
递归求解的核心代码如下
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)); } }
欢迎大家的评论!