472,981 Members | 1,102 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 472,981 software developers and data experts.

Multileval data structures, tie, and indirection

Hi. I'm attempting to write a roguelike (think nethack, crawl, angband,
tome, adom, and yes, I suppose even rogue) in Perl and I've ran into a
problem regarding the data structure for the map and handling it easily.
A map is one 2D level, a simple grid. Each tile on the grid will be a
hash of data about the grid. The map object itself has other bits of
data, so that's why I don't just make the object an anonymous array:

$map->{Map}[$y][$x] = { data about this grid tile }

So far, so good. I was using methods to access and change tiles, but it
was extremely awkward. Which looks more convenient?

$map->tile($y, $x, "Type") = "wall";
$map->{Map}[$y][$x]{Type} = "wall";

Well, bad example. But anyway, I seriously despised the first form. The
syntax itself is extremely awkward and when I generate random dungeons,
I'd have to use that awkward syntax a lot. So I messed around with
Tie::Autotie, decided it wasn't up to the task, and cobbled together a
package that I tied both anonymous arrays (The first representing rows,
pointing to the second which represented columns. Y, X is easier to deal
with sometimes.) to. The hash of data describing a tile consists of
different things, but for the most part, I'd by modifying and accessing
one key in the hash. Now I'm at the point where I can do this:

print $map->{Map}[$y][$x]; # $map->{Map}[$y][$x]{Symbol} is printed,
# through FETCH
$map->{Map}[$y][$x] = "wall"; # This basically sets
# $map->{Map}[$y][$x]{Type} to a hashref
# describing a template of that type.

Convenient and easy, so far. Then I run into the first problem. What if
I want to access or change $map->{Map}[$y][$x]{foobar}? The way I tied
the arrays didn't allow for this. So I hacked together a real messy
sort of hack, which is shown somewhere below. So far, I'm coping with
the shortcomings of tie.

But then I realized that when I assign a tile to "wall", other things
should happen. Things regarding the $map object itself. Meaning that I
need a reference to $map, as well as the value of $y. From the 'point of
view' of $x's tied FETCH routine, I cannot access these.

I know Perl, each time it encounters a tied variable that's part of a
multi-level structure, it'll take a temp value and do something with
that. Step by step. It's impossible to tie an entire data structure to
where one FETCH routine could know $map, $y, and $x.

Then I realized something. I was trying to re-implement the Unix
directory structure, complete with a level of indirection that more or
less happened to be '..'

I suppose I could replace the arrays with hashes, and instead do
$map->{Map}{$y}{$x}. In the $y hash and the $x hash (technically
inaccurate, but from the point of view of $y/$x) I could have a "back"
key pointing to the data structure that contains it. This'd solve some
problems and probably create other unforseen ones. Iterating over the
hash instead of the array would be no problem with the foreach loops I
use already.

I'd still like to avoid method calls to change and access tiles. It *is*
slower, as I've experimented and tried it. Maps will get quite big,
sometimes around 300x500 and there are a great number of maps randomly
generated. And the syntax is just easier.

Anyway, the thing that started me on all this was convenience. Accessing
and modifying $map->{Map}[$y][$x] and having all the fancy stuff happen
behind the scenes. But the way I'm tying stuff down right now is
probably a bit messy, as it won't let me _cleanly_ manipulate anything
other than that one key in the hash. Is there an easier way around all
of this mess?

If interested, here's the module I ended up writing. Buggy and messy,
beware.

######################
# Roguelike::Tilemap #
######################

package Roguelike::Tilemap;

use strict;
use warnings;
use Roguelike::Utility;
use Roguelike::Game;
use Roguelike::Tiles;

our $TILE = load Roguelike::Tiles;
our $_GET = "Symbol";

sub TIEARRAY {
my $class = shift;
return bless [ @_ ], $class;
}

sub FETCH {
my $self = shift;
my $index = shift;
return $self->[$index]{$_GET} if ref $self->[$index] eq
"HASH";
return $self->[$index];
}

