汹萃热 发表于 2025-5-30 01:11:18

游戏人生Silverlight(4) - 连连看

[索引页]
[源码下载]



游戏人生Silverlight(4) - 连连看

作者:webabcd


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


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


在线DEMO



思路
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
{
    /**//// 
    /// 连连看核心模块
    /// 
    public class Core : INotifyPropertyChanged
    {
        ObservableCollection _cards = null;
        int _rows = 0;
        int _columns = 0;
        SynchronizationContext _syncContext = null;

        public Core()
        {
            // 在容器上布满空的卡片
            _cards = new ObservableCollection();
            for (int i = 0; i  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 tempCards = new List();
                for (int i = 0; i  p.ImageName == oddImages.ElementAt(i).ImageName);
                        _cards.ImageName = tempCards.ImageName;
                    }
                }
            }

            if (!IsActive())
                Replace();

            return _cards;
        }

        /**//// 
        /// 判断两卡片是否可消
        /// 
        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  Math.Min(p1.Position, p2.Position)
                && p.Position  (p.Position - p1.Position) % Global.ContainerColumns == 0);
            };

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

            return false;
        }

        /**//// 
        /// 获取指定的 CardPoint 的 X 轴方向上的所有 ImageName 为 Global.EmptyImageName 的 CardPoint 集合
        /// 
        private List GetXPositions(CardPoint p)
        {
            var result = new List() { p };
            for (int i = 0; i 
                        {
                            Points.Clear();
                        },
                        null
                    );
                }
            );
            thread.Start();
        }

        /**//// 
        /// CardPoint 转换成坐标位置 Point
        /// 
        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);
        }

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

            for (int i = 0; i  p.ImageName).ToList();
            var targetPositions = currentCards.Select(p => p.Position).ToList();

            for (int i = 0; i  p.ImageName != Global.EmptyImageName).Count(); }
        }

        /**//// 
        /// 连连看的卡片集合
        /// 
        public ObservableCollection Cards
        {
            get { return _cards; }
        }

        private PointCollection _points;
        /**//// 
        /// 可消两卡片的连接路径上的 4 个点的集合
        /// 
        public PointCollection Points
        {
            get
            {
                if (_points == null)
                    _points = new PointCollection();

                return _points;
            }
            set
            {
                _points = value;
                if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs("Points"));
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
    }
}



OK
[源码下载]

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: 游戏人生Silverlight(4) - 连连看