生成流氓地图


10

今天,我们将为Roguelike RPG生成地图!

示例图:

##########
####    F#
####    ##
##    C#C#
#     ## #
# C   #E #
####  #  #
#        #
#P       #
##########

#是墙壁,P是玩家的起点,F是必须达到的终点,C是可以收集的硬币,E是可以战斗的敌人。

地图规格:

  • 高度和宽度都应在10到39之间(包括10和39)。高度不必等于宽度。
  • 地图边框应填满墙壁。
  • P 应该放在左下角。
  • F 应该放在右上角。
  • 应该有1到3个敌人。
  • 应该有2到4个硬币。
  • 中间应该有一些墙。应该有从得到一个路径P的每一个CE以及F,牢记玩家不能斜走。
  • 每种可能的组合都应有发生的机会。

规则

  • 最低字节程序获胜。
  • 您的程序不应接受任何输入。
  • 您的程序可能不会因错误而退出(非致命输出是STDERR可以的,但是在生成地图后我们无法出现类似流氓的崩溃!)
  • 允许使用单个尾随换行符,并允许尾随空格。
  • 不允许其他输出。

3
这很像流氓,只是fyi。
Rɪᴋᴇʀ

2
您能否澄清“每种可能的组合都应有相等的发生机会”?您的字面意思是所有有效地图(尤其是P可以到达所有C / E / F的所有地图)都必须以相等的概率出现吗?如果是这样,似乎唯一可能的算法是随机均匀地生成地图,然后检查P是否可以到达所有内容,并丢弃无效的地图,直到发生这种情况为止。
格雷格·马丁

您是否也可以澄清-“中间应该有一些墙”,如果我一直只放置2墙怎么办?
Gurupad Mamadapur

1
@GregMartin我也将其更改为“所有可能的布局都应有发生的机会”,不一定是相等的机会。
帕维尔

2
被墙壁包围的无法到达的空正方形怎么办?它是有效的布局还是应该完全避免使用?(换句话说:每个空的正方形都应该可以到达吗?)
Arnauld

Answers:


5

Perl,293个字节

-9个字节,感谢@Dom Hastings

