File Coverage

blib/lib/Games/Perlwar.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             package Games::Perlwar;
2              
3             our $VERSION = '0.03';
4              
5 1     1   24984 use strict;
  1         2  
  1         47  
6 1     1   6 use warnings;
  1         2  
  1         34  
7 1     1   1171 use utf8;
  1         16  
  1         6  
8              
9 1     1   1078 use Safe;
  1         46553  
  1         50  
10 1     1   1394 use XML::Simple;
  0            
  0            
11             use XML::Writer;
12             use XML::LibXML;
13             use IO::File;
14              
15             use Games::Perlwar::Array;
16             use Games::Perlwar::Cell;
17             use Games::Perlwar::AgentEval;
18              
19             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
20              
21             sub new {
22             my( $class, $dir ) = @_;
23             my $self = { dir => $dir || '.', interactive => 1 };
24             chdir $self->{dir};
25             bless $self, $class;
26             }
27              
28             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
29              
30             sub clear_log {
31             my $self = shift;
32             $self->{log} = ();
33             }
34              
35             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
36              
37             sub load_players_from_config {
38             my( $self, $config ) = @_;
39              
40             my %player;
41             for my $player ( $config->findnodes( '/configuration/players/player' ) ) {
42             my $name = $player->findvalue( '@name' );
43             my $color = $player->findvalue( '@color' );
44             my $status = $player->findvalue( '@status' ) || 'OK' ;
45              
46             $player{ $name } = { color => $color, status => $status };
47             }
48              
49             return %player;
50             }
51              
52             sub load_players_from_iteration {
53             my( $self, $iter ) = @_;
54              
55             my %player;
56             for my $player ( $iter->findnodes( '/iteration/summary/player' ) ) {
57             my $name = $player->findvalue( '@name' );
58             my $color = $player->findvalue( '@color' );
59             my $status = $player->findvalue( '@status' ) || 'OK' ;
60              
61             $player{ $name } = { color => $color, status => $status };
62             }
63              
64             return %player;
65             }
66              
67             sub load_players_adhoc {
68             my( $self, $config ) = @_;
69              
70             my $community_file = $config->findvalue(
71             '/configuration/players/@community' );
72             my @players = XML::LibXML->new
73             ->parse_file( $community_file )
74             ->findnodes( '//player' );
75              
76             @players = grep { -e 'mobil/'.$_->findvalue('@name') } @players;
77              
78             my %player;
79             for my $player ( @players ) {
80             my $name = $player->findvalue( '@name' );
81             my $color = $player->findvalue( '@color' );
82             my $status = 'OK' ;
83              
84             $player{ $name } = { color => $color, status => $status };
85             }
86              
87             return %player;
88             }
89              
90             sub load {
91             my ( $self, $iteration, $replay ) = @_;
92              
93             # if it's a replay, we load from the current iteration,
94             # then get the newcomers from the next iteration.
95             # if not, we get the newcomers from mobil station
96             # the loading of newcomers must happen in
97             # run_iteration
98              
99            
100             print "loading configuration.. \n";
101              
102             my $parser = XML::LibXML->new;
103              
104             my $config = $parser->parse_file( 'configuration.xml' );
105              
106             $self->{replay} = $replay;
107              
108             my $filename;
109             if ( defined $iteration ) {
110             $filename = sprintf( "round_%05d.xml", $iteration );
111             die "can't load iteration $iteration\n"
112             unless -e $filename;
113             }
114             else {
115             $filename = 'round_current.xml';
116             }
117             my $current_iteration = $parser->parse_file( $filename );
118              
119             $self->{round} = $current_iteration->findvalue( '/iteration/@nbr' );
120             print "loading iteration $self->{round}\n";
121              
122             $self->{conf}{gameLength} =
123             $config->findvalue( '/configuration/gameLength/text()' );
124             $self->{gameVariant} =
125             $config->findvalue( '/configuration/gameVariant/text()' );
126             $self->{conf}{agentMaxSize} =
127             $config->findvalue( '/configuration/agentMaxSize/text()' );
128              
129             $self->{conf}{theArraySize} =
130             $config->findvalue( '/configuration/theArraySize/text()' );
131             $self->{theArray} = Games::Perlwar::Array->new({
132             size => $self->{conf}{theArraySize} });
133             $self->{theArray}->load_from_xml( $current_iteration );
134              
135             if ( $self->{round} == 0 ) {
136            
137             $self->{conf}{player} = {
138             $config->findvalue( '//players/@list' ) eq 'predefined'
139             ? $self->load_players_from_config( $config )
140             : $self->load_players_adhoc( $config )
141             };
142             }
143             else {
144             $self->{conf}{player} = { $self->load_players_from_iteration(
145             $current_iteration ) };
146             }
147            
148             $self->set_game_status(
149             $current_iteration->findvalue( '/iteration/summary/status' )
150             || 'ongoing' );
151             }
152              
153             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
154              
155             sub visit_mobil_station {
156             my $self = shift;
157              
158             $self->{newcomers} = [];
159             chdir 'mobil';
160            
161             opendir my $dir, '.' or die "couldn't open dir mobil: $!\n";
162             my @files = sort { -M $b <=> -M $a }
163             grep { exists $self->{conf}{player}{$_} }
164             readdir $dir;
165             closedir $dir;
166              
167             my @newcomers;
168             for my $player ( @files ) {
169             my $date = localtime( $^T - (-M $player)*24*60*60 );
170            
171             my $fh;
172             my $code;
173             {
174             undef $/;
175             open $fh, $player or die;
176             $code = <$fh>;
177             close $fh;
178             }
179              
180             unlink $player or $self->log( "ERROR: $!" );
181            
182             push @newcomers, [ $player, $date, $code ];
183             }
184              
185             chdir '..';
186              
187             return @newcomers;
188             }
189              
190             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
191              
192             sub get_game_status {
193             return $_[0]->{conf}{gameStatus};
194             }
195              
196             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
197              
198             sub set_game_status {
199             return $_[0]->{conf}{gameStatus} = $_[1];
200             }
201              
202             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
203              
204             sub play_round
205             {
206             my $self = shift;
207            
208             # check if the game is over (because a player won)
209             if( $self->get_game_status eq 'over' ) {
210             print "game is already over, exiting\n";
211             return;
212             }
213              
214             $self->{round}++;
215              
216             $self->{log} = [];
217             $self->log( localtime() . " : running round ".$self->{round} );
218            
219             # import newcomers
220             $self->log( "train arriving from Station Mobil.." );
221             $self->introduce_newcomers;
222            
223             # check if players are eliminated
224             $self->checkForEliminatedPlayers;
225            
226             # run each slot
227             $self->log( "running the Array.." );
228             $self->runSlot( $_ ) for 0..$self->{conf}{theArraySize}-1;
229              
230             # end of round checks
231             $self->{theArray}->reset_operational;
232              
233             # check for victory
234             $self->agent_census;
235              
236             my @survivors;
237             for my $p ( keys %{$self->{conf}{player}} ) {
238             if ( $self->{conf}{player}{$p}{agents} ) {
239             push @survivors, $p;
240             }
241             else {
242             $self->{conf}{player}{$p}{status} = 'EOT';
243             }
244             }
245              
246             if ( @survivors > 1 ) {
247             print scalar( @survivors ),
248             " players still have agents on the field\n";
249             } else {
250             print @survivors ? "only $survivors[0] left!\n"
251             : "no survivor!\n";
252             # TODO update the config w/ victory
253             $self->set_game_status( 'over' );
254             }
255              
256             # check if the game is over (because round > game length)
257             $self->endtime_reached if $self->{round} >= $self->{conf}{gameLength};
258              
259             $self->save;
260             delete $self->{newcomers};
261             delete $self->{old_iteration};
262             }
263             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
264              
265             sub endtime_reached {
266             my $self = shift;
267              
268             print "number of rounds limit reached, game is over\n";
269             $self->set_game_status( 'over' );
270              
271             my $player = $self->{conf}{player};
272              
273             my %census = $self->agent_census;
274              
275             my @k = reverse sort { $census{$a} <=> $census{$b} }
276             grep { $player->{status} == 'OK' } keys %$player;
277              
278             return unless @k;
279              
280             $player->{ shift @k }{status} = 'w1nn3r';
281             $player->{$_}{status} = 'EOT' for @k;
282              
283             return;
284             }
285              
286             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
287              
288             sub save
289             {
290             my $self = shift;
291            
292             print "saving round $self->{round}..\n";
293            
294             #$self->saveConfiguration;
295            
296             #XMLout( $self->{conf}, OutputFile => "configuration.xml", RootName => 'configuration' );
297            
298              
299             my $output = new IO::File(">round_current.xml");
300             my $writer = new XML::Writer(OUTPUT => $output);
301              
302             $writer->startTag( "iteration", nbr => $self->{round} );
303              
304             $writer->startTag( 'summary' );
305             $writer->dataElement( 'status' => $self->get_game_status );
306              
307             my %census = $self->agent_census;
308              
309             for my $p ( keys %{$self->{conf}{player} } ) {
310             $writer->emptyTag( 'player',
311             name => $p,
312             status => $self->{conf}{player}{$p}{status},
313             color => $self->{conf}{player}{$p}{color},
314             agents => $census{ $p },
315             );
316             }
317              
318             $writer->endTag( 'summary' );
319            
320             if( $self->{newcomers} )
321             {
322             $writer->startTag( 'newcomers' );
323            
324             for( @{$self->{newcomers}} )
325             {
326             my @x = @$_;
327             $writer->dataElement( 'newcomer', $x[2], player => $x[0], time => $x[1] );
328             }
329             $writer->endTag;
330             }
331            
332            
333             if( $self->{log} )
334             {
335             $writer->startTag( 'log' );
336             $writer->dataElement( 'entry', $_ ) for @{$self->{log}};
337             $writer->endTag;
338             }
339              
340             $self->{theArray}->save_as_xml( $writer );
341              
342             $writer->endTag;
343             $writer->end();
344              
345             $output->close();
346              
347             open my $current_file, "round_current.xml" or die;
348             open my $archive, sprintf( ">round_%05d.xml", $self->{round} ) or die "$!";
349             print $archive $_ while <$current_file>;
350             close $current_file;
351             close $archive;
352             }
353              
354             ##########################################################################
355              
356             sub saveConfiguration
357             {
358             die "obsolete\n";
359             my %conf = @_ == 1 ? %{$_[0]->{conf}} : @_;
360            
361             my $output = new IO::File(">configuration.xml");
362             my $writer = new XML::Writer(OUTPUT => $output, NEWLINES => 1);
363              
364             $writer->startTag( 'configuration' );
365             $writer->dataElement( 'title', $conf{title} );
366             $writer->dataElement( 'gameStatus', $conf{gameStatus} );
367             $writer->dataElement( 'gameLength', $conf{gameLength} );
368             $writer->dataElement( 'theArraySize', $conf{theArraySize} );
369             $writer->dataElement( 'snippetMaxLength', $conf{snippetMaxLength} );
370            
371             $writer->dataElement( 'currentIteration', $conf{currentIteration} );
372             if( $conf{mamboDecrement} )
373             {
374             $writer->dataElement( 'mamboDecrement', $conf{mamboDecrement} );
375             }
376             $writer->dataElement( 'note', ref $conf{note} ? %{$conf{note}}
377             : $conf{note} );
378            
379             foreach( keys %{$conf{player}} )
380             {
381             $writer->dataElement( 'player', $_, color => $conf{player}{$_}{color},
382             password => $conf{player}{$_}{password}, status => $conf{player}{$_}{status} );
383             }
384            
385             $writer->endTag;
386             $writer->end;
387             $output->close;
388             }
389              
390             ##########################################################################
391              
392             sub checkForEliminatedPlayers
393             {
394             my $self = shift;
395            
396             no warnings 'uninitialized';
397            
398             $self->log( "checking for eliminated players.." );
399            
400             my %score = $self->{theArray}->census;
401            
402             for my $player ( keys %{ $self->{conf}{player} } )
403             {
404             next if $self->{conf}{player}{$player}{status} eq 'EOT';
405             unless( $score{ $player } )
406             {
407             $self->log( "\tplayer $player lost all agents, eliminated" );
408             $self->{conf}{player}{$player}{status} = 'EOT';
409             }
410             }
411            
412             }
413              
414             sub get_iteration_newcomers {
415             my( $self, $iteration ) = @_;
416              
417             my $iter = XML::LibXML->new->parse_file(
418             sprintf( "round_%05d.xml", $iteration )
419             );
420              
421             my @newcomers;
422             for my $n ( $iter->findnodes( '//newcomer' ) ) {
423             my $owner = $n->findvalue( '@player' );
424             my $code = $n->findvalue( 'text()' );
425             my $date = $n->findvalue( '@time' );
426              
427             push @newcomers, [ $owner, $date, $code ];
428             }
429              
430             return @newcomers;
431             }
432              
433             ##########################################################################
434              
435             sub introduce_newcomers
436             {
437             no warnings 'uninitialized';
438             my $self = shift;
439              
440             # TODO special case for adhoc at iteration 0
441            
442             my @newcomers = $self->{replay} ? $self->get_iteration_newcomers( )
443             : $self->visit_mobil_station
444             ;
445              
446             $self->{newcomers} = \@newcomers;
447              
448             $self->log( "\tno-one was aboard" ) unless @newcomers;
449              
450             AGENT: for my $newcomer ( @newcomers ) {
451             my( $player, $date, $code ) = @$newcomer;
452             $self->log( "\t".$player."'s new agent is aboard (u/l'ed $date)" );
453             # dead players can't submit agents
454             if( $self->{conf}{player}{$player}{status} eq 'EOT' ) {
455             $self->log( "\tplayer is eliminated, can't submit a new agent" );
456             next AGENT;
457             }
458            
459             my @available_slots = $self->{theArray}->empty_cells;
460            
461             if( @available_slots > 0 )
462             {
463             my $slot = $available_slots[ rand @available_slots ];
464             $self->log( "\tagent inserted at cell $slot" );
465             $self->{theArray}->cell( $slot )->insert({
466             owner => $player,
467             code => $code,
468             });
469             next AGENT;
470             }
471            
472             # no empty cells, maybe a cell already occupied by
473             # the player?
474             @available_slots = $self->{theArray}->cells_belonging_to( $player );
475            
476             if( @available_slots > 0 )
477             {
478             my $slot = $available_slots[ rand @available_slots ];
479             $self->log( "agent at cell $slot is upgraded" );
480             $self->{theArray}->cell( $slot )->insert({
481             owner => $player,
482             code => $code,
483             });
484             unlink $player or $self->log( "ERROR: $!" );
485             next AGENT;
486             }
487            
488             $self->log( "no empty slot left, agent deleted" );
489             }
490             }
491              
492             ##########################################################################
493              
494             sub log
495             {
496             my $self = shift;
497            
498             return @{$self->{log}} unless @_;
499              
500             if( $self->{interactive} ) {
501             local $\ = "\n";
502             print for @_;
503             }
504              
505             push @{$self->{log}}, @_;
506             }
507              
508             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
509              
510             sub insert_agent {
511             my ( $self, $pos, $player, $code ) = @_;
512              
513             if( $pos >= $self->{conf}{theArraySize} ) {
514             $self->log( "can't insert agent: cell $pos out of bound" );
515             return;
516             }
517            
518             $self->{theArray}->cell( $pos )->insert({
519             owner => $player,
520             code => $code,
521             });
522             }
523             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
524              
525             sub run_cell {
526             my( $self, $cell_id, $vars_ref ) = @_;
527             my %vars;
528             %vars = %$vars_ref if $vars_ref;
529              
530             return $self->array->run_cell( $cell_id => {
531             %vars,
532             '$S' => $self->{conf}{agentMaxSize},
533             '$I' => $self->{conf}{gameLength},
534             '$i' => $self->{round},
535             } );
536              
537             }
538              
539             ##########################################################################
540              
541             # ( $result, $error, @array ) = $pw->execute( @array )
542             # executes the code of $array[0]
543             sub execute
544             {
545             my( $self, $cell_id ) = @_;
546              
547             # what happens in execute(), stays in execute
548             local *STDERR;
549             my $warnings;
550             open STDERR, '>', \$warnings;
551              
552             my $owner = $self->array->cell( $cell_id )->get_owner;
553            
554             local @_ = $self->array->get_cells_code( $cell_id );
555             local $_ = $_[0];
556             my @o = $self->array->get_facades( $cell_id );
557              
558             # run this in a safe
559             my $safe = new Safe 'Container';
560             $safe->permit( qw/ rand time sort :browse :default / );
561             my $result;
562             my $error;
563            
564             eval
565             {
566             local $SIG{ALRM} = sub { die "timed out\n" };
567             alarm 3;
568             undef $@;
569             my $code = $_[0];
570             @Container::Array = @_;
571             @Container::o = @o;
572             @Container::O = $owner;
573             $Container::S = $self->{conf}{snippetMaxLength};
574             $Container::I = $self->{conf}{gameLength};
575             $Container::i = $self->{conf}{currentIteration};
576             $safe->share_from( 'Container',
577             [ '$S', '$I', '$i', '@_', '@o', '$O' ] );
578             $result = $safe->reval( <
579             local *_ = \\\@Array;
580             \$_ = \$_[0];
581             $code
582             EOT
583            
584             $error = $@;
585             alarm 0;
586             };
587              
588             return ( $result, $error ) if $error;
589              
590             my @code_array = $safe->reval( '@Array' );
591             $owner = $safe->reval( '$o[0]' );
592             $code_array[0] = $safe->reval( '$_' );
593              
594             return( $result, $error, $owner, @code_array );
595             }
596              
597             ##########################################################################
598              
599             sub runSlot {
600             my ( $self, $slotId ) = @_;
601              
602             my $cell = $self->{theArray}->cell( $slotId );
603              
604             # diddled cells and empty cells aren't executed
605             return if $cell->is_empty
606             or not $cell->get_operational;
607            
608             $self->log( "cell $slotId: agent owned by ".$cell->get_owner );
609              
610             my @code_array = $self->{theArray}->get_cells_code( $slotId );
611             my @owner_array = $self->{theArray}->get_facades( $slotId );
612              
613             # exceed permited size?
614             my $code = $cell->get_code;
615             if( length $code > $self->{conf}{agentMaxSize} ) {
616             $self->log( "\tagent crashed: is ".length($code)." chars, exceeds max permitted size $self->{conf}{snippetMaxSize}" );
617             $cell->delete;
618             return;
619             }
620              
621             $self->log( "\texecuting.." );
622            
623             # TODO squeeze in the ownership array
624             my $agent = $self->run_cell( $slotId );
625              
626             if( $agent->crashed ) {
627             $self->log( "\tagent crashed: ".$agent->error_msg );
628             $cell->delete;
629             return;
630             }
631              
632             $cell->set_code( $agent->eval( '$_' ) );
633             $cell->set_facade( $agent->eval( '$o' ) );
634              
635             no warnings qw/ uninitialized /;
636              
637             my $output = $agent->return_value;
638             my $result = $output;
639             $output = substr( $output, 0, 24 ).".." if length $output > 25;
640             $output =~ s#\n#\\n#g;
641            
642             $self->log( "\tagent returned: $output" );
643            
644             if( $result =~ /^!(-?\d*)$/ ) {
645             $self->_nuke_operation( $slotId, $1 );
646             }
647             elsif( $result =~ /^\^(-?\d*)$/ ) {
648             $self->_p0wn_operation( $slotId, $1 );
649             }
650             elsif( $result =~ /^~(-?\d*)$/ ) {
651             $self->_alter_operation( $slotId, $1, [ $agent->eval( '@Array' ) ] );
652             }
653             elsif( $result =~ /^(-?\d*):(-?\d*)$/ ) {
654             $self->_copy_operation( $slotId, $1, $2 );
655             }
656             else {
657             $self->_noop_operation();
658             }
659             }
660              
661             sub _nuke_operation {
662             my( $self, $agent_index, $target_index ) = @_;
663              
664             $target_index = $self->relative_to_absolute_position( $agent_index, $target_index );
665             return if $target_index == -1;
666            
667             if( $self->array->cell( $target_index )->is_empty ) {
668             $self->log( "\tno agent found at cell $target_index" );
669             return;
670             }
671            
672             $self->array->cell( $target_index )->clear;
673             $self->log( "\tagent in cell $target_index destroyed" );
674             }
675              
676             sub _p0wn_operation {
677             my( $self, $agent_index, $target_index ) = @_;
678              
679             $target_index = $self->relative_to_absolute_position( $agent_index, $target_index );
680              
681             return if $target_index == -1;
682              
683             my $target = $self->{theArray}->cell( $target_index );
684              
685             if( $target->is_empty ) {
686             $self->log( "\tno agent to p0wn in cell $target_index" );
687             return;
688             }
689              
690             $self->log( "\tagent in cell $target_index p0wned" );
691             $target->set_owner( $self->{theArray}->cell( $agent_index )->get_owner );
692             }
693              
694             sub relative_to_absolute_position {
695             my( $self, $slotId, $shift ) = @_;
696             $shift ||= 0;
697              
698             if( abs( $shift ) > $self->{conf}{theArraySize} ) {
699             $self->log( "\tposition $shift out-of-bound" );
700             return -1;
701             }
702             $slotId += $shift + 2 * $self->{conf}{theArraySize};
703             $slotId %= $self->{conf}{theArraySize};
704              
705             return $slotId;
706             }
707              
708             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
709              
710             sub _alter_operation {
711             my ( $self, $agent_index, $target_index, $Array_ref ) = @_;
712              
713             my $abs_target_index = $self->relative_to_absolute_position( $agent_index, $target_index );
714             return if $abs_target_index == -1;
715              
716             my $target = $self->{theArray}->cell( $abs_target_index );
717            
718             if ( $target->is_empty ) {
719             $self->log( "\tno agent found at cell $abs_target_index" );
720             return;
721             }
722              
723             $target->set_code( $Array_ref->[$target_index] );
724             $self->log( "\tcode of agent in cell $abs_target_index altered" );
725             }
726              
727             sub _copy_operation {
728             my( $self, $agent_index, $source_index, $dest_index ) = @_;
729            
730             $source_index = $self->relative_to_absolute_position( $agent_index, $source_index );
731             $dest_index = $self->relative_to_absolute_position( $agent_index, $dest_index );
732            
733             # source or destination invalid? We do nothing
734             return if $source_index == -1 or $dest_index == -1;
735              
736             my $theArray = $self->{theArray};
737             my $target = $theArray->cell( $dest_index );
738             my $agent = $theArray->cell( $agent_index );
739            
740             if( $target->get_owner
741             and $target->get_owner ne $agent->get_owner )
742             {
743             $self->log( "\tagent in cell $dest_index already owned by ".
744             $target->get_owner );
745             return;
746             }
747              
748             $self->log( "\tagent of cell $source_index copied into cell $dest_index" );
749             $target->copy( $agent );
750             $target->set_operational( 0 );
751             }
752              
753             sub _noop_operation {
754             $_[0]->log( "\tno-op" );
755             }
756              
757             sub readCell {
758             my( $self, $cellId ) = @_;
759             return undef unless $self->{theArray}[$cellId];
760             return ( $self->{theArray}[$cellId]{owner}, $self->{theArray}[$cellId]{code} );
761             }
762              
763             sub writeCell {
764             my( $self, $pos, $owner, $code ) = @_;
765             $self->{theArray}[$pos] = { owner => $owner, code => $code };
766             }
767              
768             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
769             sub array {
770             return $_[0]->{theArray};
771             }
772             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
773              
774              
775             sub agent_census {
776             my( $self ) = @_;
777              
778             my %player = %{$self->{conf}{player}};
779              
780             my %census = $self->{theArray}->census;
781              
782             for my $p ( keys %player ) {
783             $player{$p}{agents} = $census{$p} || 0;
784             }
785              
786             return %census;
787             }
788              
789              
790             =begin notes
791              
792             my $pw = new Games::Perlwar;
793              
794             $pw->{interactive} = 1;
795             $pw->{theArray} = [ { owner => 'Yanick', name => 'Neo', code => 'print "Hello world!"' },
796             { owner => '1337 h4ck3r', name => 'crash me', code => 'exit' },
797             { owner => '1337 h4ck3r', name => 'readdir me', code => 'opendir DIR, "."; return readdir DIR;' },
798             { owner => '1337 h4ck3r', name => 'infinite loop', code => '1 while 1' },
799             { owner => '1337 h4ck3r', name => 'backticks', code => '`ls`' },
800             { owner => '1337 h4ck3r', name => 'kill next', code => '"!1"' },
801             { owner => '1337 h4ck3r', name => 'must die', code => '"I am still alive?"' },
802             { owner => 'Yanick', name => 'good boy', code => '1' },
803             { owner => 'Yanick', name => 'owner', code => '"~-1"' },
804             { owner => 'Yanick', name => 'too big', code => 'a' x 200 },
805             ];
806             $pw->{config}{arraySize} = @{ $pw->{theArray} };
807             $pw->{config}{maxSnippetSize} = 100;
808              
809             $pw->runSlot( $_ ) for 0..9;
810              
811             =end notes
812              
813             =cut
814              
815             1;
816              
817             __END__