sub STORE {
elf = shift;
my $index = shift;
my $value = shift;
if ($_GET eq "Symbol") {
if (ref $value eq "ARRAY") {
tie my @data, "Roguelike::Tilemap", @{$value};
$value = \@data;
} else {
$value = $TILE->{$value};
}
}
$self->[$index] = $value;
}

sub FETCHSIZE {
my $self = shift;
return @{$self};
}

sub STORESIZE {
my $self = shift;
my $count = shift;
if ($count > $self->FETCHSIZE) {
foreach ($count - $self->FETCHSIZE() .. $count) {
$self->STORE($_, "");
}
} elsif ($count < $self->FETCHSIZE) {
foreach (0 .. $self->FETCHSIZE() - $count - 2) {
$self->POP();
}
}
}

sub EXTEND {
my $self = shift;
my $count = shift;
$self->STORESIZE($count);
}

sub EXISTS {
my $self = shift;
my $index = shift;
return defined $self->[$index];
}

sub DELETE {
my $self = shift;
my $index = shift;
}

sub CLEAR {
my $self = shift;
return $self = [];
}

sub PUSH {
my $self = shift;
my @list = @_;
my $last = $self->FETCHSIZE();
$self->STORE($last + $_, $list[$_]) foreach 0 .. $#list;
return $self->FETCHSIZE();
}

sub POP {
my $self = shift;
return pop @{$self};
}

sub SHIFT {
my $self = shift;
return shift @{$self};
}

sub UNSHIFT {
my $self = shift;
my @list = @_;
my $size = scalar(@list);
@{$self}[$size .. $#{$self} + $size] = @{$self};
$self->STORE($_, $list[$_]) foreach 0 .. $#list;
}

sub new {
my $class = shift;
tie my @map, "Roguelike::Tilemap";
my $width = shift;
my $height = shift;
my $tile = shift || " ";
foreach my $y (0 .. $height - 1) { # starting at 1 blows up
$map[$y] = [];
foreach my $x (0 .. $width - 1) { # starting at 1 blows up
$map[$y][$x] = $tile;
}
}
my $self = bless { Map => \@map, Data => [] }, $class;
return $self;
}

sub get {
my $self = shift;
my $y = shift;
my $x = shift;
my $attrib = shift;
my $value = shift;
$_GET = $attrib;
$self->{Map}[$y][$x] = $value if defined $value;
my $data = $self->{Map}[$y][$x];
$_GET = "Symbol";
return $data;
}

42;
And a test script, which sort of works.

#!/usr/bin/perl
use strict;
use warnings;
use lib "/home/dabreegster/tilemap";
use Roguelike::Tilemap;
use Roguelike::Utility;

my $map = new Roguelike::Tilemap 2, 5;

dump $map; # doesn't quite work!

print "Y" if $map->{Map}[2][0] eq " ";

print $map->get(2, 0, "Type");
$map->get(3, 3, "foo" => "bar");
print $map->get(3, 3, "foo");

# always give em a width and height. Then go through each one slowly and assign
# it (these comments won't make sense, don't worry)

# if the Tile hash contains a ref to another hash, array, whatever, it doesn't
# work. Ohwell.

Any tips?

~ 'dabreegster' in #perl and #perlcafe on irc.freenode.org
Aug 5 '05 #1
1 5060
In article <sl************************@localhost.localdomain> ,
Da-Breegster <da*********@gmail.com> wrote:
Hi. I'm attempting to write a roguelike (think nethack, crawl, angband,
tome, adom, and yes, I suppose even rogue) in Perl and I've ran into a
problem regarding the data structure for the map and handling it easily.
A map is one 2D level, a simple grid. Each tile on the grid will be a
hash of data about the grid. The map object itself has other bits of
data, so that's why I don't just make the object an anonymous array:


[rest of long post snipped]

