Beefy Boxes and Bandwidth Generously Provided by pair Networks
good chemistry is complicated,
and a little bit messy -LW
 
PerlMonks  

Writing a random dungeon generator in perl.

by Trag (Monk)
on Aug 15, 2005 at 21:41 UTC ( [id://484003]=perlquestion: print w/replies, xml ) Need Help??

Trag has asked for the wisdom of the Perl Monks concerning the following question:

I'm trying to write a roguelike in perl, but I'm having a bit of trouble with the random dungeon generator (which is all I have done so far). I've written the following code. As a test, it's supposed to output a maze, with the walls represented as #, and the floors denoted by periods. The maze is stored in the @dungeon array, and is 80 x 80 tiles. Anything directly above any particular tile can be found by looking 80 tiles either ahead or behind in the array. Anything to the left or right can be found by looking 1 ahead or behind. When I run it at the moment, I get the following error constantly repeated: "Argument "#" isn't numeric in addition (+) at C:\mygame\mapgenerator.pl line 22." That's strange, because no addition is taking place on line 22 (if  ($start < 0 || $start > 6400) {). I'd appreciate help with that error, but the real reason I'm posting this is because I'm sure there's a better way to implement the thing, and I'd appreciate some tips. Thanks!
#!/usr/bin/perl use warnings; use strict; #package mapgenerator; my @dungeon = ('#') x 6400; sub generatemap { my $count; my $choice; my $increment = 0; my $notdoneyet = 1; my $start = int (rand(6399)); #chooses a random point to st +art building the maze my $specialfloor = shift; $dungeon[$start] = '.'; while($notdoneyet) { $choice = (-1, +1, -80, +80)[rand 4]; $start = $start + $choice; if ($start < 0 || $start > 6400) { redo; } elsif ($dungeon[$start] eq '#' && $dungeon[$start] + 1 eq '#' && $dungeon[$start] - 1 eq '#' && $dungeon[$start] + 80 eq '#' + && $dungeon[$start] - 80 eq '#'){ $dungeon[$start] = '.'; } else { $increment++; if ($increment == 10) { $start = int (rand(6399)); $increment = 0; } } $count = grep m/./, @dungeon; if ($count == 3200) { $notdoneyet = 0; } } } sub printmap { my $first = 0; my $second = 80; my $totalprinted = 0; while ($totalprinted < 6400) { print $dungeon[$first..$second]; print "\n"; $first = $first + 80; $second = $second + 80; $totalprinted = $totalprinted + 80; } } generatemap; printmap;

Replies are listed 'Best First'.
Re: Writing a random dungeon generator in perl.
by xdg (Monsignor) on Aug 15, 2005 at 21:51 UTC

    I think line 22 just refers to the start of the if chain. This is start of your problem:

    elsif ($dungeon[$start] eq '#' && $dungeon[$start] + 1 eq '#'

    You probably want to do the "+1" inside the brackets.

    -xdg

    Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.

      To the second part of your question, you'll probably have an easier time with your project if you try some of these tips:

      • Parameterize your sizes and use them in your code; it will help you catch logical errors and makes it easier to test how it works on a small size array
      • Parameterize your map symbols; easier to change later and easier to understand in the logic of your code
      • Write in logical paragraph and consider commenting them for clarity of intent
      • Replace some of your loop flags and "init then increment" loops with logic that more closely resembles what you're trying to do

      I've revised parts of your example to show what I mean. (Not the most Perlish way to do it, but a step closer that should be clear to see the difference.) I've left out most of your room logic as it wasn't clear what you intended.

      #!/usr/bin/perl use strict; use warnings; my ($dungeon_width, $dungeon_height) = (20, 20); my $WALL = '#'; my $ROOM = '.'; my @dungeon = ($WALL) x ( $dungeon_width * $dungeon_height ); + sub generatemap { # clear a starting location my $cur_location = int rand( @dungeon ); # 0 to $#dungeon $dungeon[ $cur_location ] = $ROOM; # clear squares for (about) half the dungeon my $room_count = 1; while ($room_count < @dungeon / 2) { # pick a square adjacent to the current one my $choice = (-1, +1, -$dungeon_width, +$dungeon_width)[int ra +nd 4]; my $new_location = $cur_location + $choice; # pick again if it would take us out of bounds if ($new_location < 0 || $new_location > $#dungeon) { redo; } # clear it if it's not already a room $cur_location = $new_location; if ( $dungeon[ $cur_location ] ne $ROOM ) { $dungeon[ $cur_location ] = $ROOM; $room_count++; } } } sub printmap { for my $row (0 .. $dungeon_height - 1) { my $first_col = $row * $dungeon_width; my $last_col = $first_col + $dungeon_width - 1; print @dungeon[ $first_col .. $last_col ], "\n"; } } generatemap; printmap;

      -xdg

      Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.

Re: Writing a random dungeon generator in perl.
by displeaser (Hermit) on Aug 16, 2005 at 10:05 UTC
    Hi,
    for some ideas on how to do things, have a look at the following site: http://roguelikedevelopment.org/development/.

    It contains lots of articles on many different aspects of rogulike development. some of it is c++ based but the ideas still stand.

    Also I'd second XDG's comment about placing the increment in brackets.

    Instead of
    elsif ($dungeon[$start] eq '#' && $dungeon[$start] + 1 eq '#'

    try
    elsif ($dungeon[$start] eq '#' && $dungeon[$start+1] eq '#'


    Also see cpan for some modules you might find handy,
    Games-Lineofsight
    Games-Maze
    Games-Dice


    Hope this helps.
    Displeaser
      Let me add Games::Dice::Probability as another handy bit of code... written by a friend of mine.

      --
      $you = new YOU;
      honk() if $you->love(perl)

Re: Writing a random dungeon generator in perl.
by kirbyk (Friar) on Aug 15, 2005 at 21:51 UTC
    It looks like the # character is being interpreted as a start-comment character, which makes it very confusing as to what perl actually things this code is.

    I'd recommend storing it internally as something else, and only converting it to a # in printmap. The fewer \# you have, the better.

    -- Kirby, WhitePages.com

Re: Writing a random dungeon generator in perl.
by Trag (Monk) on Aug 15, 2005 at 22:48 UTC
    I tried both your suggestions, but now I get the following error: Use of uninitialized value in string eq at C:\mygame\mapgenerator.pl line 22.

      Some of this you're going to have to debug yourself -- PM is not a line-by-line debugging service. You're already using "strict" and "warnings", which is good-- both are very good programming practices and will help you greatly. You might also want to try "use diagnostics" or look up the meaning of the warning you're getting in perldiag.

      -xdg

      Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.

Re: Writing a random dungeon generator in perl.
by GrandFather (Saint) on Aug 17, 2005 at 11:55 UTC

    You may like this recursive solution:

    use warnings; use strict; my $rows = 21; # Must be odd and > 3 my $cols = 41; # Must be odd and > 3 my $cells = $rows * $cols; my @dungeon = ('X') x $cells; sub dAt { my ($x, $y) = @_; return \$dungeon [$x + $y * $cols]; } sub generatemap { my ($x, $y) = @_; my $dirCount = 0; my $dir = int rand (4); while ($dirCount++ < 4) { $dir = 0 if $dir > 3; my ($xInc, $yInc) = @{([0, 2], [2, 0], [0, -2], [-2, 0])[$dir]}; my ($xNew, $yNew) = ($x + $xInc, $y + $yInc); next if $xNew < 0 || $xNew >= $cols || $yNew < 0 || $yNew >= $rows; next if ${dAt ($xNew, $yNew)} eq '.'; ${dAt ($x + $xInc / 2, $y + $yInc / 2)} = '.'; ${dAt ($xNew, $yNew)} = '.'; generatemap ($xNew, $yNew); } continue {$dir = 0 if ++$dir > 3;} } sub printmap { my $first = 0; while ($first < $cells) { print join "", @dungeon [$first..($first + $cols - 1)]; print "\n"; $first += $cols; } } ${dAt (1, 1)} = '.'; generatemap (1, 1); #Start in top left corner - gota go there anyway printmap;

    Perl is Huffman encoded by design.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://484003]
Approved by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others pondering the Monastery: (7)
As of 2024-04-19 10:52 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found