สร้างแผนที่สำหรับโร๊คไลค์


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
มันเป็นโร๊คไลค์เพียงแค่ fyi
Rɪᴋᴇʀ

2
คุณสามารถอธิบาย "ชุดค่าผสมที่เป็นไปได้ทุกชุดควรมีโอกาสเกิดขึ้นเท่ากัน"? คุณหมายถึงว่าแผนที่ที่ถูกต้องทั้งหมด (โดยเฉพาะแผนที่ทั้งหมดที่ P สามารถเข้าถึง C / E / Fs ทั้งหมด) ต้องเกิดขึ้นด้วยความน่าจะเป็นที่เท่ากันหรือไม่? ถ้าเป็นเช่นนั้นดูเหมือนว่าอัลกอริทึมเดียวที่เป็นไปได้คือการสร้างแผนที่แบบสุ่มและตรวจสอบว่า P สามารถเข้าถึงทุกสิ่งโดยยกเลิกแผนที่ที่ไม่ถูกต้องจนกว่าจะเกิดขึ้น
Greg Martin

คุณช่วยอธิบายได้ไหม - "ควรมีกำแพงอยู่ตรงกลาง" ถ้าฉันวางแค่ 2 กำแพงตลอดเวลา
Gurupad Mamadapur

1
@GregMartin ฉันจะเปลี่ยนเหมือนกัน "ทุกเลย์เอาท์ที่เป็นไปได้ควรมีโอกาสเกิดขึ้น" ไม่จำเป็นต้องมีโอกาสเท่ากัน
Pavel

2
สิ่งที่เกี่ยวกับช่องว่างเปล่าที่ไม่สามารถเข้าถึงได้ล้อมรอบด้วยกำแพง มันเป็นรูปแบบที่ถูกต้องหรือควรหลีกเลี่ยงทั้งหมด? (ในคำอื่น ๆ : แต่ละตารางว่างสามารถเข้าถึงได้หรือไม่)
Arnauld

คำตอบ:


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'

ลองออนไลน์!

คำอธิบาย

