介绍

使用 Silverlight 2.0(c#) 开发一个连连看游戏

玩法

用鼠标左键选中卡片,如果选中的两卡片间的连线不多于 3 根直线,则选中的两卡片可消除

在线DEMO



思路

1、卡片初始排列算法:已知容器容量为 x, 不重复的卡片数量为 y, x >= y && x % 2 == 0, 首先在容器内随机排列卡片,然后取出容器内相同的卡片个数为奇数的集合(集合内成员数量必为偶数个),最后将该集合一刀切,将集合右半部分的卡片的依次复制到集合左半部分。以上算法保证了在一定随机率的基础上,不会出现相同的卡片个数为奇数的情况。(文/webabcd

2、无解算法和重排算法:在容器内存在的卡片中,两两计算是否存在可消路径,如果没有就是无解,需要重排。重排时,需要得到现存的卡片集合和卡片位置集合,在卡片集合中随机取卡片(取出一个,原集合就要移除这一个),然后依次放到卡片位置集合内,从而达到将现存卡片重新排列的目的。

3、两点消去路径的算法以及取最优消去路径的算法:取玩家选的第一点的 x 轴方向和 y 轴方向上的所有无占位符的坐标集合(包括自己),名称分别为 x1s, y1s;取玩家选的第二点的 x 轴方向和 y 轴方向上的所有无占位符的坐标集合(包括自己),名称分别为 x2s, y2s。先在 x1s 和 x2s 中找 x 坐标相等的两点,然后找出该两点与玩家选的两点可组成一条连续的直线的集合,该集合就是可消路径的集合,之后同理再在 y1s 和 y2s 中找到可消路径的集合。两集合合并就是玩家选中的两点间的所有可消路径的集合,该集合为空则两点不可消,该集合内的最短路径则为最优消去路径,集合内的 4 点连接线则为消去路径的连接线。

4、游戏使用MVVM(Model - View - ViewModel)模式开发。

关键代码
Core.cs
  1. using System;
  2. using System.Net;
  3. using System.Windows;
  4. using System.Windows.Controls;
  5. using System.Windows.Documents;
  6. using System.Windows.Ink;
  7. using System.Windows.Input;
  8. using System.Windows.Media;
  9. using System.Windows.Media.Animation;
  10. using System.Windows.Shapes;

  11. using YYMatch.Models;
  12. using System.Collections.ObjectModel;
  13. using System.Linq;
  14. using System.Collections.Generic;
  15. using System.ComponentModel;
  16. using System.Threading;

  17. namespace YYMatch.ViewModels
  18. {
  19.     /**//// <summary>
  20.     /// 连连看核心模块
  21.     /// </summary>
  22.     public class Core : INotifyPropertyChanged
  23.     {
  24.         ObservableCollection<CardModel> _cards = null;
  25.         int _rows = 0;
  26.         int _columns = 0;
  27.         SynchronizationContext _syncContext = null;

  28.         public Core()
  29.         {
  30.             // 在容器上布满空的卡片
  31.             _cards = new ObservableCollection<CardModel>();
  32.             for (int i = 0; i < Global.ContainerColumns * Global.ContainerRows; i++)
  33.             {
  34.                 _cards.Add(new CardModel("00", i));
  35.             }

  36.             _syncContext = SynchronizationContext.Current;
  37.         }

  38.         public void Start(int rows, int columns)
  39.         {
  40.             _rows = rows;
  41.             _columns = columns;

  42.             InitCard();
  43.         }

  44.         private ObservableCollection<CardModel> InitCard()
  45.         {
  46.             Random r = new Random();
  47.             // 卡片集合在容器内的范围
  48.             int minX = (Global.ContainerColumns - _columns) / 2;
  49.             int maxX = minX + _columns - 1;
  50.             int minY = (Global.ContainerRows - _rows) / 2;
  51.             int maxY = minY + _rows - 1;

  52.             for (int x = 0; x < Global.ContainerColumns; x++)
  53.             {
  54.                 for (int y = 0; y < Global.ContainerRows; y++)
  55.                 {
  56.                     // 18 张图随机排列
  57.                     string imageName = r.Next(1, Global.ImageCount + 1).ToString().PadLeft(2, '0');
  58.                     var cardPoint = new CardPoint(x, y);

  59.                     if (x >= minX && x <= maxX && y >= minY && y <= maxY)
  60.                     {
  61.                         _cards[cardPoint.Position] = new CardModel(imageName, cardPoint.Position);
  62.                     }
  63.                 }
  64.             }

  65.             // 相同的卡片个数为奇数的集合
  66.             var oddImages = _cards.Where(p => p.ImageName != Global.EmptyImageName)
  67.                 .GroupBy(p => p.ImageName)
  68.                 .Select(p => new { ImageName = p.Key, Count = p.Count() })
  69.                 .Where(p => p.Count % 2 > 0)
  70.                 .ToList();

  71.             // 如果 oddImages 集合的成员为奇数个(保证容器容量为偶数个则不可能出现这种情况)
  72.             if (oddImages.Count() % 2 > 0)
  73.             {
  74.                 throw new Exception("无法初始化程序");
  75.             }
  76.             else
  77.             {
  78.                 // 在集合中将所有的个数为奇数的卡片各自取出一个放到 temp 中
  79.                 // 将 temp 一刀切,使其右半部分的卡片的 ImageName 依次赋值为左半部分的卡片的 ImageName
  80.                 // 由此保证相同的卡片均为偶数个
  81.                 List<CardModel> tempCards = new List<CardModel>();
  82.                 for (int i = 0; i < oddImages.Count(); i++)
  83.                 {
  84.                     if (i < oddImages.Count() / 2)
  85.                     {
  86.                         var tempCard = _cards.Last(p => p.ImageName == oddImages.ElementAt(i).ImageName);
  87.                         tempCards.Add(tempCard);
  88.                     }
  89.                     else
  90.                     {
  91.                         var tempCard = _cards.Last(p => p.ImageName == oddImages.ElementAt(i).ImageName);
  92.                         _cards[tempCard.Position].ImageName = tempCards[i - oddImages.Count() / 2].ImageName;
  93.                     }
  94.                 }
  95.             }

  96.             if (!IsActive())
  97.                 Replace();

  98.             return _cards;
  99.         }

  100.         /**//// <summary>
  101.         /// 判断两卡片是否可消
  102.         /// </summary>
  103.         public bool Match(CardModel c1, CardModel c2, bool removeCard)
  104.         {
  105.             bool result = false;

  106.             if (c1.ImageName != c2.ImageName
  107.                 || c1.ImageName == Global.EmptyImageName
  108.                 || c2.ImageName == Global.EmptyImageName
  109.                 || c1.Position == c2.Position)
  110.                 return false;

  111.             // 如果可消的话,则 point1, point2, point3, point4 会组成消去两卡片的路径(共4个点)
  112.             CardPoint point1 = new CardPoint(0);
  113.             CardPoint point2 = new CardPoint(0);
  114.             CardPoint point3 = new CardPoint(0);
  115.             CardPoint point4 = new CardPoint(0);
  116.             // 最小路径长度
  117.             int minLength = int.MaxValue;

  118.             CardPoint p1 = new CardPoint(c1.Position);
  119.             CardPoint p2 = new CardPoint(c2.Position);

  120.             var p1xs = GetXPositions(p1);
  121.             var p1ys = GetYPositions(p1);
  122.             var p2xs = GetXPositions(p2);
  123.             var p2ys = GetYPositions(p2);

  124.             // 在两点各自的 X 轴方向上找可消点(两个可消点的 X 坐标相等)
  125.             var pxs = from p1x in p1xs
  126.                       join p2x in p2xs
  127.                       on p1x.X equals p2x.X
  128.                       select new { p1x, p2x };
  129.             foreach (var px in pxs)
  130.             {
  131.                 if (MatchLine(p1, px.p1x) && MatchLine(px.p1x, px.p2x) && MatchLine(px.p2x, p2))
  132.                 {
  133.                     int length = Math.Abs(p1.X - px.p1x.X) + Math.Abs(px.p1x.Y - px.p2x.Y) + Math.Abs(px.p2x.X - p2.X);

  134.                     // 查找最短连接路径
  135.                     if (length < minLength)
  136.                     {
  137.                         minLength = length;
  138.                         point1 = p1;
  139.                         point2 = px.p1x;
  140.                         point3 = px.p2x;
  141.                         point4 = p2;
  142.                     }
  143.                     result = true;
  144.                 }
  145.             }

  146.             // 在两点各自的 Y 轴方向上找可消点(两个可消点的 Y 坐标相等)
  147.             var pys = from p1y in p1ys
  148.                       join p2y in p2ys
  149.                       on p1y.Y equals p2y.Y
  150.                       select new { p1y, p2y };
  151.             foreach (var py in pys)
  152.             {
  153.                 if (MatchLine(p1, py.p1y) && MatchLine(py.p1y, py.p2y) && MatchLine(py.p2y, p2))
  154.                 {
  155.                     int length = Math.Abs(p1.Y - py.p1y.Y) + Math.Abs(py.p1y.X - py.p2y.X) + Math.Abs(py.p2y.Y - p2.Y);

  156.                     // 查找最短连接路径
  157.                     if (length < minLength)
  158.                     {
  159.                         minLength = length;
  160.                         point1 = p1;
  161.                         point2 = py.p1y;
  162.                         point3 = py.p2y;
  163.                         point4 = p2;
  164.                     }
  165.                     result = true;
  166.                 }
  167.             }

  168.             if (removeCard && result)
  169.             {
  170.                 RemoveCard(c1, c2, point1, point2, point3, point4);
  171.             }

  172.             return result;
  173.         }

  174.         /**//// <summary>
  175.         /// 直线上的两个 CardPoint 是否可以消去
  176.         /// </summary>
  177.         private bool MatchLine(CardPoint p1, CardPoint p2)
  178.         {
  179.             if (p1.X != p2.X && p2.Y != p2.Y)
  180.                 return false;

  181.             var range = _cards.Where(p =>
  182.                 p.Position > Math.Min(p1.Position, p2.Position)
  183.                 && p.Position < Math.Max(p1.Position, p2.Position));

  184.             if (p1.X == p2.X)
  185.             {
  186.                 range = range.Where(p => (p.Position - p1.Position) % Global.ContainerColumns == 0);
  187.             };

  188.             if (range.Count() == 0 || range.All(p => p.ImageName == Global.EmptyImageName))
  189.                 return true;

  190.             return false;
  191.         }

  192.         /**//// <summary>
  193.         /// 获取指定的 CardPoint 的 X 轴方向上的所有 ImageName 为 Global.EmptyImageName 的 CardPoint 集合
  194.         /// </summary>
  195.         private List<CardPoint> GetXPositions(CardPoint p)
  196.         {
  197.             var result = new List<CardPoint>() { p };
  198.             for (int i = 0; i < Global.ContainerColumns; i++)
  199.             {
  200.                 var point = new CardPoint(p.Y * Global.ContainerColumns + i);
  201.                 if (_cards[point.Position].ImageName == Global.EmptyImageName)
  202.                     result.Add(point);
  203.             }

  204.             return result;
  205.         }

  206.         /**//// <summary>
  207.         /// 获取指定的 CardPoint 的 Y 轴方向上的所有 ImageName 为 Global.EmptyImageName 的 CardPoint 集合
  208.         /// </summary>
  209.         private List<CardPoint> GetYPositions(CardPoint p)
  210.         {
  211.             var result = new List<CardPoint>() { p };
  212.             for (int i = 0; i < Global.ContainerRows; i++)
  213.             {
  214.                 var point = new CardPoint(i * Global.ContainerColumns + p.X);
  215.                 if (_cards[point.Position].ImageName == Global.EmptyImageName)
  216.                     result.Add(point);
  217.             }

  218.             return result;
  219.         }

  220.         /**//// <summary>
  221.         /// 执行消去两个卡片的任务
  222.         /// 参数为:两个卡片对象和消去路径的四个点(此四个点需按路径方向依次传入)
  223.         /// </summary>
  224.         private void RemoveCard(CardModel c1, CardModel c2, CardPoint p1, CardPoint p2, CardPoint p3, CardPoint p4)
  225.         {
  226.             _cards[c1.Position] = new CardModel(Global.EmptyImageName, c1.Position);
  227.             _cards[c2.Position] = new CardModel(Global.EmptyImageName, c2.Position);

  228.             Points.Clear();
  229.             Points.Add(CardPoint2Point(p1));
  230.             Points.Add(CardPoint2Point(p2));
  231.             Points.Add(CardPoint2Point(p3));
  232.             Points.Add(CardPoint2Point(p4));

  233.             Thread thread = new Thread
  234.             (
  235.                 x =>
  236.                 {
  237.                     Thread.Sleep(100);

  238.                     _syncContext.Post
  239.                     (
  240.                         y =>
  241.                         {
  242.                             Points.Clear();
  243.                         },
  244.                         null
  245.                     );
  246.                 }
  247.             );
  248.             thread.Start();
  249.         }

  250.         /**//// <summary>
  251.         /// CardPoint 转换成坐标位置 Point
  252.         /// </summary>
  253.         private Point CardPoint2Point(CardPoint cardPoint)
  254.         {
  255.             // 38 - 每个正方形卡片的边长
  256.             // 19 - 边长 / 2
  257.             // cardPoint.X * 2 - 卡片的 Padding 为 1 ,所以卡片间的间距为 2
  258.             var x = cardPoint.X * 38 + 19 + cardPoint.X * 2;
  259.             var y = cardPoint.Y * 38 + 19 + cardPoint.Y * 2;

  260.             return new Point(x, y);
  261.         }

  262.         /**//// <summary>
  263.         /// 检查当前是否仍然有可消除的卡片
  264.         /// </summary>
  265.         public bool IsActive()
  266.         {
  267.             var currentCards = _cards.Where(p => p.ImageName != Global.EmptyImageName).ToList();

  268.             for (int i = 0; i < currentCards.Count() - 1; i++)
  269.             {
  270.                 for (int j = i + 1; j < currentCards.Count(); j++)
  271.                 {
  272.                     if (Match(currentCards, currentCards[j], false))
  273.                         return true;
  274.                 }
  275.             }

  276.             return false;
  277.         }

  278.         /**//// <summary>
  279.         /// 重新排列当前卡片集合
  280.         /// 并保证有可消卡片
  281.         /// </summary>
  282.         public void Replace()
  283.         {
  284.             Random r = new Random();
  285.             var currentCards = _cards.Where(p => p.ImageName != Global.EmptyImageName).ToList();
  286.             var count = currentCards.Count;
  287.             if (count == 0)
  288.                 return;

  289.             var targetImageNames = currentCards.Select(p => p.ImageName).ToList();
  290.             var targetPositions = currentCards.Select(p => p.Position).ToList();

  291.             for (int i = 0; i < count; i++)
  292.             {
  293.                 var index = r.Next(0, count - i);

  294.                 var targetImageName = targetImageNames.Skip(index).First();
  295.                 var targetPosition = targetPositions.Skip(i).First();

  296.                 _cards[targetPosition] = new CardModel(targetImageName, targetPosition);
  297.                 targetImageNames.RemoveAt(index);
  298.             }

  299.             while (!IsActive())
  300.             {
  301.                 Replace();
  302.             }
  303.         }

  304.         /**//// <summary>
  305.         /// 清除卡片集合
  306.         /// </summary>
  307.         public void Clear()
  308.         {
  309.             _cards.Clear();
  310.             Points.Clear();
  311.         }

  312.         /**//// <summary>
  313.         /// 当前卡片数量
  314.         /// </summary>
  315.         public int CardCount
  316.         {
  317.             get { return _cards.Where(p => p.ImageName != Global.EmptyImageName).Count(); }
  318.         }

  319.         /**//// <summary>
  320.         /// 连连看的卡片集合
  321.         /// </summary>
  322.         public ObservableCollection<CardModel> Cards
  323.         {
  324.             get { return _cards; }
  325.         }

  326.         private PointCollection _points;
  327.         /**//// <summary>
  328.         /// 可消两卡片的连接路径上的 4 个点的集合
  329.         /// </summary>
  330.         public PointCollection Points
  331.         {
  332.             get
  333.             {
  334.                 if (_points == null)
  335.                     _points = new PointCollection();

  336.                 return _points;
  337.             }
  338.             set
  339.             {
  340.                 _points = value;
  341.                 if (PropertyChanged != null)
  342.                     PropertyChanged(this, new PropertyChangedEventArgs("Points"));
  343.             }
  344.         }

  345.         public event PropertyChangedEventHandler PropertyChanged;
  346.     }
  347. }
复制代码
源码下载:
附件: 亲,您没有权限下载或查看附件喔:-) 试试登录注册吧!
TOP

学习一下

学习一下
TOP

该用户文章内容已被屏蔽
TOP

怎么做的!!
TOP

不错,学习一下
TOP

学习学习!
TOP

不好,看看.
TOP

很好哦

顶一下
TOP

sdfsfasfasdfa
TOP

study
TOP