#!/usr/bin/perl -w package SimulatorInterface; use strict; use utf8; use lib '.'; use PiratePort; use vars qw(@ISA); @ISA = qw(Interface); sub new { my $self = shift->SUPER::new(@_); $self->{shipCount} = 0; $self->{ships} = { }; $self->{map} = { }; my $file; print "Loading planets...\n"; open($file, '<', 'data.planets') or die "Couldn't open 'data.planets': $!"; while (defined($_ = <$file>)) { m/^\| +(\d+) \| +(\d+) \| +(\d+) \| (.+?) +\| +(\d+|NULL) \|$/o or die "Couldn't read 'data.planets': syntax error on line $."; my($id, $x, $y, $name, $owner) = ($1, $2, $3, $4, $5); $self->{map}->{"$x,$y"} = { type => 'planet', name => $name, owner => $owner eq 'NULL' ? undef : $owner, id => $id, }; if ($owner eq 'NULL') { $self->{homeX} = $x; $self->{homeY} = $y; } } close($file); print "Loading anomalies...\n"; open($file, '<', 'data.anomalies') or die "Couldn't open 'data.anomalies': $!"; while (defined($_ = <$file>)) { m/^\| +(\d+) \| +(\d+) \| +(\d+) \| +(\d+) \|$/o or die "Couldn't read 'data.anomalies': syntax error on line $."; my($id, $x, $y, $type) = ($1, $2, $3, $4); $self->{map}->{"$x,$y"} = { type => $type == 10 ? 'star' : 'unknown', id => $id, }; } close($file); return $self; } sub canLoad { my($self) = @_; return -f 'data.ships'; } sub loadData { my($self) = @_; print "Loading universe....\n"; open(my $file, '<', 'data.ships') or die "Couldn't open 'data.ships': $!"; my $mode = (stat $file)[2]; if ($mode & 00066 != 0) { die 'Won\'t process \'data.ships\': File is group or world writable'; } local $/ = undef; my $data = <$file>; close($file); $data = eval $data; if ($@) { die "Couldn't read 'data.ships': $@"; } $self->{shipCount} = $data->{shipCount}; $self->{ships} = $data->{ships}; return $data->{data}; } sub saveData { my($self, $data) = @_; use Data::Dumper; print "Saving universe...\n"; # local $Data::Dumper::Indent = 0; local $Data::Dumper::Terse = 1; open(my $file, '>', 'data.ships') or die "Couldn't open 'data.ships': $!"; print $file Dumper({ data => $data, shipCount => $self->{shipCount}, ships => $self->{ships}, }) or die "Couldn't write to 'data.ships': $!"; close($file); } sub spawnShip { my($self) = @_; my $id = ++$self->{shipCount}; $self->{ships}->{$id} = { x => $self->{homeX}, y => $self->{homeY}, orders => [], fuel => 100, health => 100, xp => 0, id => $id, }; print "Spawning ship $id at ($self->{homeX}, $self->{homeY})...\n"; return ($id, $self->{homeX}, $self->{homeY}); } sub reapShip { my($self, $id) = @_; delete($self->{ships}->{$id}); } sub nukeShip { my($self, $id) = @_; $self->{ships}->{$id}->{health} -= 10; } sub isShipActive { my($self, $id) = @_; return ($self->{ships}->{$id}->{health} > 0 and $self->{ships}->{$id}->{fuel} > 0); } sub getShipFuel { my($self, $id) = @_; return $self->{ships}->{$id}->{fuel}; } sub getShipHealth { my($self, $id) = @_; return $self->{ships}->{$id}->{health}; } sub getShipPosition { my($self, $id) = @_; return ($self->{ships}->{$id}->{x}, $self->{ships}->{$id}->{y}); } sub getShipExperience { my($self, $id) = @_; return $self->{ships}->{$id}->{xp}; } sub isEmptySpace { my($self, $x, $y) = @_; return not defined $self->{map}->{"$x,$y"}; } sub isStar { my($self, $x, $y) = @_; return (not $self->isEmptySpace($x, $y) and $self->{map}->{"$x,$y"}->{type} eq 'star'); } sub isNeutralPlanet { my($self, $x, $y) = @_; return (not $self->isEmptySpace($x, $y) and $self->{map}->{"$x,$y"}->{type} eq 'planet' and not defined $self->{map}->{"$x,$y"}->{owner}); } sub isEvilPlanet { my($self, $x, $y) = @_; return (not $self->isEmptySpace($x, $y) and $self->{map}->{"$x,$y"}->{type} eq 'planet' and defined $self->{map}->{"$x,$y"}->{owner}); } # not implemented # sub isEvilFleet {} # sub isEvilStation {} # sub isAlliedFleet {} sub moveShip { my($self, $id, $dx, $dy) = @_; if (@{$self->{ships}->{$id}->{orders}} != 2 or $self->{ships}->{$id}->{orders}->[0] != $dx or $self->{ships}->{$id}->{orders}->[1] != $dy) { $self->{ships}->{$id}->{orders} = [$dx, $dy]; } } sub doShipAction { my($self, $id, $dx, $dy) = @_; if (@{$self->{ships}->{$id}->{orders}} != 1 or $self->{ships}->{$id}->{orders}->[0] ne 'action') { $self->{ships}->{$id}->{orders} = ['action']; } } sub sleep { my($self) = @_; foreach my $ship (values %{$self->{ships}}) { if (@{$ship->{orders}}) { $ship->{fuel} -= 4; $ship->{health} += 5; if ($ship->{orders}->[0] eq 'action') { if ($self->isEvilPlanet($ship->{x}, $ship->{y}) or $self->isEvilStation($ship->{x}, $ship->{y}) or $self->isEvilFleet($ship->{x}, $ship->{y})) { print "Ship $ship->{id} attacking at ($ship->{x}, $ship->{y}).\n"; $ship->{xp} += 1; } } else { $ship->{x} += $ship->{orders}->[0]; $ship->{y} += $ship->{orders}->[1]; while ($ship->{x} > 99) { $ship->{x} -= 100; } while ($ship->{x} < 0) { $ship->{x} += 100; } while ($ship->{y} > 99) { $ship->{y} -= 100; } while ($ship->{y} < 0) { $ship->{y} += 100; } } } if ($self->isStar($ship->{x}, $ship->{y})) { print "Refuelling ship $ship->{id} at ($ship->{x}, $ship->{y})...\n"; $ship->{health} -= 15; $ship->{fuel} += 20; } $ship->{fuel} = 100 if $ship->{fuel} > 100; $ship->{health} = 100 if $ship->{health} > 100; } select undef, undef, undef, 0.01; } sub reportShipObituary { my($self, $id, $score, $age, $index) = @_; my($dx, $dy) = ($self->{homeX} - $self->{ships}->{$id}->{x}, $self->{homeY} - $self->{ships}->{$id}->{y}); my $distance += int sqrt($dx * $dx + $dy * $dy); printf "Ship %6d went distance %3d ending with fuel %3d and health %3d, aged %3d, ending with XP %2d; Final score: %5d; Position: $index.\n", $id, $distance, $self->{ships}->{$id}->{fuel}, $self->{ships}->{$id}->{health}, $age, $self->{ships}->{$id}->{xp}, $score, $index; } package main; use strict; use utf8; PiratePort::run(SimulatorInterface->new());