Tạo bản đồ cho một roguelike


10

Hôm nay, chúng tôi sẽ tạo ra một bản đồ cho một game nhập vai roguelike!

Bản đồ ví dụ:

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

#là những bức tường, Plà vị trí bắt đầu của người chơi, Flà kết thúc phải đạt được, Clà những đồng tiền có thể được thu thập và Elà kẻ thù có thể chiến đấu.

Thông số kỹ thuật bản đồ:

  • Cả chiều cao và chiều rộng nên nằm trong khoảng từ 10 đến 39. Chiều cao không phải bằng chiều rộng.
  • Các đường viền bản đồ nên được lấp đầy bằng các bức tường.
  • P nên được đặt ở góc dưới bên trái.
  • F nên được đặt ở góc trên cùng bên phải.
  • Nên có từ 1 đến 3 kẻ thù.
  • Nên có từ 2 đến 4 đồng tiền.
  • Nên có một số lượng tường ở giữa. Nên có một con đường để đi từ Pđể Mỗi C, E, và F, hãy nhớ rằng các cầu thủ không thể di chuyển theo đường chéo.
  • Mỗi sự kết hợp có thể nên có một số cơ hội xảy ra.

Quy tắc

  • Ít chương trình byte chiến thắng.
  • Chương trình của bạn không nên có bất kỳ đầu vào.
  • Chương trình của bạn có thể không thoát với một lỗi (đầu ra không gây tử vong là STDERRđược, nhưng chúng ta không thể gặp sự cố giống như lừa đảo sau khi tạo bản đồ!)
  • Một dòng mới duy nhất được cho phép và không gian dấu được cho phép.
  • Không có đầu ra khác được cho phép.

3
Đó là roguelike, chỉ là fyi.
Rɪᴋᴇʀ

2
Bạn có thể làm rõ "mọi sự kết hợp có thể nên có cơ hội xảy ra như nhau" không? Bạn có nghĩa đen là tất cả các bản đồ hợp lệ (cụ thể là tất cả các bản đồ mà P có thể đạt tới tất cả các C / E / F) phải xảy ra với xác suất bằng nhau? Nếu vậy, có vẻ như thuật toán duy nhất có thể là tạo các bản đồ một cách ngẫu nhiên và sau đó kiểm tra xem P có thể đạt được mọi thứ hay không, loại bỏ các bản đồ không hợp lệ cho đến khi điều đó xảy ra.
Greg Martin

Bạn cũng có thể làm rõ - "Nên có một số lượng tường ở giữa", nếu tôi chỉ đặt 2 bức tường mọi lúc thì sao?
Gurupad Mamadapur

1
@GregMartin Tôi cũng sẽ thay đổi "Mọi bố cục có thể sẽ có cơ hội xảy ra", không nhất thiết phải là một cơ hội như nhau.
Pavel

2
Những gì về hình vuông trống không thể truy cập được bao quanh bởi các bức tường? Đây có phải là một bố cục hợp lệ hay chúng nên được tránh hoàn toàn? (Nói cách khác: mỗi ô vuông trống có thể truy cập được không?)
Arnauld

Câu trả lời:


5

Perl, 293 byte

-9 byte nhờ @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

Thêm -Ecờ để chạy nó:

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'

Tuy nhiên, phải mất một thời gian dài để chạy, vì vậy tôi khuyên bạn nên sử dụng phiên bản này thay thế:

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'

Hãy thử trực tuyến!

Giải trình

