zzzznl 发表于 2024-8-27 20:45

算法求助:小游戏方块之谜遍历解

媳妇给娃买的玩具(个人觉得商标不算广告,如需打码,我再改),媳妇不会玩,我就想写段代码辅助一下。

​一开始我觉得这代码很简单呀,递归一下,算就完了,花了半天撸出一版代码,跑起来发现是我肤浅了,代码跑完怕是得以年计,那我要他做甚,陪我女儿慢慢长大吗。于是怒改代码,少许剪枝后,3*5的格子一分钟就算出一组解。我想了想,要他做甚,和我比赛谁拼的快吗。于是大改代码,先列出所有组合,对每种组合计算,这样必要时可以开线程。结果单线程时3*5用时1秒不到得28组解(多为镜像),4*5用时2秒多得193解(这个有点奇怪),5*5用时近一分钟得796解,6*5用时近21分钟得1985解,所以很显然,这算法还是失败的。

​由于我基础确实很差,大学好像是学过算法课,但早忘光了,重新学习进度极为缓慢,在此恳请大佬指导下复杂度较低的算法,最好能在1小时内遍历完12*5的所有解。如有大佬指导,不胜感激。
我的主要思路:
1.用二维数组表示积木方块,类似int {
            {3,3,3,3 },
            {0,0,3,0 } };
2.旋转或翻转后重复(对称)的方块提前建立数组,记录需要翻转或旋转的次数;
3.棋盘为二维数组,放置积木是将积木数组与棋盘数组相加,放完会判断下重叠,即积木数组区域是否出现大于积木数值的值
      private int[,] Addarray(int[,] nx, int[,] na, int r, int c,int nn,ref bool ol)
      {
            int[,] temp = new int;
            Array.Copy(nx,temp, nx.Length);
            checked
            {
                try
                {
                  for (int i = r; i < (na.GetLength(0) + r); i++)
                  {
                        for (int j = c; j < (na.GetLength(1) + c); j++)
                        {
                            temp += na;
                            if (temp > (nn + 1))//检查是否重叠
                            {
                              ol = true;
                              return nx;
                            }
                        }
                  }
                  ol = false;
                  return temp;
                }
                catch (Exception e)
                {
                  textBox1.Text += "故障:" + e + "\n\r";
                  ol = true;
                  return nx;
                }
            }
      }

4. 3-11块的需生成所有积木组合
      private void generateTask(int n)//遍历积木组合
      {
            prog_task.Clear();
            int[] set= new int;
            for (int i = 0; i < n; i++) set = i;//初始化,第一个集合
            prog_task.Add(set.ToArray());
            if (n < 12)
            {
                int position = n - 1;
                while (true)
                {
                  if (set == 11) position--;//最右数到上限,指针左移一位
                  else position = n - 1;//否则指针至最右
                  set++;
                  // 调整右边元素
                  for (int i = position + 1; i < n; i++)
                        set = set + 1;
                  prog_task.Add(set.ToArray());
                  if (set >= (12 - n))
                        break;
                }
            }
      }

5.求解过程用递归,必须积木全部放完,则进行记录,并额外回退一个,否则为失败,下一个;
      private void docal(int[,] sqr, int[] task, int nu)
      {
            if (nu == num) //积木放完,输出结果
            {
                ans.Add(Recordans(sqr));
                //textBox1.Text += DateTime.Now.ToString() + "获得一组解\r\n";
                flag2 = 1;
            }
            else
            {
                int n = task;
                int[,] ntemp = nl;
                for (int k = 0; k < sn; k++)//翻转
                {
                  for (int l = 0; l < sn; l++)//旋转90度×4
                  {
                        for (int i = 0; i < 6 - ntemp.GetLength(0); i++)
                        {
                            for (int j = 0; j < num + 1 - ntemp.GetLength(1); j++)
                            {
                              if ((sqr * ntemp) != 0) //格子已放则下一格
                              {
                                    continue;
                              }
                              bool ol = false;
                              int[,] temp = Addarray(sqr, ntemp, i, j, n, ref ol);
                              //如果能放下,按放下后的棋盘放下一块,放不下则下一位置
                              if (!ol)
                              {
                                    docal(temp, task, nu + 1);
                                    if (flag2 > 0)
                                    {
                                        flag2--;
                                        return;
                                    }
                              }
                            }
                        }
                        ntemp = Roll(ntemp);
                  }
                  ntemp = Overturn(ntemp);
                }
            }
      }

6.其他,旋转和翻转,大致能用,没追求效率;
      private int[,] Roll(int[,] nx)
      {
            int[,] nr = new int;
            for (int i = 0; i < nx.GetLength(1); i++)
            {
                for (int j = 0; j < nx.GetLength(0); j++)
                {
                  nr = nx;
                }
            }
            nr = Overturn(nr);
            return nr;
      }
      private int[,] Overturn(int[,] nx)
      {
            int[,] no = new int;
            for (int i = 0; i < nx.GetLength(0); i++)
            {
                for (int j = 0; j < nx.GetLength(1); j++)
                {
                  no = nx;
                }
            }
            return no;
      }

非专业人员,起名和代码非主流,我也有考虑学习学习,这个文件的不便还请见谅。
源码:
链接:https://pan.baidu.com/s/1NC_wNfXhvQS6tKa57hOhCQ
提取码:cd0w

justwz 发表于 2024-8-27 21:52

不能套用现成的吗

zzzznl 发表于 2024-8-27 22:27

justwz 发表于 2024-8-27 21:52
不能套用现成的吗

哪里有现成的,现成的是什么,算法还是软件,或者遍历完成的解法,大佬请细说

nob 发表于 2024-8-28 07:06

可以看一下这个网站:
https://isomerdesign.com/Pentomino/

ailmail 发表于 2024-8-28 08:20

这个玩具怎么个玩法啊,没看懂

zzzznl 发表于 2024-8-28 22:46

nob 发表于 2024-8-28 07:06
可以看一下这个网站:
https://isomerdesign.com/Pentomino/

虽然网站没看到源码,但让我知道了这游戏叫pentominos,这非常有用,以此为关键字,可以搜到一些内容,具体包括:
https://math.hws.edu/xJava/PentominosSolver/source/   8x8 with 4 holes解法源码
https://benhoyt.com/writings/python-pentomino/    Fast pentomino puzzle solver ported from Forth to Python
https://github.com/brunotag/pentomino    某种限制打不开GitHub,暂时不知道这是啥
https://rosettacode.org/wiki/Pentomino_tiling#    不同语言的8x8 with 4 holes解法源码
以上可以算是现成的。虽然没细看,但其中有些不是遍历解,效率不一定高,但也够我学习好一阵了
页: [1]
查看完整版本: 算法求助:小游戏方块之谜遍历解