数学吧 关注:898,205贴子:8,778,491

求助一个地图生成的问题

只看楼主收藏回复

我有在独自制作一个垃圾小游戏,遇到一个问题,就是在一个2维的非连续地图上根据已有的两个坐标随机生成指定长度的道路。
具体是这样的。假设有一个大小为n x m的相连方块,方块只能是墙或是地板,现在已知两个地板的坐标为(a,b),(c,d),其曼哈顿距离为L = |a-c|+|b-d|,求生成一个只能上下左右移动的长度为L+N(N为大于0的偶数)且没有岔路的连接两个已知地板的连续地板道路。其中没有岔路是指除了已知的两个地板以外,所有生成的地板必然只和两个墙和两个通路相连。

这个问题我倒是可以用编程语言使用递归随机遍历所有可能获得。但是我觉得这不够优雅,想要使用一种数学工具,将这个问题简化,或将其隐含的关系用数学公式表达出来。
我首先想到的就是将求坐标序列的问题转换为求生成的第n个地板对第n-1个地板的偏移序列,这很容易,因为两个初始地板已知,且只能上下左右生成。比如上图示例中,右下角的地板相对左上角的地板向右偏移3格、向下偏移3格,于是就可以得到一个序列(→,→,→,↓,↓,↓),然后将这个序列随机打乱,再根据序列由左上角地板开始生成地板,得到一个随机的长度为L + 0 = 6的连续道路,但是如果我想要生成L+4的道路并往之前的序列里加入(→,↓,↑,↓)四个偏移后并随机打乱,就可能生成图中展示的错误道路。这是因为生成的地板必须满足只和两个地板两个墙相连,而我想出的偏移序列并没有考虑到这一点。
所以我想问问吧里的带佬们有没有什么办法或思路来解决这个问题,除了递归。
当然带佬们也可以直接开喷“自己的程序自己想啊!”,那至少希望可以可怜可怜下我这个菜鸡给我指明一条道路,让我知道哪个方向的数学知识可以解决这个问题,最好给一个相似的经典问题让我参考参考。谢谢谢谢


IP属地:湖北1楼2022-07-31 16:26回复
    围棋,长三气,拐两气


    IP属地:上海来自Android客户端3楼2022-08-01 19:14
    收起回复
      从头开始说吧。首先,我们定义空格的上下左右四格是它的气,对角线不算,很容易得到一格是4口气,两格是6口气。
      然后我们可以定义“长”,就是往当前方向继续前进一格,会发现有三个新格子变成了“气”,而有一格原来的气被这一格占据,增加了2口气。
      接下来定义“拐”,就是朝当前方向的90度前进一格,我们发现多了两口新气,损失一口气,这样算下来是多一口气。
      下一种就可以叫做“团”,具体可以参考一个L4(俄罗斯方块玩过吧),团成一个刀把5(我相信你可以想出来是什么样),一口气都没有增加,可以说是“死”了。这时候发现,和你题设里的“岔路”条件相吻合了。
      所以按照你的想法,接下来可以用气来排除路线,如果一条路线走的时候有一步“气”没有增加,那么这条路线就不符合要求。


      IP属地:上海来自Android客户端4楼2022-08-01 19:39
      收起回复
        似乎可以参考MC中海底神殿的生成方式(wiki有)


        IP属地:陕西来自Android客户端5楼2022-08-01 22:58
        回复
          既然你都想到了N必须是偶数,那你怎么想不到添加N个操作的时候保证左右个数相等上下个数相等呢,再然后是环路避免,这个有点难整,我再想想


          IP属地:北京来自Android客户端6楼2022-08-01 23:53
          收起回复
            那个气的思路确实牛,不过我自己设计迷宫的时候,一般都是直接两格两格生长,只要没撞到就绝对不会有相邻的地板


            IP属地:陕西来自Android客户端7楼2022-08-02 08:32
            回复
              假设终点在起点的右下方
              当n不为0且为偶数的时候,在原先的n=0的道路的基础上修改,使得向左走的格子数量与向上走的格子数量之和恰好是n的一半。
              回路判断自己写,应该不难的


              IP属地:湖南来自Android客户端8楼2022-08-02 08:57
              回复
                穷举


                IP属地:湖北来自Android客户端9楼2022-08-02 11:22
                回复
                  先把错误的也列出来,然后回头检查把错误的删除掉


                  IP属地:湖北来自Android客户端10楼2022-08-02 11:24
                  回复
                    每走第n+1步的时候,从第n步那一格周围三个可选格中选一个,同时将剩下两格标记为不能走的格子,最后把第n+1格也标记为不可走,这样的方法枚举也能避免出现你说的错误


                    IP属地:湖北来自Android客户端11楼2022-08-02 11:29
                    回复
                      递归里加个条件判断:
                      如果:当且仅当偏移的路的上下左右存在另一条路,递归继续,否则跳过
                      这样可以保证联通且无岔路


                      IP属地:上海来自Android客户端12楼2022-08-02 13:12
                      回复
                        算法去计算机科学讨论吧,这题初步看是动规


                        IP属地:江苏来自Android客户端13楼2022-08-02 14:16
                        回复
                          因为每个格子周围必须有两堵墙,那么只要每次生成一个新的格子,就把上一个格子周围打上×就可以了,比如三个格子的排布是(用手机九宫格表示)458,那么对于4来说17,5来说426就是不能再经过的位置(当然这种情况下8的下一个也只能是0)。不过这样没法保证在用完额外步数后剩余的箭头是否一定能走到终点


                          IP属地:美国来自iPhone客户端14楼2022-08-02 15:33
                          回复
                            或者换一个想法,先生成一条长为L的起点到终点的路,再对其修改N/2次。每次选定一格然后往旁边“推”一下,推的格子只能是和周围两格呈一条直线的。比如258变成21478或者23698,但458就无法推动。然后看变换后是否仍然满足条件。如果不满足就撤销并从candidate里移除,避免重复计算。


                            IP属地:美国来自iPhone客户端15楼2022-08-02 15:54
                            回复