Latest news: Mapster is a success!

The Alien Adoption Agency is an odd rpg-style game written in Perl, with a BOFH-like creator, who likes to punish the denizens with hard puzzles. Like daily changing 30x30 mazes with the destination point at unknown co-ordinates.

As you might see, even while employing the 'always turn right at T-splits' tactic, exploring the maze can take many hours. I used a paper map grid for a couple of days now (generated by a Perl program of course ;-), but I thought it would save a lot of time if I could let other people do the work for me (remember, "Laziness is a Virtue"...), so I created Mapster. It allows people to co-operatively explore the maze, and share their maps effortlessly. Errors can be overwritten, curious Perl Monks can create dead ends in the middle for fun, and best of all, when the maze changes in the game, the program will start to use a new file to store the map.

Enough said, here's some code:

Grid.pl - create a customized grid with many options

(the additional axes don't work perfectly, but it's good enough)
This file is self-contained, except for the CGI module, it doesn't rely on any other files.

#!/usr/bin/perl -w use CGI; my $q = new CGI; print $q->header; if(!$q->param()) { &print_empty_form; } else { &create_grid; } sub create_grid { # Make sure these variables contain valid values or a default value +for a nice grid. $width = ($q->param('width') =~ /(\d{1,2})/)[0] || 30; my $height = ($q->param('height') =~ /(\d{1,2})/)[0] || 30; $ulx = ($q->param('ULX') =~ /(-?\d{1,3})/)[0] || 1; my $uly = ($q->param('ULY') =~ /(-?\d{1,3})/)[0] || 1; my $xaxis = ($q->param('XAxis') =~ /(\d{1,2})/)[0] || 0; my $yaxis = ($q->param('YAxis') =~ /(\d{1,2})/)[0] || 0; my $xoption = $q->param('XOption'); my $yoption = $q->param('YOption'); my $xtop = $q->param('XTop'); my $xbottom = $q->param('XBottom'); $yleft = $q->param('YLeft'); $yright = $q->param('YRight'); my $border = ($q->param('border') =~ /(\d{1,2})/)[0] || 0; my $cellspacing = ($q->param('cellspacing') =~ /(\d{1,2})/)[0] || 0; my $cellpadding = ($q->param('cellpadding') =~ /(\d{1,2})/)[0] || 0; $fill = ($q->param('fill') =~ /(.{1,3})/)[0] || " + "; $fill =~ s/ /&nbsp;/g; if($ulx < -99) { $ulx /= 10; $ulx = int $ulx; } # I don't know wheth +er it's necessary if($uly < -99) { $uly /= 10; $uly = int $uly; } # to int them, but i +t might be safer. $perx = 0; $pery = 0; if($xaxis > 0) { if($xoption eq "XTimes") { $pery = $ width / ($xaxis+1); } else { $pery = $xaxis; } } if($yaxis > 0) { if($yoption eq "YTimes") { $perx = $height / ($yaxis+1); } else { $perx = $yaxis; } } print "<html><head><title>($width x $height) Grid starting at ($ulx, +$uly)</title></head>"; print "<killbanner>"; # I don't want to force people to print a s +tupid banner! They'll prefer to click away the stupid popup instead! print "<body>\n"; print "<table border=$border cellspacing=$cellspacing cellpadding=$c +ellpadding align=center>\n"; &print_x_axis if $xtop eq "yes"; my $iy = 1; for ($uly .. $uly + $height - 1) { &print_row($_); if($pery > 0 && $iy - $pery > 0 && $_ < $uly + $height - 1) { $iy -= $pery; &print_x_axis; } $iy++; } &print_x_axis if $xbottom eq "yes"; print "</table></body></html>\n"; } sub print_x_axis { my $ix = 1; print "<tr align=center>"; print "<td>&nbsp;</td>" if $yleft eq "yes"; for ($ulx .. $ulx + $width - 1) { if(length == 1) { print "<td><small>&nbsp;${_}&nbsp;</td>"; } elsif(length == 2) { print "<td><small>&nbsp;$_</td>"; } else { print "<td><small>$_</td>"; } if($perx > 0 && $ix - $perx > 0 && $_ < $ulx + $width - 1) { $ix -= $perx; print "<td></td>"; } $ix++; } print "<td>&nbsp;</td>" if $yright eq "yes"; print "</tr>\n"; } sub print_row { my $ix = 1; print "<tr align=center>"; print "<td><small>$_[0]</td>" if $yleft eq "yes"; for ($ulx .. $ulx + $width - 1) { print "<td>$fill</td>"; if($perx > 0 && $ix - $perx > 0 && $_ < $ulx + $width - 1) { $ix -= $perx; print "<td><small>$_[0]</td>"; } $ix++; } print "<td><small>$_[0]</td>" if $yright eq "yes"; print "</tr>\n"; } sub print_empty_form { print <<HERE; <html><head><title>Grid Generator</title></head><body> <center> <h1>Grid Generator</h1> <form method="post" onReset="return confirm('Do you really want to res +et the parameters to the default values?')"> <table border=1> <tr><td align=center> <small>Size options</small><br> Grid size: ( <input type=text name=width size=2 maxlength=2 value=30> , <input type=text name=height size=2 maxlength=2 value=30> ) </td></tr> <tr><td align=center> <small>Offset options</small><br> Upper-left corner co-ordinates are: ( <input type=text name=ULX size=3 maxlength=3 value=1> , <input type=text name=ULY size=3 maxlength=3 value=1> ) </td></tr> <tr><td> <center><small>Axis options</small></center> Display X axis at <input type=checkbox name=XTop checked value=y +es> top, <input type=checkbox name=XBottom checked value=yes> bottom<br> Display Y axis at <input type=checkbox name=YLeft checked value= +yes> left, <input type=checkbox name=YRight checked value=yes> right side <center><small>Additional axes within the grid</small><center> Display an additional X axis <input type=text name=XAxis size=2 maxlength=2 value=0> <select name=XOption> <option value=XTimes>times</option> <option value=PerX>every ... rows</option> </select> within the grid<br> Display an additional Y axis <input type=text name=YAxis size=2 maxlength=2 value=0> <select name=YOption> <opTion value=YTimes>times</option> <option value=PerY>every ... cols</option> </select> within the grid </td></tr> <tr><td align=center> <small>Additional options</small><br> Fill grid with <input type=text name=fill size=3 maxlength=3 val +ue=" + "> Table border <input type=text name=border size=2 maxlength=2 val +ue=0><br> Table cellspacing <input type=text name=cellspacing size=2 maxle +ngth=2 value=0> Table cellpadding <input type=text name=cellpadding size=2 maxle +ngth=2 value=0> </td></tr> <tr><td align=center> <input type=submit value="Generate Grid"> <input type=reset value="Reset to defaults"> </td></tr> </table> </form> </center> </body></html> HERE }

