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

I've created an ascii-art game that uses a two-dimensional array to keep track of the map, which refreshes each time the character moves. The game is also multiplayer, using io::socket::inet sockets to recieve the opponent's location and refresh the map when a change is made. To achieve this, I used threads so that both the keyboard and the sockets would wait for input simultaneously.

My problem is this: when the thread is created, the 2D array storing the map on the main level:

#@level is an array of strings; each string is a row of the map. # This breaks the entire thing into a 2D array where each element is a + character on the map. my @tempArray; my @twoDee; for(my $m = 0; $m <= $#level; $m++) { @tempArray = split(//, $level&#91;$m&#93;); push @twoDee, &#91;@tempArray&#93;; }


is duplicated and a copy is sent to the thread governing socket input. Thus, each time the thread calls the function I've created for redrawing the map, it sends it uses its own copy of the 2D array instead of the one on the main level. As it stands, I have set it up so that both copies of the map are updated identically using shared variables that contain the X and Y coordinates of the player and his / her opponent. The problem is, should I attempt to add other features to the game, like, say, more opponents, I will have to create yet another thread and track everyone's movements yet again.

I have read the man pages from threads::shared many times, but can't find / understand a means of sharing a 2D array, which would make my life much simpler. I've poked and prodded, but usually get an error message about inappropriate scalar values.

I'd appreciate any help anyone could offer -- also, let me know if you need to see more code blocks and I'll paste them here.

Janitored by Corion: Added formatting, code tags, as per Writeup Formatting Tips

Replies are listed 'Best First'.
Re: Using threads::shared with multidimensional arrays
by BrowserUk (Patriarch) on Jun 10, 2006 at 23:18 UTC

    Silly example. The background thread randomly increments values in the shared array of shared arrays, whilst the foregorund thread displays it.

    Things to note.

    1. To build a shared multi-dimensional array, you have to share the base array, and then fill it with references to shared arrays.
    2. You must share the arrays before you put anything in them. Calling share() discards any existing contents of an array (or hash).
    3. To share an anonymous array (or hash), you have to bypass the prototype on share() by calling it with &.
    4. I haven't used lock() in this example, but be aware that if you lock the base array each time, you will severally restrict the performance of both threads. Applying locking to the subarrays, or individual scalars as appropriate to your code, will decrease the contention and improve performance.
    #! perl -slw use strict; use threads; use threads::shared; our $X :shared; $X ||= 10; our $Y :shared; $Y ||= 10; sub background { my $AoA = shift; while( 1 ) { $AoA->[ rand $Y ][ rand $X ]++; select undef, undef, undef, 0.01; } } my $AoA: shared = &share( [] ); @{ $AoA } = map{ &share( [] ) } 1 .. $Y; @{ $AoA->[ $_ ] } = (0) x $X for 0 .. $X-1; async \&background, $AoA; while( 1 ) { system 'cls'; print "@$_" for @{ $AoA }; select undef, undef, undef, 0.1; }

    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
Re: Using threads::shared with multidimensional arrays
by renodino (Curate) on Jun 10, 2006 at 22:37 UTC
    use threads; use threads::shared; use strict; use warnings; my @level = ( 'ABCDEFGHI', '12434567', '+-+-+-+-+-' ); my @twoDee : shared = (); foreach (@level) { # # inner array must be shared to assign its ref # to the outer array # my @tempArray : shared = split //; push @twoDee, \@tempArray; } print join(', ', @$_), "\n" foreach (@twoDee);
Re: Using threads::shared with multidimensional arrays
by chromatic (Archbishop) on Jun 11, 2006 at 04:43 UTC

    Would you consider a different approach? Use a stricter separation of model, view, and controller where each socket thread only gets and sends view information and uses message passing to update the view. That is, instead of updating a shared data structure, send a message (perhaps just by pushing onto a simple queue) to the model describing the move in terms of what the player has requested.

    I've used this before to allow very different input forms... it's pretty flexible and keeps things clean.

      First off, thanks very much for the help! I was shocked to have so many responses so quickly. (I'm a new member.) I would definitely consider a different method, chromatic, but I'm unclear on the method you're describing. Would I share the array containing the list of moves and create a thread to constantly check it, redrawing the map when its length had changed? Thanks again, Monks.