로그 라이크에 대한 맵 생성


10

오늘, 우리는 악성 RPG에 대한 맵을 생성 할 것입니다!

지도 예 :

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

#벽, P플레이어의 시작 위치, F도달해야하는 마무리, C수집 할 수있는 동전, E싸울 수있는 적입니다.

지도 사양 :

  • 높이와 너비는 모두 10에서 39 사이 여야합니다. 높이는 너비와 같을 필요는 없습니다.
  • 지도 경계는 벽으로 채워 져야합니다.
  • P 왼쪽 하단에 배치해야합니다.
  • F 오른쪽 상단에 배치해야합니다.
  • 적 1 ~ 3 명이 있어야합니다.
  • 동전은 2 개에서 4 개 사이 여야합니다.
  • 중간에 약간의 벽이 있어야합니다. 에서 얻을 수있는 경로가 있어야 P마다에 C, E그리고 F, 마음에 유지하는 플레이어는 대각선으로 이동할 수있다.
  • 가능한 모든 조합이 발생할 가능성이 있어야합니다.

규칙

  • 가장 적은 바이트 프로그램이 승리합니다.
  • 당신의 프로그램은 어떤 입력도받지 않아야합니다.
  • 프로그램이 오류와 함께 종료되지 않을 수 있습니다 (치명적이지 않은 출력 STDERR은 괜찮지 만 맵 생성 후 불량과 같은 충돌을 일으킬 수 없습니다!)
  • 단일 후행 줄 바꿈이 허용되고 후행 공백이 허용됩니다.
  • 다른 출력은 허용되지 않습니다.

3
도적과 비슷합니다.
Rɪᴋᴇʀ

2
"가능한 모든 조합이 균등 한 기회를 가져야 함"을 명확히 할 수 있습니까? 문자 그대로 모든 유효한 맵 (특히 P가 모든 C / E / F에 도달 할 수있는 모든 맵)이 동일한 확률로 발생해야한다는 의미입니까? 그렇다면 가능한 유일한 알고리즘은 무작위로 맵을 균일하게 생성 한 다음 P가 모든 것에 도달 할 수 있는지 확인하여 그럴 때까지 유효하지 않은 맵을 버리는 것입니다.
Greg Martin

당신은 또한 "중간에 약간의 벽이 있어야한다"고 명확히 할 수 있습니까, 만약 내가 항상 2 개의 벽을 놓으면 어떻게 될까요?
Gurupad Mamadapur 2014

1
@GregMartin 필자는 반드시 같은 기회 일 필요는 없지만 "가능한 모든 레이아웃이 발생할 가능성이 있어야한다"고 바꾸겠다.
Pavel

2
벽으로 둘러싸인 도달 할 수없는 빈 사각형은 어떻습니까? 유효한 레이아웃입니까 아니면 완전히 피해야합니까? (즉, 각 빈 사각형에 도달 할 수 있어야합니까?)
Arnauld

답변:


5

펄, 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 + "" ) x1369 ); 0 ] [ $ =] = F ;                     
     rand 30 ; # 무작위로 맵의 폭 -2 선택 #을 (-2 우리가 아직 국경을 포함하지 않기 때문에) @r = $ "= (); # 리셋 @r 및 세트 $를" 미확정 @a = ( # 생성 보드 ( C ) x4 , # 4 동전 'C' ( E ) x3 , # 3 적 'E' ( "#" ) x1369 , # 37 * 37 '#' (                   
                                     
    
                                 
                               
                               
                          
                         # 37 * 37 스페이스 에 대한 $ I ( 0..7 + 랜드 30 ) # 2 차원지도를 작성 (7 랜드 (30)가 이제 막 생성 된 높이이다 +) 에 대한 $ _ ( 0 . $ = - 1 ) { 
        $ r [ $ i ] [ $ _ ] = # index [$ i] [$ _] 수신 ... 
           splice @ a , rand @ a , 1 # .. 이전에 생성 된 목록에서 임의의 문자 # (문자는 'splice'덕분에 목록에서 제거되었습니다.) } } 
    $ r [
                     
                                     
                                       
      
                           # 마무리 셀 추가 
    $ r [ -1 ] [ 0 ] = P ; # 시작 셀을 추가하십시오 
    $ _ = $ r = # 여기에서 map 
          join $ / 의 문자열 표현을 생성합니다 . # 줄 바꿈으로 다음 요소를 조인합니다. 
            $ v = "#" x ( $ = + = 3 ) # 첫 번째 # 줄만 ( map "# @ $ _ #" , @r ), # 각 줄의 시작과 끝에 # 추가                                                                                      
                       
            $ v ; #의 마지막 줄 #                        

    # 다음 정규식은 모든 액세스 가능한 셀을 F 
       $ r = ~로 바꿉니다 .                 S / F (. { $ =})? [^ # F ] / F $ 1 층 / S   # 오른쪽에 셀 또는 하단 F 셀이 교체되었습니다   || # 또는 
       $ r = ~ s / [^ # F ] (. { $ =})? F / F $ 1F / s ; # F 셀의 왼쪽 또는 맨 위에있는 셀은 
    $ r ! ~ / [CEP] / 로 대체됩니다 . # 맵에 C, E 또는 P가 없으면 (모두 액세스 가능함 ) &&
                                            
      /C.*C/ s          # 그리고 최소 2 개의 동전이 있습니다 && / E / ? # and 1 enemy last : # 맵이 유효하고, 루프 리두를 종료하고 # 다시 시작하고 } 
말하고                      # 보드를 인쇄하십시오.
                  
                   
                    

보드에 넣을 캐릭터를 무작위로 고르는 목록 ( @a)에는 1369 개의 ​​공백과 #, 동전 4 개와 적 3 개만 들어 있기 때문에 실행하는 데 시간이 오래 걸립니다 . 따라서 너비와 높이의 크기가 작 으면 공간이 많고 #동전과 적과 비교되므로 임의의지도가 유효하지 않을 가능성이 큽니다. 은 "최적화 된"버전이 빠른 이유입니다 : 우리가 문자를 선택할 수있는 목록이지도보다는 조금 더 큰 (목록입니다 @a=((C)x4,(E)x3,("#")x($v=rand $=*$%),($")x($=*$%-$v)): 난수 $v#지도의 크기 열등 () 및 size of the map - $v공백).


나는 펄을 잘 모르지만 구문 강조를 보면 ($ ") x $ = ** 2);에서 타의 추종을 불허하는 것처럼 보입니다. 아마도 강조가 제대로 작동하지 않아 기능입니다. 공백은 도달 할 수 없습니다.
Pavel

1
@Pavel $"은 합법적 인 Perl 변수이지만 구문 강조는 알지 못하므로 그렇게 보입니다. 도달 할 수없는 공간에 대한 설명을 삭제하겠습니다.
Dada

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);

당신의 설명에서 당신은 39가 아니라 37까지 높이와 너비를 생성하고 있습니다.
Pavel

@Pavel 고정; 알아 주셔서 감사합니다
Titus


@Pavel 당신은 코드를 둘러싸 야합니다<?php .... ?>
Dada

1
좋아, 나는 그것을했고, 벽은 규칙적인 직사각형 덩어리로 생성되는 것을 알았습니다. 예제 맵과 같은 것을 생성 할 수 있어야합니다. 또한 항상 Es를 생성하지는 않습니다 .
Pavel

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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.