Maze.pl - the maze mapper

This program relies on a number of image files, and can automatically create maze data files when necessary, as long as it has write access to the specified directory. It'll also log the IP and time of additions to the maze, so in case someone is deliberately trying to sabotage the map, I could add some protection to it. It'll detect whether it's running on my own computer or on Prohosting.com, and select the correct paths by itself, so I can ftp it to there without changing it. The <killbanner> directive chooses for a popup instead of a banner above, I chose that because it ruined the nice display of the table, with the neatly aligned maze tiles. (Prohosting.com is a little slow, but it's quite okay for these kinds of programs, IMHO.)

I posted the current code here again, so you can see what it looks like right now. I still might need to adjust the error handling, but the program should not encounter them very often. Also, I wouldn't want to have many different Mapsters exist, it would defeat its purpose! Please don't run it on your own server, it'd fragment Mapster.

Update: I removed CGI.pm from it due to speed considerations. It's a great module, but it takes a second to load, and the server is extremely loaded and doesn't seem to support mod_perl or something. Now, it runs much faster, and also doesn't load the announcement from a separate file. It's built in as well.

Nah, I don't want Mapster to fragment, it'll kill it. Therefor, Mapste +r is Closed Source. Sorry, people ;-)

Replies are listed 'Best First'.
Re: Co-operative maze mapping
by dws (Chancellor) on Mar 17, 2001 at 22:25 UTC
    An intesting collaborive application.

    Your development efforts, and the lives of those who try to run your scripts elsewhere, might be eased somewhat if you were to add use CGI::Carp qw(fatalsToBrowser); to the tops of your scripts. Then you could safely add error checking to all of your open calls.

    Before you spring this on the general populus, cleaning up your die messages might be in order. Reporting "404" for open failures is at best cute, and at worst misleading. If someone emails you to report "I tried your stuff and got a 404", you're not going to know whether it was an HTTP 404 or a 404 from one of your error messages, and you'll have to go an extra round of email before you can sort out the problem.

      I'm calling the program "Mapster" :-)

      I'll consider the fatalsToBrowser, it'd give the users more of a clue about what is wrong when something goes wrong than just a white screen.

      However, when something goes wrong, it's usually because I'm fooling around with it, and I'll have it fixed in a matter of minutes, so it might not be that necessary.

      But thanks for your input. Also, I've fixed some more bugs. I'll update the code soon.

      Well, I followed your instruction with the Mapster::TNG framework prototype, and boy, did it save me a lot of time! A shame I can only ++ a post once :-)