ローグライクのマップを生成する


10

今日は、ローグライクRPGのマップを生成します!

サンプルマップ:

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

#壁でPあり、プレイヤーの開始位置でFあり、到達しなければならないフィニッシュであり、C収集できるコインでありE、戦うことができる敵です。

地図の仕様:

  • 高さと幅は両方とも10から39の間でなければなりません。高さは幅と同じである必要はありません。
  • マップの境界は壁で埋める必要があります。
  • P 左下隅に配置する必要があります。
  • F 右上隅に配置する必要があります。
  • 1〜3人の敵がいるはずです。
  • 2〜4枚のコインがあるはずです。
  • 中央にある程度の壁があるはずです。そこから取得するためのパスでなければなりませんPすべてにCEF、プレイヤーが斜めに動くことができないということを念頭に置いて。
  • 可能なすべての組み合わせには、発生する可能性があります。

ルール

  • 最少バイトのプログラムが勝ちます。
  • プログラムは何も入力しないでください。
  • プログラムはエラーで終了しない可能性があります(致命的でない出力は問題STDERRありませんが、マップ生成後に不正なクラッシュを起こすことはできません!)
  • 単一の末尾の改行が許可され、末尾のスペースが許可されます。
  • 他の出力は許可されません。

3
それはローグライク、ただのfyiです。
Rɪᴋᴇʀ

2
「考えられるすべての組み合わせで、発生する機会が等しくなるはずです」を明確にできますか?文字通り、すべての有効なマップ(特に、PがすべてのC / E / Fに到達できるすべてのマップ)が同じ確率で発生する必要があるということですか?もしそうなら、可能な唯一のアルゴリズムはランダムにマップを一様に生成し、Pがすべてに到達できることを確認し、それが起こるまで無効なマップを破棄することであるようです。
グレッグマーティン

また、「真ん中にある程度の壁があるはずです」、常に2つの壁しか配置しない場合はどうでしょうか。
グルパッドママダプール16

1
@GregMartin私もそれを「すべての可能なレイアウトが発生する可能性があるはず」に変更しますが、必ずしも均等な機会ではありません。
パベル

2
壁に囲まれた到達不可能な空の正方形はどうですか?それは有効なレイアウトですか、それとも完全に避けるべきですか?(換言すれば、各空の正方形は到達可能であるべきですか?)
アーナルド

回答:


5

Perl、293バイト

@Dom Hastingsのおかげで-9バイト

{$==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をリセットし、$" undef @a =(#createに設定ボード上にいることができるキャラクターのリストC x4 #4コイン 'C' E x3 #3敵 'E' "#" x1369 #37 * 37 '#'                      
                       
                                     
    
                                 
                               
                               
                          
     "" x1369 ); #37 * 37スペースのために$ I 0..7 +ランド30 #2次元マップを作成する(7ランド30が今生成された高さ、である+)のための$ _ 0 $ = - 1 ){ 
        $ r [ $ i ] [ $ _ ] = #index [$ i] [$ _]が受け取る... 
           splice @ a rand @ a 1 #..以前に生成されたリストからランダムな文字#(文字は「スプライス」のおかげでリストから削除されました)} } 
    $ r [                    
                     
                                     
                                       
      
    0 ] [ $ =] = F ; #終了セルを追加
    $ r [ -1 ] [ 0 ] = P ; #開始セルを追加
    $ _ = $ r = #ここでマップの文字列表現を生成
          join $ /、#次の要素を改行で結合
            $ v = "#" x $ = + = 3 )、#最初#のみの行map "#@ $ _#" @ r )、各行の先頭と末尾に#を追加                                                                                                             
                       
            $ v ; ##の最後の行                        

    1while #次の正規表現は、Fですべてのアクセス可能なセルを置換する
       $ R =〜S / F (。{ $ [^#?=})F ] / F $ 1F / sの   右またはAの底部に#セルFセルが置き換えられます   || #または
       $ R =〜S / [^#F ]({ $ =})?F / F $ 1F / s ; #左側のセルまたはFセルの上部が置き換えられます
    $ r !〜/ [CEP] / #マップ上にC、EまたはPがない場合(すべてアクセス可能であることを意味します)&&                
                                            
      /C.*C/ s          #および少なくとも2つのコインがあります&& / E / 最後に敵1人#マップは有効です。ループを終了し、やり直し#その他、やり直し} 
と言い                      、ボードを印刷します
                  
                   
                    

ボードにランダムに文字を選択するリスト(@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変数ですが、構文の強調表示はそれを認識しません。そのため、そのように見えます。OK、到達不能なスペースに関するコメントを削除します。
ダダ

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<?php .... ?>
ダダ

1
わかりました、私はそれをしました、そして、私は壁が規則的な長方形の塊で生成されることに気づきました。サンプルマップのようなものを生成できるはずです。また、常にEs を生成するとは限りません。
パベル

3

C#(Visual C#Interactive Compiler)、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#の;-)でそれを実装するためにあなたの番です
ベンスJoful

2
サブドミナントへの単純な変調、問題なし:)
グレッグマーティン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.