游戏人生Silverlight(4) - 连连看[Silverlight 2.0(c#)]

[索引页]
[源码下载]


游戏人生Silverlight(4) - 连连看[Silverlight 2.0(c#)]



作者:webabcd


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


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


在线DEMO
Get Microsoft Silverlight


思路
1、卡片初始排列算法:已知容器容量为 x, 不重复的卡片数量为 y, x >= y && x % 2 == 0, 首先在容器内随机排列卡片,然后取出容器内相同的卡片个数为奇数的集合(集合内成员数量必为偶数个),最后将该集合一刀切,将集合右半部分的卡片的依次复制到集合左半部分。以上算法保证了在一定随机率的基础上,不会出现相同的卡片个数为奇数的情况
2、无解算法和重排算法:在容器内存在的卡片中,两两计算是否存在可消路径,如果没有就是无解,需要重排。重排时,需要得到现存的卡片集合和卡片位置集合,在卡片集合中随机取卡片(取出一个,原集合就要移除这一个),然后依次放到卡片位置集合内,从而达到将现存卡片重新排列的目的
3、两点消去路径的算法以及取最优消去路径的算法:取玩家选的第一点的 x 轴方向和 y 轴方向上的所有无占位符的坐标集合(包括自己),名称分别为 x1s, y1s;取玩家选的第二点的 x 轴方向和 y 轴方向上的所有无占位符的坐标集合(包括自己),名称分别为 x2s, y2s。先在 x1s 和 x2s 中找 x 坐标相等的两点,然后找出该两点与玩家选的两点可组成一条连续的直线的集合,该集合就是可消路径的集合,之后同理再在 y1s 和 y2s 中找到可消路径的集合。两集合合并就是玩家选中的两点间的所有可消路径的集合,该集合为空则两点不可消,该集合内的最短路径则为最优消去路径,集合内的 4 点连接线则为消去路径的连接线
4、游戏使用MVVM(Model - View - ViewModel)模式开发


关键代码
Core.cs

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

using YYMatch.Models;
using System.Collections.ObjectModel;
using System.Linq;
using System.Collections.Generic;
using System.ComponentModel;
using System.Threading;

namespace YYMatch.ViewModels
{
    
/// <summary>
    
/// 连连看核心模块
    
/// </summary>

    public class Core : INotifyPropertyChanged
    
{
        ObservableCollection
<CardModel> _cards = null;
        
int _rows = 0;
        
int _columns = 0;
        SynchronizationContext _syncContext 
= null;

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


            _syncContext 
= SynchronizationContext.Current;
        }


        
public void Start(int rows, int columns)
        
{
            _rows 
= rows;
            _columns 
= columns;

            InitCard();
        }


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

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

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

                }

            }


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

            
// 如果 oddImages 集合的成员为奇数个(保证容器容量为偶数个则不可能出现这种情况)
            if (oddImages.Count() % 2 > 0)
            
{
                
throw new Exception("无法初始化程序");
            }

            
else
            
{
                
// 在集合中将所有的个数为奇数的卡片各自取出一个放到 temp 中
                
// 将 temp 一刀切,使其右半部分的卡片的 ImageName 依次赋值为左半部分的卡片的 ImageName
                
// 由此保证相同的卡片均为偶数个
                List<CardModel> tempCards = new List<CardModel>();
                
for (int i = 0; i < oddImages.Count(); i++)
                
{
                    
if (i < oddImages.Count() / 2)
                    
{
                        var tempCard 
= _cards.Last(p => p.ImageName == oddImages.ElementAt(i).ImageName);
                        tempCards.Add(tempCard);
                    }

                    
else
                    
{
                        var tempCard 
= _cards.Last(p => p.ImageName == oddImages.ElementAt(i).ImageName);
                        _cards[tempCard.Position].ImageName 
= tempCards[i - oddImages.Count() / 2].ImageName;
                    }

                }

            }


            
if (!IsActive())
                Replace();

            
return _cards;
        }


        
/// <summary>
        
/// 判断两卡片是否可消
        
/// </summary>

        public bool Match(CardModel c1, CardModel c2, bool removeCard)
        
{
            
bool result = false;

            
if (c1.ImageName != c2.ImageName
                
|| c1.ImageName == Global.EmptyImageName
                
|| c2.ImageName == Global.EmptyImageName
                
|| c1.Position == c2.Position)
                
return false;

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

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

            var p1xs 
= GetXPositions(p1);
            var p1ys 
= GetYPositions(p1);
            var p2xs 
= GetXPositions(p2);
            var p2ys 
= GetYPositions(p2);

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

                    
// 查找最短连接路径
                    if (length < minLength)
                    
{
                        minLength 
= length;
                        point1 
= p1;
                        point2 
= px.p1x;
                        point3 
= px.p2x;
                        point4 
= p2;
                    }

                    result 
= true;
                }

            }


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

                    
// 查找最短连接路径
                    if (length < minLength)
                    
{
                        minLength 
= length;
                        point1 
= p1;
                        point2 
= py.p1y;
                        point3 
= py.p2y;
                        point4 
= p2;
                    }

                    result 
= true;
                }

            }


            
if (removeCard && result)
            
{
                RemoveCard(c1, c2, point1, point2, point3, point4);
            }


            
return result;
        }


        
/// <summary>
        