{# ใส่บล็อก (ซึ่งใช้เป็นวง)                     # enter บล็อก (ซึ่งใช้เป็นลูป)
    {$ == 7 + แรนด์ 30; # สุ่มเลือกความกว้างของแผนที่ -2{ $ == 7 + แรนด์30 ; # สุ่มเลือกความกว้างของแผนที่ -2                   
                                     # (-2 เพราะเรายังไม่รวมเขตแดน)# (-2 เพราะเรายังไม่รวมเขตแดน)
    @r = $ "= (); # reset @r และตั้ง $" เป็น undef@r = $ "= (); # reset @r และตั้ง $" เป็นundef
    @ a = (# สร้างรายการของตัวละครที่สามารถอยู่บนกระดาน@a = ( # สร้างรายการของตัวละครที่สามารถอยู่บนกระดานได้                             
     (C) x4, # 4 เหรียญ 'C'( C ) x4 , # 4 เหรียญ 'C'                          
     (E) x3, # 3 ศัตรู 'E'( E ) x3 , # 3 ศัตรู 'E'                          
     ("#") x1369, # 37 * 37 '#'( "#" ) x1369 , # 37 * 37 '#'                     
     ("") x1369); # 37 * 37 ช่องว่าง( "" ) x1369 ); # 37 * 37 ช่องว่าง                    
    สำหรับ $ i (0..7 + แรนด์ 30) # สร้างแผนที่ 2D (7 + แรนด์ 30 คือความสูงซึ่งเพิ่งสร้างขึ้นในขณะนี้)สำหรับ$ i ( 0..7 + แรนด์30 ) # สร้างแผนที่ 2D (7 + แรนด์ 30 คือความสูงซึ่งเพิ่งสร้างขึ้นในขณะนี้)                 
      ราคา $ _ (0 .. $ = - 1) {สำหรับ$ _ ( 0. . $ = - 1 ) {
        $ r [$ i] [$ _] = # index [$ i] [$ _] ได้รับ ...[ $ i ] [ $ _ ] = # index [$ i] [$ _] ได้รับ ...                    
           splice @ a, rand @ a, 1 # .. อักขระแบบสุ่มจากรายการที่สร้างไว้ก่อนหน้านี้, rand @ a , 1 # .. อักขระแบบสุ่มจากรายการที่สร้างไว้ก่อนหน้านี้           
                                       # (ตัวละครจะถูกลบออกจากรายการด้วย 'splice')# (ตัวละครจะถูกลบออกจากรายการด้วย 'splice')
      }}
    }}
    $ R [0] [$ =] = F; # เพิ่มเซลล์ที่เสร็จสิ้น[ 0 ] [ $ =] = F ; # เพิ่มเซลล์ที่เสร็จสิ้น                       
    $ R [-1] [0] = p; # เพิ่มเซลล์เริ่มต้น[- 1 ] [ 0 ] = P ; # เพิ่มเซลล์เริ่มต้น                       
    $ _ = $ r = # ที่นี่เราสร้างการแสดงสตริงของแผนที่= $ r = # ที่นี่เราสร้างการแสดงสตริงของแผนที่                             
          เข้าร่วม $ /, # เข้าร่วมองค์ประกอบต่อไปนี้ด้วยการขึ้นบรรทัดใหม่/, # เข้าร่วมองค์ประกอบต่อไปนี้ด้วยการขึ้นบรรทัดใหม่                      
            $ v = "#" x ($ = + = 3), # บรรทัดแรกของ # เท่านั้น= "#" x ( $ = + = 3 ), # บรรทัดแรกของ # เท่านั้น            
            (แผนที่ "# @ $ _ #", @ r), # เพิ่ม # ไปยังจุดเริ่มต้นและจุดสิ้นสุดของแต่ละบรรทัด( แผนที่"# @ $ _ #" , @r ), # เพิ่ม # ไปยังจุดเริ่มต้นและจุดสิ้นสุดของแต่ละบรรทัด           
            $ V; # บรรทัดสุดท้ายของ #; # บรรทัดสุดท้ายของ #                        

    1 ในขณะที่ # regex ต่อไปนี้จะแทนที่เซลล์ที่เข้าถึงได้ด้วย F1 ในขณะที่# regex ต่อไปนี้จะแทนที่เซลล์ที่เข้าถึงได้ด้วย F                
       $ r = ~ s / F (. {$ =})? [^ # F] / F $ 1F / s # เซลล์ทางด้านขวาหรือด้านล่างของเซลล์ F ถูกแทนที่  = ~ s / F (. { $ =})? [^ # F ] / F $ 1F / s   # เซลล์ทางด้านขวาหรือด้านล่างของเซลล์ F จะถูกแทนที่  
         || # หรือ|| # หรือ                         
       $ r = ~ s / [^ # F] F / f $ 1F / s ({$ =}.); # เซลล์ทางด้านซ้ายหรือด้านบนของเซลล์ F ถูกแทนที่= ~ s / [^ # F ] (. { $ =})? F / F $ 1F / s ; # เซลล์ทางด้านซ้ายหรือด้านบนของเซลล์ F ถูกแทนที่ 
    $ r! ~ / [CEP] / # ถ้าไม่มี C, E หรือ P บนแผนที่ (หมายถึงพวกเขาเข้าถึงได้ทั้งหมด)! ~ / [CEP] / # หากไม่มี C, E หรือ P บนแผนที่ (หมายถึงพวกเขาเข้าถึงได้ทั้งหมด)         
     && /C.*C/s # และมีอย่างน้อย 2 เหรียญ&& /C.*C/ s          # และมีอย่างน้อย 2 เหรียญ 
     && / E /? # และ 1 ศัตรู&& / E / ? # และ 1 ศัตรู             
      สุดท้าย: # แผนที่นั้นถูกต้องเราออกจากลูปสุดท้าย: # แผนที่นั้นถูกต้องเราออกจากลูป             
      ทำซ้ำ # else เริ่มต้นใหม่ทำซ้ำ# else เริ่มต้นใหม่              
}}
พูด # และพิมพ์กระดาน# และพิมพ์บอร์ด

ใช้เวลานานในการรันเนื่องจากรายการที่เราสุ่มเลือกตัวละครที่จะนำไปวางบนกระดาน ( @a) มีช่องว่าง 1,369 ช่อง#และมีเพียง 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 ที่ถูกต้อง แต่การเน้นไวยากรณ์ไม่ทราบเกี่ยวกับมันนั่นเป็นเหตุผลว่าทำไมมันถึงเป็นแบบนั้น ตกลงฉันจะลบความคิดเห็นเกี่ยวกับช่องว่างที่ไม่สามารถเข้าถึงได้
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);

ในคำอธิบายของคุณคุณกำลังสร้างความสูงและความกว้างเป็น 37 ไม่ใช่ 39
Pavel

@Pavel คงที่; ขอบคุณที่สังเกต
ติตัส

ให้ผลลัพธ์เป็นซอร์สโค้ดของตัวเองเมื่อฉันลองใช้ลองออนไลน์
Pavel

@Pavel คุณต้องล้อมรหัสด้วย<?php .... ?>
Dada

1
ตกลงฉันทำอย่างนั้นและฉันสังเกตเห็นว่ากำแพงนั้นถูกสร้างขึ้นเป็นก้อนสี่เหลี่ยมปกติ มันควรจะสามารถสร้างบางอย่างเช่นแผนที่ตัวอย่าง นอกจากนี้ยังไม่สร้างรายได้เสมอEไป
Pavel

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

ลองออนไลน์!

Ungolfed:

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
การปรับง่ายต่อการภายไม่มีปัญหา :)
เกร็กมาร์ติน
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.