{ # nhập một khối (được sử dụng như một vòng lặp) { $ == 7 + rand 30 ; # chọn ngẫu nhiên độ rộng của bản đồ -2 # (-2 vì chúng tôi chưa bao gồm các đường viền) @r = $ "= (); # reset @r và đặt $" thành undef @a = ( # tạo một danh sách các nhân vật có thể ở trên bảng ( C ) x4 , # 4 xu 'C' ( E ) x3 , # 3 kẻ thù 'E' ( "#" ) x1369 , # 37 * 37 '#' (                     
                       
                                     
    
                                 
                               
                               
                          
     "" ) x1369 ); # 37 * 37 khoảng trắng cho $ i ( 0..7 + rand 30 ) # tạo bản đồ 2D (7 + rand 30 là chiều cao, được tạo ngay bây giờ) với $ _ ( 0 .. $ = - 1 ) { 
        $ r [ $ i ] [ $ _ ] = # index [$ i] [$ _] nhận ... 
           splice @ a , rand @ a , 1 # .. một ký tự ngẫu nhiên từ danh sách được tạo trước đó # (ký tự là sau đó xóa khỏi danh sách nhờ vào 'splice') } } 
    $ r [                    
                     
                                     
                                       
      
    0 ] [ $ =] = F ; # thêm ô kết thúc 
    $ r [- 1 ] [ 0 ] = P ; # thêm ô bắt đầu 
    $ _ = $ r = # ở đây chúng tôi tạo một chuỗi đại diện cho bản đồ 
          tham gia $ /, # tham gia các phần tử sau với dòng mới 
            $ v = "#" x ( $ = + = 3 ), # a trước dòng # only ( bản đồ "# @ $ _ #" , @r ), # thêm # vào đầu và cuối của mỗi dòng                                                                                                             
                       
            $ v ; # dòng cuối cùng của #                        

    1 trong khi # regex sau sẽ thay thế mọi ô có thể truy cập bằng F 
       $ r = ~ s / F (. { $ =})? [^ # F ] / F $ 1F / s   # một ô ở bên phải hoặc dưới cùng của một Ô F được thay thế   | | # hoặc 
       $ r = ~ s / [^ # F ] (. { $ =})? F / F $ 1F / s ; # một ô ở bên trái hoặc trên cùng của một ô F được thay thế 
    $ r ! ~ / [CEP] / # nếu không có C, E hoặc P trên bản đồ (có nghĩa là tất cả chúng đều có thể truy cập được) &&                
                                            
      /C.*C/ s          # và có ít nhất 2 xu && / E / ? # và 1 kẻ thù cuối cùng : # bản đồ hợp lệ, chúng tôi thoát khỏi vòng lặp làm lại # khác, bắt đầu lại } 
nói                      # và in bảng
                  
                   
                    

Phải mất một thời gian dài để chạy, bởi vì danh sách mà chúng tôi chọn ngẫu nhiên các ký tự để đặt lên bảng ( @a) chứa 1369 khoảng trắng #và chỉ có 4 đồng xu và 3 kẻ thù. Vì vậy, nếu kích thước của chiều rộng và chiều cao nhỏ, có rất nhiều khoảng trống và #so với đồng xu và kẻ thù, thì rất có khả năng một bản đồ ngẫu nhiên sẽ không hợp lệ. Đó là lý do tại sao phiên bản "tối ưu hóa" là nhanh hơn: danh sách từ đó chúng ta chọn các nhân vật chỉ là lớn hơn một chút so với bản đồ (danh sách là @a=((C)x4,(E)x3,("#")x($v=rand $=*$%),($")x($=*$%-$v)): một số ngẫu nhiên $vcủa #(kém hơn so với kích thước của bản đồ), và size of the map - $vkhoảng trắng).


Tôi thực sự không biết perl, nhưng nhìn vào cú pháp tô sáng bạn dường như có một trích dẫn chưa từng có trong ($ ") x $ = ** 2); Có thể tính năng tô sáng không hoạt động đúng và đó là một tính năng. , khoảng trắng có thể không truy cập được.
Pavel

1
@Pavel $"là một biến Perl hợp pháp, nhưng cú pháp tô sáng không biết về nó, đó là lý do tại sao nó trông như thế. Ok, tôi sẽ xóa bình luận về không gian không thể truy cập.
Dada

5

PHP, 422 417 415 309 373 369 364 361 byte

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

hoạt động trên một chuỗi không có ngắt dòng; đào đường dẫn ngẫu nhiên giữa các tính năng bổ sung. Chạy với -r.

Lưu ý: Các đường dẫn được tạo bằng cách đi bộ theo các hướng ngẫu nhiên. Sự lựa chọn hướng cho mỗi bước sẽ chủ yếu tạo ra các bản đồ rộng mở; và bản đồ ví dụ rất khó xuất hiện; nhưng nó có thể.

phá vỡ

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

Theo giải thích của bạn, bạn đang tạo chiều cao và chiều rộng tới 37, chứ không phải 39.
Pavel

@Pavel cố định; cảm ơn vì đã chú ý
Titus

Xuất ra mã nguồn của riêng nó khi tôi thử trên Dùng thử trực tuyến
Pavel

@Pavel bạn cần bao quanh mã với<?php .... ?>
Dada

1
Ok, tôi đã làm điều đó, và tôi nhận thấy rằng các bức tường được tạo ra trong các khối hình chữ nhật thông thường. Nó sẽ có thể tạo ra một cái gì đó giống như bản đồ ví dụ. Nó cũng không luôn luôn tạo ra Es.
Pavel

3

C # (Trình biên dịch tương tác Visual C #) , 730 byte

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

Hãy thử trực tuyến!

Ung dung:

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

Chỉnh sửa: đã lưu 8 byte, làm cho nó kém hiệu quả hơn một chút bằng cách khóa vòng kiểm tra có thể truy cập của người chơi thành 99 lần lặp. Tôi biết nó sẽ không bao giờ thực sự cạnh tranh với các câu trả lời khác ở đây, nhưng tôi rất vui!


@GregMartin Bây giờ đến lượt bạn triển khai nó trong F # ;-)
Bence Joful

2
Một điều chế đơn giản cho subdominant, không có vấn đề gì :)
Greg Martin
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.