/// 直线上的两个 CardPoint 是否可以消去
        
/// </summary>

        private bool MatchLine(CardPoint p1, CardPoint p2)
        
{
            
if (p1.X != p2.X && p2.Y != p2.Y)
                
return false;

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

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

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

            
return false;
        }


        
/// <summary>
        
/// 获取指定的 CardPoint 的 X 轴方向上的所有 ImageName 为 Global.EmptyImageName 的 CardPoint 集合
        
/// </summary>

        private List<CardPoint> GetXPositions(CardPoint p)
        
{
            var result 
= new List<CardPoint>() { p };
            
for (int i = 0; i < Global.ContainerColumns; i++)
            
{
                var point 
= new CardPoint(p.Y * Global.ContainerColumns + i);
                
if (_cards[point.Position].ImageName == Global.EmptyImageName)
                    result.Add(point);
            }


            
return result;
        }


        
/// <summary>
        
/// 获取指定的 CardPoint 的 Y 轴方向上的所有 ImageName 为 Global.EmptyImageName 的 CardPoint 集合
        
/// </summary>

        private List<CardPoint> GetYPositions(CardPoint p)
        
{
            var result 
= new List<CardPoint>() { p };
            
for (int i = 0; i < Global.ContainerRows; i++)
            
{
                var point 
= new CardPoint(i * Global.ContainerColumns + p.X);
                
if (_cards[point.Position].ImageName == Global.EmptyImageName)
                    result.Add(point);
            }


            
return result;
        }


        
/// <summary>
        
/// 执行消去两个卡片的任务
        
/// 参数为:两个卡片对象和消去路径的四个点(此四个点需按路径方向依次传入)
        
/// </summary>

        private void RemoveCard(CardModel c1, CardModel c2, CardPoint p1, CardPoint p2, CardPoint p3, CardPoint p4)
        
{
            _cards[c1.Position] 
= new CardModel(Global.EmptyImageName, c1.Position);
            _cards[c2.Position] 
= new CardModel(Global.EmptyImageName, c2.Position);

            Points.Clear();
            Points.Add(CardPoint2Point(p1));
            Points.Add(CardPoint2Point(p2));
            Points.Add(CardPoint2Point(p3));
            Points.Add(CardPoint2Point(p4));

            Thread thread 
= new Thread
            (
                x 
=>
                
{
                    Thread.Sleep(
100);

                    _syncContext.Post
                    (
                        y 
=>
                        
{
                            Points.Clear();
                        }
,
                        
null
                    );
                }

            );
            thread.Start();
        }


        
/// <summary>
        
/// CardPoint 转换成坐标位置 Point
        
/// </summary>

        private Point CardPoint2Point(CardPoint cardPoint)
        
{
            
// 38 - 每个正方形卡片的边长
            
// 19 - 边长 / 2
            
// cardPoint.X * 2 - 卡片的 Padding 为 1 ,所以卡片间的间距为 2
            var x = cardPoint.X * 38 + 19 + cardPoint.X * 2;
            var y 
= cardPoint.Y * 38 + 19 + cardPoint.Y * 2;

            
return new Point(x, y);
        }


        
/// <summary>
        
/// 检查当前是否仍然有可消除的卡片
        
/// </summary>

        public bool IsActive()
        
{
            var currentCards 
= _cards.Where(p => p.ImageName != Global.EmptyImageName).ToList();

            
for (int i = 0; i < currentCards.Count() - 1; i++)
            
{
                
for (int j = i + 1; j < currentCards.Count(); j++)
                
{
                    
if (Match(currentCards[i], currentCards[j], false))
                        
return true;
                }

            }


            
return false;
        }


        
/// <summary>
        
/// 重新排列当前卡片集合
        
/// 并保证有可消卡片
        
/// </summary>

        public void Replace()
        
{
            Random r 
= new Random();
            var currentCards 
= _cards.Where(p => p.ImageName != Global.EmptyImageName).ToList();
            var count 
= currentCards.Count;
            
if (count == 0)
                
return;

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

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

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

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


            
while (!IsActive())
            
{
                Replace();
            }

        }


        
/// <summary>
        
/// 清除卡片集合
        
/// </summary>

        public void Clear()
        
{
            _cards.Clear();
            Points.Clear();
        }


        
/// <summary>
        
/// 当前卡片数量
        
/// </summary>

        public int CardCount
        
{
            
get return _cards.Where(p => p.ImageName != Global.EmptyImageName).Count(); }
        }


        
/// <summary>
        
/// 连连看的卡片集合
        
/// </summary>

        public ObservableCollection<CardModel> Cards
        
{
            
get return _cards; }
        }


        
private PointCollection _points;
        
/// <summary>
        
/// 可消两卡片的连接路径上的 4 个点的集合
        
/// </summary>

        public PointCollection Points
        
{
            
get
            
{
                
if (_points == null)
                    _points 
= new PointCollection();

                
return _points;
            }

            
set
            
{
                _points 
= value;
                
if (PropertyChanged != null)
                    PropertyChanged(
thisnew PropertyChangedEventArgs("Points"));
            }

        }


        
public event PropertyChangedEventHandler PropertyChanged;
    }

}




OK
[源码下载]

posted @ 2009-04-16 09:05  webabcd  阅读(16908)  评论(63编辑  收藏  举报