游戲人生Silverlight(4) - 連連看[Silverlight 2.0(c#)]
作者:webabcd
介紹
使用 Silverlight 2.0(c#) 開發(fā)一個連連看游戲
玩法
用鼠標左鍵選中卡片,如果選中的兩卡片間的連線不多于 3 根直線,則選中的兩卡片可消除
在線DEMO
思路
1、卡片初始排列算法:已知容器容量為 x, 不重復的卡片數(shù)量為 y, x >= y && x % 2 == 0, 首先在容器內(nèi)隨機排列卡片,然后取出容器內(nèi)相同的卡片個數(shù)為奇數(shù)的集合(集合內(nèi)成員數(shù)量必為偶數(shù)個),最后將該集合一刀切,將集合右半部分的卡片的依次復制到集合左半部分。以上算法保證了在一定隨機率的基礎上,不會出現(xiàn)相同的卡片個數(shù)為奇數(shù)的情況
2、無解算法和重排算法:在容器內(nèi)存在的卡片中,兩兩計算是否存在可消路徑,如果沒有就是無解,需要重排。重排時,需要得到現(xiàn)存的卡片集合和卡片位置集合,在卡片集合中隨機取卡片(取出一個,原集合就要移除這一個),然后依次放到卡片位置集合內(nèi),從而達到將現(xiàn)存卡片重新排列的目的
3、兩點消去路徑的算法以及取最優(yōu)消去路徑的算法:取玩家選的第一點的 x 軸方向和 y 軸方向上的所有無占位符的坐標集合(包括自己),名稱分別為 x1s, y1s;取玩家選的第二點的 x 軸方向和 y 軸方向上的所有無占位符的坐標集合(包括自己),名稱分別為 x2s, y2s。先在 x1s 和 x2s 中找 x 坐標相等的兩點,然后找出該兩點與玩家選的兩點可組成一條連續(xù)的直線的集合,該集合就是可消路徑的集合,之后同理再在 y1s 和 y2s 中找到可消路徑的集合。兩集合合并就是玩家選中的兩點間的所有可消路徑的集合,該集合為空則兩點不可消,該集合內(nèi)的最短路徑則為最優(yōu)消去路徑,集合內(nèi)的 4 點連接線則為消去路徑的連接線
4、游戲使用MVVM(Model - View - ViewModel)模式開發(fā)
關(guān)鍵代碼
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();
// 卡片集合在容器內(nèi)的范圍
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);
}
}
}
// 相同的卡片個數(shù)為奇數(shù)的集合
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 集合的成員為奇數(shù)個(保證容器容量為偶數(shù)個則不可能出現(xiàn)這種情況)
if (oddImages.Count() % 2 > 0)
{
throw new Exception("無法初始化程序");
}
else
{
// 在集合中將所有的個數(shù)為奇數(shù)的卡片各自取出一個放到 temp 中
// 將 temp 一刀切,使其右半部分的卡片的 ImageName 依次賦值為左半部分的卡片的 ImageName
// 由此保證相同的卡片均為偶數(shù)個
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>
/// 執(zhí)行消去兩個卡片的任務
/// 參數(shù)為:兩個卡片對象和消去路徑的四個點(此四個點需按路徑方向依次傳入)
/// </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 轉(zhuǎn)換成坐標位置 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>
/// 當前卡片數(shù)量
/// </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(this, new PropertyChangedEventArgs("Points"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
}

OK
[源碼下載]


浙公網(wǎng)安備 33010602011771號