一款消消乐游戏的自动解法

前段时间玩了一款网页游戏,消消乐(http://www.5u3d.com/game/xxk/index.htm?1=0?f=&i=#noref)
游戏规则介绍:
在一个10*10的方格中,有5种颜色的方块,当双击联通块时,联通块消失。(联通块的定义:至少有2个方块颜色相同,并且相连)联通块中的每一块的价值是n,n由联通块中的个数决定。
当双击联通块消失后,联通块上面的方块会下降,填充消失区域。如果某一列消失,右侧的列会向左靠齐。直到没有联通块为止,游戏结束,看谁的得分高。
分析:
1、联通块越大,最后得分越高。比如由5个小方格组成的联通块价值:5*5=25.而如果是由10块组成的,那就是10*10=100.所以应该尽量让同种色块组合成一个尽量大的联通块。
2、理想状态:颜色尽可能少,同色块尽可能是连接在一起的。这个主要是为了后来算法评估做准备。数学证明比较简单。
假设有5中颜色,每种颜色的数量为a,b,c,d,e,那么就有a+b+c+d+e=100,需要求a*a+b*b+c*c+d*d+e*e的最大值。取值范围[0~100],所以,当某一个颜色是100,最大。同理,同一个颜色的色块链接在一起价值最大。
有了这些准备,我们再来看算法:
当给定一个图形时:我们能够知道如下价格关键数据
1、图形中有多少个联通块。
2、图形中色块的完美值:各个色块的平方和。
3、由上一个图形状态转换到当前图形状态,已经产生的价值。
当我们拿到一个图形,通过分析会得到N个联通块,每一个联通块消失都会产生一种新的视图。就有N种情况。在这N中情况中,我们会选择评估值最高的去尝试(贪心嘛,总是为了得到最大值),于是就开始了递归查询。
程序设计:
1、一个视图类,用来保存视图的各种数据
2、视图帮助类,用来计算分析视图的,(当然完全可以放在视图类里面)
3、视图管理类,用来处理算法,筛选最优视图去处理,并且调度并行任务。
视图类GameVIew


</pre> <pre code_snippet_id="1596212" snippet_file_name="blog_20160303_2_8130802" name="code" class="csharp">using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace BlockGame
{
    public class GameView
    {
        public GameView()
        {
            State = 0;
        }
        /// <summary>
        /// 0:未检测过
        /// 1:
        /// </summary>
        public int State
        {
            get; set;
        }
        public GameView Parent
        {
            get; set;
        }
        public int Col
        {
            get; set;
        }
        public int Row
        {
            get; set;
        }
        public int Color
        {
            get; set;
        }
        public int[,] ViewData
        {
            get; set;
        }
        public string  ViewDataString
        {
            get; set;
        }
        /// <summary>
        /// 转换到当前视图的累计分数
        /// </summary>
        public int PreScore
        {
            get; set;
        }
        /// <summary>
        /// 从上一个视图转换到当前视图的分数
        /// </summary>
        public int StepScore
        {
            get; set;
        }
        /// <summary>
        /// 视图的评估值=当前值+视图完美值
        /// </summary>
        public int AppraiseScore
        {
            get; set;
        }
        /// <summary>
        /// 当前视图的完美值
        /// </summary>
        public int PerfectScore
        {
            get;set;
        }
    }
}

帮助类:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;

namespace BlockGame
{
    public class GameViewHepler
    {
        public void CalcPerfectScore(GameView view)
        {
            int[] cs = new int[view.Color];
            int c = 0;
            for (int x = 0; x < view.Row; x++)
            {
                for (int y = 0; y < view.Col; y++)
                {
                    c = view.ViewData[x, y];
                    if (c != 0)
                    {
                        cs[c-1]++;
                    }
                }
            }
            int perfectScore = 0;
            for (int i = 0; i < cs.Length; i++)
            {
                perfectScore += cs[i] * cs[i];
            }
            view.PerfectScore = perfectScore;
        }
        public void CalcAppraiseScore(GameView view)
        {
            view.AppraiseScore = view.PreScore + view.PerfectScore;
        }
        /// <summary>
        /// 初始化视图ViewDataString属性
        /// </summary>
        /// <param name="view"></param>
        public void CalcViewDataString(GameView view)
        {
            StringBuilder s = new StringBuilder();
            for (int x = 0; x < view.Row; x++)
            {
                for (int y = 0; y < view.Col; y++)
                {
                    s.Append(view.ViewData[x, y].ToString() + ",");
                }
            }
            view.ViewDataString = s.ToString();
        }
        /// <summary>
        /// 由字符串初始化GameView
        /// GameView已经被正确创建Row,Col,Color,PreScore,StepScore,Parent属性
        /// 随后初始化其他属性:ViewData,ViewDataString,PerfectScore,AppraiseScore,
        /// </summary>
        /// <param name="view"></param>
        /// <param name="s"></param>
        public void InitViewDataByString(GameView view,string s)
        {
            #region 初始化数据部分
            string[] strs = s.Split(',');
            for (int x = 0; x < view.Row; x++)
            {
                for (int y = 0; y < view.Col; y++)
                {
                    int c = Convert.ToInt32(strs[x * view.Col + y]);
                    view.ViewData[x, y] = c;
                }
            }
            #endregion

            view.ViewDataString = s;

            CalcPerfectScore(view);

            CalcAppraiseScore(view);
        }

        #region 计算有多少个联通块
        /// <summary>
        /// 计算有多少个联通块
        /// </summary>
        /// <param name="view"></param>
        /// <returns></returns>
        public List<List<Point>> CalcBlocks(GameView view)
        {
            int[,] usedView = new int[view.Row, view.Col];
            List<List<Point>> ViewBlocks = new List<List<Point>>();
            for (int x = 0; x < view.Row; x++)
            {
                for (int y = 0; y < view.Col; y++)
                {
                    if (usedView[x, y] == 0)
                    {
                        //没有被使用过
                        int c = view.ViewData[x, y];
                        if (c > 0)
                        {
                            usedView[x, y] = 1;
                            List<Point> block = new List<Point>();
                            block.Add(new Point(x, y));
                            analyseNext(c, x, y, block, view, usedView);
                            if (block.Count == 1)
                            {
                                //只有一个块的,不进入
                                continue;
                            }
                            ViewBlocks.Add( block);
                        }
                    }
                }
            }
            return ViewBlocks;
        }
        private void analyseNext(int color, int x, int y, List<Point> block,GameView view,int[,] usedView)
        {
            if (x - 1 >= 0)
            {
                if (view.ViewData[x - 1, y] == color && usedView[x - 1, y] == 0)
                {
                    block.Add(new Point(x - 1, y));
                    usedView[x - 1, y] = 1;
                    analyseNext(color, x - 1, y, block,view,usedView);
                }
            }
            if (x + 1 < view.Row)
            {
                if (view.ViewData[x + 1, y] == color && usedView[x + 1, y] == 0)
                {
                    block.Add(new Point(x + 1, y));
                    usedView[x + 1, y] = 1;
                    analyseNext(color, x + 1, y, block,view,usedView);
                }
            }
            if (y - 1 >= 0)
            {
                if (view.ViewData[x, y - 1] == color && usedView[x, y - 1] == 0)
                {
                    block.Add(new Point(x, y - 1));
                    usedView[x, y - 1] = 1;
                    analyseNext(color, x, y - 1, block, view, usedView);
                }
            }
            if (y + 1 < view.Col)
            {
                if (view.ViewData[x, y + 1] == color && usedView[x, y + 1] == 0)
                {
                    block.Add(new Point(x, y + 1));
                    usedView[x, y + 1] = 1;
                    analyseNext(color, x, y + 1, block, view, usedView);
                }
            }
        }
        #endregion

        /// <summary>
        /// 移除某个联通块,构建一个新的视图
        /// </summary>
        /// <param name="view"></param>
        /// <param name="block"></param>
        /// <returns></returns>
        public GameView RemoveBlock(GameView view, List<Point> block)
        {
            #region Clone新视图
            GameView v = new GameView()
            {
                Col = view.Col,
                Row = view.Row,
                Color = view.Color,
                Parent = view,
                PreScore = view.PreScore + block.Count * block.Count,
                StepScore = block.Count * block.Count 
            };
            v.ViewData = new int[v.Row, v.Col];
            InitViewDataByString(v, view.ViewDataString);
            #endregion
            #region 移除
            foreach (Point p in block)
            {
                v.ViewData[p.X, p.Y] = 0; 
            }
            #endregion
            ReBuildViewData(v);
            CalcViewDataString(v);
            CalcPerfectScore(v);
            CalcAppraiseScore(v);
            return v;
        }
        /// <summary>
        /// 重构视图的数据部分,也就是移动后ViewData
        /// </summary>
        private void ReBuildViewData(GameView view)
        {
            for (int y = view.Col - 1; y >= 0; y--)
            {
                //是否需要向左移动
                bool moveleft = true;
                //从上向下找,是否有颜色快存在
                bool hasColor = false;
                //找到右侧可以平移的列
                bool hasCol = false;
                #region 掉落
                for (int x = 0; x < view.Row; x++)
                {
                    if (view.ViewData[x, y] != 0)
                    {
                        moveleft = false;
                        hasColor = true;
                    }
                    else
                    {
                        if (hasColor)
                        {
                            //开始下移
                            for (int i = x; i >= 0; i--)
                            {
                                if (i == 0)
                                {
                                    view.ViewData[i, y] = 0;
                                }
                                else
                                {
                                    if (view.ViewData[i - 1, y] == 0)
                                    {
                                        view.ViewData[i, y] = 0;
                                        break;
                                    }
                                    else
                                    {
                                        view.ViewData[i, y] = view.ViewData[i - 1, y];
                                    }
                                }
                            }
                        }
                    }
                }
                #endregion
                #region 左移
                if (moveleft)
                {
                    if (y == view.Col - 1)
                    {
                        continue; ;
                    }
                    for (int i = 0; i < view.Row; i++)
                    {
                        if (view.ViewData[i, y + 1] != 0)
                        {
                            hasCol = true;
                            break;
                        }
                    }
                    if (hasCol)
                    {
                        for (int i = 0; i < view.Row; i++)
                        {
                            for (int j = y; j < view.Col; j++)
                            {
                                if (j == view.Col - 1)
                                {
                                    view.ViewData[i, j] = 0;
                                }
                                else
                                {
                                    view.ViewData[i, j] = view.ViewData[i, j + 1];
                                }
                            }
                        }
                    }
                }
                #endregion
            }
        }
    }
}
管理类:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Drawing;
using System.Diagnostics;

namespace BlockGame
{
    public class GameViewManager
    {
        private int max;
        private List<GameView> gameViewList;
        private List<GameView> resultViewList;
        private GameViewHepler hepler;
        private object objlock = new object();
        private Dictionary<string, int> taskDic;
        private Action<int,long> completedEventHandler;
        private long time;
        public Action<int,long> Completed
        {
            set
            {
                completedEventHandler = value;
            }
        }
        private void OnCompleted()
        {
            if (completedEventHandler != null)
            {
                completedEventHandler(max,time);
            }
        }
        public GameViewManager()
        {
            gameViewList = new List<GameView>();
            resultViewList = new List<GameView>();
            hepler = new GameViewHepler();
            max = 0;
            taskDic = new Dictionary<string, int>();
        }
        /// <summary>
        /// 分析一个GameView,并转换成下一步的若干个GameView
        /// </summary>
        /// <param name="view"></param>
        private void analyseGameView(GameView view)
        {
            List<List<Point>> blocks = hepler.CalcBlocks(view);
            if (blocks.Count == 0)
            {
                //此视图终结
                resultViewList.Add(view);
                return;
            }
            foreach (List<Point> block in blocks)
            {
                GameView v = hepler.RemoveBlock(view, block);
                lock(objlock)
                {
                    if (v.PreScore > max)
                    {
                        max = v.PreScore;
                    }
                }
                #region 判断是否存在
                GameView evl=null;
                lock(objlock)
                {
                    evl = (from p in gameViewList
                           where p.ViewDataString == v.ViewDataString
                           select p).FirstOrDefault();
                    if (evl == null)
                    {
                        Console.WriteLine("添加新view");
                        gameViewList.Add(v);
                    }
                    else
                    {
                        Console.WriteLine("有相同view存在");
                        if (evl.PreScore < v.PreScore)
                        {
                            Console.WriteLine("有相同view存在,而且新的更优");
                            evl = v;
                        }
                    }
                }
                //Thread.Sleep(3000);
                #endregion
                #region 判断是否是优秀结果
                //在选择的时候已经过滤了,所以,这里不用处理
                //是不是删除更好?加快查找?但也有可能,删除后会再次加入
                var bv = from p in gameViewList
                         where p.AppraiseScore <= max && p.State == 0
                         select p;
                lock(objlock)
                {
                    Console.WriteLine("将会淘汰{0}", bv.Count());
                }
                #endregion

                //Console.WriteLine("执行完毕");
            };
        }
        private Task analyseGameViewAsync(string taskid)
        {
             return Task.Run(()=>
             {
                 bool flag = true;
                 while (flag)
                 {
                     GameView view=null;
                     lock (objlock)
                     {
                         view = (from p in gameViewList
                                 where p.State == 0 && p.AppraiseScore >= max
                                 orderby p.AppraiseScore descending
                                 select p).FirstOrDefault();
                         if (view != null)
                         {
                             view.State = 1;
                             taskDic[taskid] = 1;
                         }
                         else
                         {
                             taskDic[taskid] = 0;
                         }
                     }
                     if (view == null)
                     {
                         //其他线程都完成了,整个任务完成,
                         bool completed = true;
                         lock(objlock)
                         {
                             foreach (KeyValuePair<string, int> kvp in taskDic)
                             {
                                 if (kvp.Value == 1)
                                 {
                                     completed = false;
                                     break;
                                 }
                             }
                         }
                         if (completed)
                         {
                             flag = false;
                         }
                     }
                     else
                     {
                         Console.WriteLine("线程{0}处理一条数据", taskid);
                         analyseGameView(view);
                     }
                 }
                 OnCompleted();
             });
        }
        public  void AddTask(string taskid)
        {
            if (taskDic.ContainsKey(taskid))
            {
                throw new Exception("重复taskid");
            }
            lock(objlock)
            {
                taskDic.Add(taskid, 1);
            }
            /*
            Stopwatch watch = new Stopwatch();
            await analyseGameViewAsync(taskid);
            */
        }
        public async void StartTask()
        {
            List<Task> task = new List<Task>();
            lock(objlock)
            {
                foreach (var taskid in taskDic)
                {
                    Task t = analyseGameViewAsync(taskid.Key);
                    task.Add(t);
                }
            }
            Stopwatch watch = new Stopwatch();
            watch.Start();
            await Task.WhenAll(task);
            watch.Stop();
            time = watch.ElapsedMilliseconds;
            Console.WriteLine("共用时:{0}", watch.ElapsedMilliseconds);
            OnCompleted();
        }

        public void AddFirstGameView(GameView view)
        {
            max = 0;
            time = 0;
            gameViewList.Clear();
            resultViewList.Clear();
            gameViewList.Add(view);
        }
    }
}


窗体里面调用

首先初始化第一个视图

            int x = Convert.ToInt16(this.textBox1.Text);
            int y = Convert.ToInt16(this.textBox2.Text);
            int c = Convert.ToInt16(this.textBox3.Text);


            gv = new GameView() { Row = x, Col = y, Color = c, PreScore = 0, StepScore = 0 };
            gv.ViewData = new int[gv.Row, gv.Col];


            string str = "";
            for (int i = 0; i < x; i++)
            {
                for (int j = 0; j < y; j++)
                {
                    Random r = new Random(Guid.NewGuid().GetHashCode());
                    int rc = r.Next(1, c + 1);
                    str += rc.ToString() + ",";
                }
            }
            hepler.InitViewDataByString(gv, str);

开始计算任务

GameViewManager manager = new GameViewManager();
            manager.AddFirstGameView(gv);
            manager.Completed = new Action<int,long>(Show);
            manager.AddTask("1");
            manager.AddTask("2");
            manager.AddTask("3");
            manager.AddTask("4");
            manager.StartTask();


程序运行效果图


源代码即下载地址:http://download.csdn.net/detail/mamihong/9452014

vs2015的

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值