I am afraid you have wasted your time by posting to a defunct
newsgroup. Try comp.lang.perl.misc in the future.
----== Posted via Newsfeeds.Com - Unlimited-Uncensored-Secure Usenet News==----
http://www.newsfeeds.com The #1 Newsgroup Service in the World! >100,000 Newsgroups
---= East/West-Coast Server Farms - Total Privacy via Encryption =---
Aug 5 '05 #2

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

4
by: Thomas Paul Diffenbach | last post by:
Can anyone point me to an open source library of /statically allocated/ data structures? I'm writing some code that would benefit from trees, preferably self balancing, but on an embedded system...
10
by: Bart Goeman | last post by:
Hi, I have a question about how to put redundant information in data structures, initialized at compile time. This is often necessary for performance reasons and can't be done at run time (data...
19
by: santosh | last post by:
Hi all, In the following program I allocate a block of pointers to type char, initialised to zero. I then point each of those pointers to a block of allocated memory of fixed size (33 bytes). A...
3
by: osp | last post by:
hi to every one.... i just started out with c++ and i think i am doing well.i use Robert Laffore to study. which book should i use for data structures ? please help. thank you with regards ...
11
by: efrat | last post by:
Hello, I'm planning to use Python in order to teach a DSA (data structures and algorithms) course in an academic institute. If you could help out with the following questions, I'd sure...
29
by: Mik0b0 | last post by:
Hallo to everyone. This fall I am going to start data structures as a part of C language course. The problem is I could not find any satisfying tutorial about structures in C. There are plenty of...
4
by: jehugaleahsa | last post by:
Hello: When developing data structures for C#, there is an obvious performance hit when utilizing primitive types. For instance, a recent hash table implementation I wrote works exceedingly fast...
3
by: imaloner | last post by:
I am posting two threads because I have two different problems, but both have the same background information. Common Background Information: I am trying to rebuild code for a working,...
1
Markus
by: Markus | last post by:
I'm having a hard time grasping the concept of multiple levels of indirection, that is for example a char **. I understand that passing a pointer to some data to a function means that memory for that...
0
by: lllomh | last post by:
Define the method first this.state = { buttonBackgroundColor: 'green', isBlinking: false, // A new status is added to identify whether the button is blinking or not } autoStart=()=>{
2
isladogs
by: isladogs | last post by:
The next Access Europe meeting will be on Wednesday 4 Oct 2023 starting at 18:00 UK time (6PM UTC+1) and finishing at about 19:15 (7.15PM) The start time is equivalent to 19:00 (7PM) in Central...
2
by: giovanniandrean | last post by:
The energy model is structured as follows and uses excel sheets to give input data: 1-Utility.py contains all the functions needed to calculate the variables and other minor things (mentions...
3
NeoPa
by: NeoPa | last post by:
Introduction For this article I'll be using a very simple database which has Form (clsForm) & Report (clsReport) classes that simply handle making the calling Form invisible until the Form, or all...
1
by: Teri B | last post by:
Hi, I have created a sub-form Roles. In my course form the user selects the roles assigned to the course. 0ne-to-many. One course many roles. Then I created a report based on the Course form and...
0
isladogs
by: isladogs | last post by:
The next Access Europe meeting will be on Wednesday 1 Nov 2023 starting at 18:00 UK time (6PM UTC) and finishing at about 19:15 (7.15PM) Please note that the UK and Europe revert to winter time on...
3
by: nia12 | last post by:
Hi there, I am very new to Access so apologies if any of this is obvious/not clear. I am creating a data collection tool for health care employees to complete. It consists of a number of...
0
NeoPa
by: NeoPa | last post by:
Introduction For this article I'll be focusing on the Report (clsReport) class. This simply handles making the calling Form invisible until all of the Reports opened by it have been closed, when it...
4
by: GKJR | last post by:
Does anyone have a recommendation to build a standalone application to replace an Access database? I have my bookkeeping software I developed in Access that I would like to make available to other...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.