{$==7+rand 30;@r=$"=();@a=((C)x4,(E)x3,("#")x1369,(" ")x1369);for$i(0..7+rand 30){$r[$i][$_]=splice@a,rand@a,1for 0..$=}$r[0][$=]=F;$r[-1][0]=P;$_=$r=join$/,$v="#"x($=+=3),(map"#@$_#",@r),$v;1while$r=~s/F(.{$=})?[^#F]/F$1F/s||$r=~s/[^#F](.{$=})?F/F$1F/s;$r!~/[CEP]/&&/C.*C/s&&/E/?last:redo}say

添加-E标志以运行它:

perl -E '{$==7+rand 30;@r=$"=();@a=((C)x4,(E)x3,("#")x1369,(" ")x1369);for$i(0..7+rand 30){$r[$i][$_]=splice@a,rand@a,1for 0..$=}$r[0][$=]=F;$r[-1][0]=P;$_=$r=join$/,$v="#"x($=+=3),(map"#@$_#",@r),$v;1while$r=~s/F(.{$=})?[^#F]/F$1F/s||$r=~s/[^#F](.{$=})?F/F$1F/s;$r!~/[CEP]/&&/C.*C/s&&/E/?last:redo}say'

但是,它需要很长时间才能运行,因此我建议改用以下版本:

perl -E '{${$_}=8+rand 30for"=","%";@r=$"=();@a=((C)x4,(E)x3,("#")x($v=rand $=*$%),(" ")x($=*$%-$v));for$i(0..$%-1){$r[$i][$_]=splice@a,rand@a,1for 0..$=-1}$r[0][$=-1]=F;$r[$%-1][0]=P;$_=$r=join$/,$v="#"x($=+=2),(map"#@$_#",@r),$v;1while$r=~s/F(.{$=})?[^#F]/F$1F/s||$r=~s/[^#F](.{$=})?F/F$1F/s;$r!~/[CEP]/&&/C.*C/s&&/E/?last:redo}say'

在线尝试!

说明

{ #输入一个块(用作循环){ $ == 7 + rand 30 ; #随机选择地图的宽度-2 #(-2,因为我们不包括边界尚)@r = $ “=();#复位@r,并设置$” ,以民主基金@a =( 创建可能在棋盘上的角色的列表C x4 #4个硬币'C' E x3 #3个敌人'E' “#” x1369 #37 * 37'#'                      
                       
                                     
    
                                 
                               
                               
                          
     “” x1369 ); #37 * 37位$ I 0..7 +兰特30 #创建2D地图(7 + 30兰特是高度,这刚才产生)$ _ 0 $ = - 1 ){ 
        $ r [ $ i ] [ $ _ ] = #索引[$ i] [$ _]接收... 
           splice @ a rand @ a 1 #..从先前生成的列表中的随机字符#(该字符为然后由于'splice'而从列表中删除)} } 
    $ r [                    
                     
                                     
                                       
      
    0 ] [ $ =] = F ; #添加完成单元格
    $ r [ -1 ] [ 0 ] = P ; #添加开始单元
    $ _ = $ r = #在这里我们生成映射的字符串表示形式
          join $ /,#用换行符连接以下元素
            $ v = “#” x $ = + = 3 ),#首先仅#行映射“#@ $ _#” @r ),#在每行的开头和结尾添加#                                                                                                             
                       
            $ v ; ##的最后一行                        

    1while #以下正则表达式将取代每个接入的小区与A F 
       $ R =〜小号/ ˚F ({ $ =})?[^#˚F ] / F $ 1F / 小号   #右侧的细胞或底部F单元被替换   || #或
       $ R =〜小号/ [^#˚F ]({ $ =})?F / F $ 1F / s ; #替换F单元左侧或顶部的单元格
    $ r !〜/ [CEP] / #如果地图上没有C,E或P(表示它们都可以访问)&&                
                                            
      /C.*C/ s          #并且至少有2个硬币&& / E / #和最后一个敌人#地图有效,我们退出循环重做#else,重新开始} #并印制电路板
                  
                   
                    

运行需要很长时间,因为我们从中随机选择要放在板上的字符的列表(@a)包含1369个空格和#,并且只有4个硬币和3个敌人。因此,如果宽度和高度的大小较小,则存在很多空间,并且#与硬币和敌人相比,因此随机映射很可能无效。这就是为什么“优化”的版本速度更快:从我们选择的人物的名单只是比地图有点大(名单是@a=((C)x4,(E)x3,("#")x($v=rand $=*$%),($")x($=*$%-$v)):一个随机数$v#(劣于地图的大小),和size of the map - $v空格)。


我不是很了解perl,但是看语法高亮显示,您似乎在($“)x $ = ** 2)中有一个不匹配的引号;。也许高亮显示不正确,这是一个功能。 ,空格可能无法到达
Pavel

1
@Pavel $"是一个合法的Perl变量,但是语法高亮不知道它,这就是它看起来像这样的原因。好的,我将删除有关无法访问的空格的注释。
达达

5

PHP,422 417 415 309 373 369 364 361字节

function w($p){global$r,$h,$w;for($q=$p;$r[$q]<A;)for($r[$p=$q]=" ";($q=$p+(1-(2&$m=rand()))*($m&1?:$w))%$w%($w-1)<1|$q/$w%$h<1;);}$r=str_pad("",($w=rand(10,39))*$h=rand(10,39),"#");$r[$w*2-2]=F;w($p=$q=$w*(--$h-1)+1);$r[$p]=P;for($c=rand(2,4);$i<$c+rand(1,3);$p=rand($w,$h*$w))if($r[$p]<A&&$p%$w%($w-1)){w($p);$r[$p]=EC[$i++<$c];w($p);}echo chunk_split($r,$w);

对没有换行符的字符串进行操作;挖掘演员之间的随机路径。用运行-r

注意:路径是通过沿随机方向行走创建的。每个步骤的方向选择通常都会产生张开的地图。并且该示例图极不可能出现;但是可能

分解

// aux function: randomly walk away from $p placing spaces, stop when a special is reached
function w($p)
{global$r,$h,$w;
    for($q=$p;
        $r[$q]<A;                               // while $q is not special
    )
        for($r[$p=$q]=" ";                          // 3. replace with space
            ($q=$p+(1-(2&$m=rand()))*($m&1?:$w))    // 1. pick random $q next to $p
            %$w%($w-1)<1|$q/$w%$h<1;                // 2. that is not on the borders
        );
}

// initialize map
$r=str_pad("",
    ($w=rand(10,39))*$h=rand(10,39) // random width and height
    ,"#");                          // fill with "#"
$r[$w*2-2]=F;                       // place Finish
w($p=$q=$w*(--$h-1)+1);             // build path from Player position to F
// $h is now height -1 !
$r[$p]=P;                           // place Player

// place Coins ans Enemies
for($c=rand(2,4);$i<$c+rand(1,3);   // while $i has not reached no. of coins+no. of enemies
    $p=rand($w,$h*$w))              // pick a random position
    if($r[$p]<A&&$p%$w%($w-1))      // that is neither special nor out of bounds
    {
        w($p);                      // build path from there to another special
        $r[$p]=EC[$i++<$c];         // place this special
        w($p);      // additional path to allow special in the middle of a dead end tunnel
    }

// insert linebreaks and print
echo chunk_split($r,$w);

在你的解释,你要生成高度和宽度37,而不是39
帕维尔·

@Pavel修复; 感谢您的注意
泰特斯

当我尝试在线尝试
Pavel

@Pavel,您需要在代码周围加上<?php .... ?>
Dada,

1
好的,我这样做了,并且我注意到墙壁是按规则的矩形块生成的。它应该能够生成类似示例地图的内容。它也不总是生成E
帕维尔

3

C#(Visual C#交互式编译器),730字节

var R=new Random();for(;;){char P='P',C='C',E='E',Q='#';int w=R.Next(8,37),h=R.Next(8,37),H=h,t,g=99,W,i,j,r;string l,s,p=new string(Q,w+2);var m=new List<string>();for(;H>0;H--){l="";for(W=w;W>0;W--){r=R.Next(999);l+=r<3?C:r<6?E:r<g?Q:' ';}m.Add(l);}m[0]=m[0].Substring(0,w-1)+'F';m[h-1]=P+m[h-1].Substring(1);s=String.Join("#\n#",m);t=s.Split(E).Length-1;if(t<1||t>3)continue;t=s.Split(C).Length-1;if(t<2||t>4)continue;while(g>0){g--;for(i=0;i<h;i++)for(j=0;j<w;j++)if(m[i][j]!=Q&&m[i][j]!=P&&(i>0&&m[i-1][j]==P)||(i<h-1&&m[i+1][j]==P)||(j>0&&m[i][j-1]==P)||(j<w-1&&m[i][j+1]==P))m[i]=m[i].Substring(0,j)+P+m[i].Substring(j+1,w-j-1);}if(String.Join("",m).Split(E,C,'F').Length>1)continue;Console.Write(p+"\n#"+s+"#\n"+p);break;}

在线尝试!

取消高尔夫:

var R = new Random();
for (;;)
{
    char P = 'P', C = 'C', E = 'E', poundSymbol = '#';
    int width = R.Next(8, 37), height = R.Next(8, 37), HeightTemp = height, testVariable, goThroughLoop = 99, WidthTemp, i, j, rand;
    string line, strMap, poundSymbolPadding = new string(poundSymbol, width + 2);

    var map = new List<string>(); //initialize map
    for (; HeightTemp > 0; HeightTemp--)
    {
        line = "";
        for (WidthTemp = width; WidthTemp > 0; WidthTemp--)
        {
            rand = R.Next(999);
            //add a character randomly.  Re-use the goThroughLoop variable here, which gives approx. 1 wall per 10 spaces.
            line += rand < 3 ? C : rand < 6 ? E : rand < goThroughLoop ? poundSymbol : ' ';
        }
        map.Add(line);
    }
    //add finish and player
    map[0] = map[0].Substring(0, width - 1) + 'F';
    map[height - 1] = P + map[height - 1].Substring(1);

    strMap = String.Join("#\n#", map);
    //check proper # of enemies, regenerate if invalid
    testVariable = strMap.Split(E).Length - 1;
    if (testVariable < 1 || testVariable > 3)
        continue;
    //check proper # of coins, regenerate if invalid
    testVariable = strMap.Split(C).Length - 1;
    if (testVariable < 2 || testVariable > 4)
        continue;
    //map out areas Player can access.  Iterates until all accessible places have been marked as such.
    while (goThroughLoop > 0)
    {
        goThroughLoop--;
        for (i = 0; i < height; i++)
            for (j = 0; j < width; j++)
                if (map[i][j] != poundSymbol && map[i][j] != P && ((i > 0 && map[i - 1][j] == P) || (i < height - 1 && map[i + 1][j] == P) || (j > 0 && map[i][j - 1] == P) || (j < width - 1 && map[i][j + 1] == P)))
                    //mark this space as accessible
                    map[i] = map[i].Substring(0, j) + P + map[i].Substring(j + 1, width - j - 1);
    }
    //if player cannot access all features (defeated enmies, collected coins, arrived at finish), regenerate map.
    if (String.Join("", map).Split(E, C, 'F').Length > 1)
        continue;

    //output our final map
    Console.Write(poundSymbolPadding + "\n#" + strMap + "#\n" + poundSymbolPadding);

    break;
}

编辑:保存了8个字节,通过将播放器可访问的测试循环锁定为99次迭代,使其效率略有降低。我知道它永远不会与这里的其他答案竞争,但我很开心!


@GregMartin现在轮到您在F#中实现它;-)
Bence Joful

2
对主要部分的简单调制,没问题:)
Greg Martin
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.