#!/usr/bin/perl

use strict;
use IO::Socket;

# Globals
our $handle;
our $turn;
our ($width, $height, @map);
our ($id, $capacity, $money);
our %players;
our %packages;
our %packageshere;
our %packagescarried;
our @deliveryorder;
our @bases;
our %basepkgcount;
our %distances;
our (@oldradar, @radar);
our %watch;
our $dest;
our ($scorex, $scorey);
our $orders;
our $wanted;
our $bumped;

sub ReadBoard
{
	$_ = <$handle>;
	chomp;
	($width, $height) = split(/\s+/);
	for my $row (0..($height - 1))
	{
		$_ = <$handle>;
		chomp;

		my $line = $_;

		# Save base positions
		my $startcol = 0;
		my $basecol = index $_, "@", $startcol;
		while ($basecol >= $startcol) {
			my @pos = ($basecol+1, $row+1);
			my $pos = "$pos[0],$pos[1]";
			$bases[$#bases+1] = \@pos;
			# Assume lots of packages at each base
			$basepkgcount{$pos} = 999999;
			$startcol = $basecol + 1;
			$basecol = index $_, "@", $startcol;
		}
		# Just store bases as 'plain'
		$line =~ s/@/./g;
		@{$map[$row]} = split(//, $line);
	}

	#print "\nMap:\n";
	#for my $r (0..$height-1)
	#{
	#	for my $c (0..$width-1)
	#	{
	#		print "$map[$r][$c]";
	#	}
	#	print "\n";
	#}
	
	foreach $b (@bases)
	{
		#my $dest = join (",", @{$b});
		#print "Base at ($dest)\n";
		DistanceMap (${$b}[0], ${$b}[1]);
		#for my $r (0..$height-1)
		#{
		#	for my $c (0..$width-1)
		#	{
		#		if ($map[$r][$c] eq ".")
		#		{
		#			printf ("%1.1x", $distances{$dest}{'Map'}[$r][$c] %16);
		#		}
		#		else
		#		{
		#			print "$map[$r][$c]";
		#		}
		#	}
		#	print "\n";
		#}
	}
}


sub ReadConfig
{
	$_ = <$handle>;
	chomp;
	($id, $capacity, $money) = split(/\s+/);
	#print "I am $id with capacity $capacity and \$$money\n";
}

sub ReadResponse
{
	$_ = <$handle>;
	chomp;
	#print "Response: $_\n";
	# special code to stop when dead or game over
	if (/Robot (.*) died/)
	{
		if ($1 eq $id)
		{
			close $handle;
			exit;
		}
	}
	if (/Game over/)
	{
		close $handle;
		exit;
	}

	s/^\#//;
	my (@playerinfo) = split(/#/);
	my @pids = ();
	foreach my $p (@playerinfo)
	{
		my @response = split(/\s+/, $p);
		my $pid = shift @response;
		push @pids, $pid;
		while ($#response >= 0)
		{
			my $cmd = shift @response;
			if (!defined $cmd && $pid == $id)
			{
				$bumped+=5;
			}
			elsif ($cmd eq "N")			# move north
			{
				$players{$pid}{'CurrY'}++;
				if (($pid == $id) && ($wanted ne "N"))
				{
					$bumped+=5;
				}
				else
				{
					$bumped--;
					$bumped = 0 if ($bumped < 0);
				}
			}
			elsif ($cmd eq "S")		# move south
			{
				$players{$pid}{'CurrY'}--;
				if (($pid == $id) && ($wanted ne "S"))
				{
					$bumped+=5;
				}
				else
				{
					$bumped--;
					$bumped = 0 if ($bumped < 0);
				}
			}
			elsif ($cmd eq "E")		# move east
			{
				$players{$pid}{'CurrX'}++;
				if (($pid == $id) && ($wanted ne "E"))
				{
					$bumped+=5;
				}
				else
				{
					$bumped--;
					$bumped = 0 if ($bumped < 0);
				}
			}
			elsif ($cmd eq "W")		# move west
			{
				$players{$pid}{'CurrX'}--;
				if (($pid == $id) && ($wanted ne "W"))
				{
					$bumped+=5;
				}
				else
				{
					$bumped--;
					$bumped = 0 if ($bumped < 0);
				}
			}
			elsif ($cmd eq "P")		# pickup package
			{
				my $parm = shift @response;
				push @{$packagescarried{$pid}}, $parm;
				$packages{$parm}{'Carried'} = 1;
				$packages{$parm}{'CarriedBy'} = $pid;
				$bumped--;
				$bumped = 0 if ($bumped < 0);
				# this location now has one less package
				my $pos = "$players{$pid}{'CurrX'},$players{$pid}{'CurrY'}";
				$basepkgcount{$pos}--;
				if ($basepkgcount{$pos} == 0)
				{
					my $b = 0;
					while ($b <= $#bases)
					{
						if ($bases[$b][0] == $players{$pid}{'CurrX'} && $bases[$b][1] == $players{$pid}{'CurrY'})
						{
							#print "Base ($bases[$b][0],$bases[$b][1]) is out of packages!\n";
							splice @bases, $b, 1;
							last;
						}
						$b++;
					}
				}
			}
			elsif ($cmd eq "D")		# drop package
			{
				my $parm = shift @response;
				@{$packagescarried{$pid}} = grep (!/^$parm$/, @{$packagescarried{$pid}});
				@deliveryorder = grep (!/^$parm$/, @deliveryorder);
				$packages{$parm}{'Carried'} = 0;
				$packages{$parm}{'CurrX'} = $players{$pid}{'CurrX'};
				$packages{$parm}{'CurrY'} = $players{$pid}{'CurrY'};

				# this location is now a base with one more pkgcount
				my $pos = "$players{$pid}{'CurrX'},$players{$pid}{'CurrY'}";
				$basepkgcount{$pos} = 0 if (!defined $basepkgcount{$pos});
				$basepkgcount{$pos}++;
				# if not already a base, add it
				if ($basepkgcount{$pos} == 1)
				{
					my @pos = ($players{$pid}{'CurrX'}, $players{$pid}{'CurrY'});
					$bases[$#bases+1] = \@pos;
					DistanceMap($players{$pid}{'CurrX'}, $players{$pid}{'CurrY'});
				}

			}
			elsif ($cmd eq "X")		# set X position
			{
				my $parm = shift @response;
				$players{$pid}{'CurrX'} = $parm;
			}
			elsif ($cmd eq "Y")		# set Y position
			{
				my $parm = shift @response;
				$players{$pid}{'CurrY'} = $parm;
			}
		}
		$players{$pid}{'TurnX'}[$turn] = $players{$pid}{'CurrX'};
		$players{$pid}{'TurnY'}[$turn] = $players{$pid}{'CurrY'};
	}
	# Get rid of data on players that have disappeared (died)
	# player entry
	foreach my $pid (keys %players)
	{
		if (!grep ($_ eq $pid, @pids))
		{
			delete $players{$pid} if (!grep ($_ eq $pid, @pids));
			# packages carried by dead robots are lost
			foreach my $pkgid (@{$packagescarried{$pid}}) {
				delete $packages{$pkgid};
			}
			delete $packagescarried{$pid};
		}
	}
}

sub sortbyscore
{
	# Sort by decreasing score
	if (my $s = GetPackageScoreFrom($b, $scorex, $scorey) <=> GetPackageScoreFrom ($a, $scorex, $scorey))
	{
		return $s;
	}
	# if equal, sort by increasing distance
	else
	{
		my $name = "$scorex,$scorey";
		if (my $d = $distances{$name}{'Map'}[$packages{$a}{'DestY'}-1][$packages{$a}{'DestX'}-1] <=> $distances{$name}{'Map'}[$packages{$b}{'DestY'}-1][$packages{$b}{'DestX'}-1])
		{
			return $d;
		}
		# if distance also equal sort by x value of destination
		else
		{
			if (my $x = $packages{$a}{'DestX'} <=> $packages{$b}{'DestX'})
			{
				return $x;
			}
			elsif (my $y = $packages{$a}{'DestY'} <=> $packages{$b}{'DestY'})
			{
				return $y;
			}
			else
			{
				return $packages{$a}{'Weight'} <=> $packages{$b}{'Weight'};
			}
		}
	}
}

sub GetPackageScoreFrom
{
	my $pkgid = shift;
	my $x = shift;
	my $y = shift;
	my $destx = $packages{$pkgid}{'DestX'};
	my $desty = $packages{$pkgid}{'DestY'};
	my $name = "$packages{$pkgid}{'DestX'},$packages{$pkgid}{'DestY'}";
	my $dist = $distances{$name}{'Map'}[$y-1][$x-1];
	# Total weight of all packages going to same destination, as all can be delivered at once (up to $capacity)
	my $totalweight = 0;
	foreach my $pkgid1 (keys %packages)
	{
		if (($packages{$pkgid1}{'DestX'} == $destx) && ($packages{$pkgid}{'DestY'} == $desty))
		{
			$totalweight += $packages{$pkgid1}{'Weight'};
		}
	}
	my $score;
	if ($dist == 0)
	{
		$score = 999999;
	}
	else
	{
		$score = $totalweight / $dist;
	}
	# Give closer packages an extra bonus
	my $increment = ($width + $height) / 8;
	if ($dist < $increment)
	{
		$score *= 1.2;
	}
	elsif ($dist < 2 * $increment)
	{
		$score *= 1.1;
	}
	return $score;
}

sub ReadPackages
{
	$_ = <$handle>;
	chomp;
	my $packagecount = 0;
	undef %packageshere;
	my @pkglist = split(/\s+/);
	while (my $pkgid = shift @pkglist)
	{
		$packagecount++;
		$packageshere{$pkgid}{'DestX'} = shift @pkglist;
		$packageshere{$pkgid}{'DestY'} = shift @pkglist;
		$packageshere{$pkgid}{'Weight'} = shift @pkglist;

		$packages{$pkgid}{'CurrX'} = $players{$id}{'CurrX'};
		$packages{$pkgid}{'CurrY'} = $players{$id}{'CurrY'};
		$packages{$pkgid}{'DestX'} = $packageshere{$pkgid}{'DestX'};
		$packages{$pkgid}{'DestY'} = $packageshere{$pkgid}{'DestY'};
		$packages{$pkgid}{'Weight'} = $packageshere{$pkgid}{'Weight'};

		# figure out how far everything is from package destination
		DistanceMap ($packages{$pkgid}{'DestX'}, $packages{$pkgid}{'DestY'});
	}
	my $pos = "$players{$id}{'CurrX'},$players{$id}{'CurrY'}";
	$basepkgcount{$pos} = $packagecount;
	return $packagecount;
}

# If robot is on a base square but there are no packages here then
# this base has run out of goods.  Remove it from the base list.
sub RemoveInactiveBase
{
	# Is robot on square with no packages?
	my @pkgshere = keys %packageshere;
	if ($#pkgshere == -1)
	{
		# Is robot on base?
		my $x = $players{$id}{'CurrX'};
		my $y = $players{$id}{'CurrY'};

		my $b = 0;
		while ($b <= $#bases)
		{
			if ($bases[$b][0] == $x && $bases[$b][1] == $y)
			{
				#print "Base ($bases[$b][0],$bases[$b][1]) is out of packages!\n";
				splice @bases, $b, 1;
				last;
			}
			$b++;
		}
	}
}

# sort bases in ascending order by distance from my current position
sub sortbydist
{
	my $aname = "${$a}[0],${$a}[1]";
	my $bname = "${$b}[0],${$b}[1]";
	return ($distances{$aname}{'Map'}[$players{$id}{'CurrY'}-1][$players{$id}{'CurrX'}-1] <=> $distances{$bname}{'Map'}[$players{$id}{'CurrY'}-1][$players{$id}{'CurrX'}-1]);
}

sub DropOff
{
	my @pkgstodrop;
	my $x = $players{$id}{'CurrX'};
	my $y = $players{$id}{'CurrY'};
	# If robot is on a square that is a package destination, drop it.
	foreach my $pkgid (@{$packagescarried{$id}})
	{
		push @pkgstodrop, $pkgid if ($x == $packages{$pkgid}{'DestX'} && $y == $packages{$pkgid}{'DestY'});
	}
	if ($#pkgstodrop != -1)
	{
		$orders = "1 Drop " . join " ", @pkgstodrop;
		#print "DropOff wants to: $orders\n";
	}
	# Review delivery order based on new location
	$scorex = $players{$id}{'CurrX'};
	$scorey = $players{$id}{'CurrY'};
	@deliveryorder = sort sortbyscore @deliveryorder;
}

sub PickUp
{
	# Robot is on a square with some packages.  Pick some up?  Which ones?

	# Sort packages by score/dist score for each package.  Give a bonus for
	# closer deliveries - it is an uncertain world and might be dangerous
	# to head off into the distance...

	my %packagesleft = %packageshere;
	my $carried = 0;
	foreach my $pkgid (@{$packagescarried{$id}})
	{
		$carried += $packages{$pkgid}{'Weight'};
	}

	$scorex = $players{$id}{'CurrX'};
	$scorey = $players{$id}{'CurrY'};

	my $count = 1;
	my @pickup = ();
	while ($count > 0)
	{
		$count = 0;
		my @pkgs = sort sortbyscore keys %packagesleft;

		# Go through list until a package small enough to be carried
		# is found.  Then pick it up and as many more for that location
		# as possible.
		
		my ($destx, $desty) = (0,0);
		# Pick up all packages with same destination
		foreach my $pkgid (@pkgs)
		{
			if (($packages{$pkgid}{'Weight'} + $carried <= $capacity) && ((($destx == $desty) && ($destx == 0)) || ($destx == $packages{$pkgid}{'DestX'} && $desty == $packages{$pkgid}{'DestY'})))
			{
				push @pickup, $pkgid;
				push @deliveryorder, $pkgid;
				delete $packagesleft{$pkgid};
				$destx = $packages{$pkgid}{'DestX'};
				$desty = $packages{$pkgid}{'DestY'};
				$carried += $packages{$pkgid}{'Weight'};
				$count++;
			}
		}
		$scorex = $destx;
		$scorey = $desty;
	}
	if ($#pickup != -1)
	{
		$orders = "1 Pick " . join(" ", @pickup);
	}
	$scorex = $players{$id}{'CurrX'};
	$scorey = $players{$id}{'CurrY'};
	@deliveryorder = sort sortbyscore @deliveryorder;
}

sub PickADelivery
{
	# Robot has packages.  Select a package destination to head for.
	# Just go to first destination in @deliveryorder
	$dest = "$packages{$deliveryorder[0]}{'DestX'},$packages{$deliveryorder[0]}{'DestY'}";
	#print "In PickADelivery dest = $dest\n";
	$orders = MoveTo($dest);
}

sub PickABase
{
	# Robot has no packages, no other robots around.  Look for a good
	# base to head for.

	$dest = "";
	# Only one base.  Head for it.
	if ($#bases == 0)
	{
		$dest = "$bases[0][0],$bases[0][1]";
		#print "PickABase - Only one base at $dest\n";
	}

	if ($dest eq "")
	{
		# Is there a base that I am closer to than any other robot?
		# Find the closest base for which I am nearest robot.
		my @basesbydist = sort sortbydist @bases;
		foreach my $b (@basesbydist)
		{
			# who is next closest to base?
			my $closest = -1;
			my $dist = 999999;
			my $name = "${$b}[0],${$b}[1]";
			foreach my $pid (keys %players)
			{
				if ($pid != $id)
				{
					my $playerdist = $distances{$name}{'Map'}[$players{$pid}{'CurrY'}-1][$players{$pid}{'CurrX'}-1];
					if ($playerdist < $dist)
					{
						$closest = $pid;
						$dist = $playerdist;
					}
				}
			}
			if ($dist + 1 > $distances{$name}{'Map'}[$players{$id}{'CurrY'}-1][$players{$id}{'CurrX'}-1])
			{
				$dest = $name;
				last;
			}
		}

		# I am not the closest to any base.  Just go to closest base.
		$dest = "$basesbydist[0][0],$basesbydist[0][1]";
	}
	#print "In PickABase dest = $dest\n";
	$orders = MoveTo($dest);
}

sub MoveTo
{
	my $goto = shift;
	# Navigate to selected destination
	my $x = $players{$id}{'CurrX'};
	my $y = $players{$id}{'CurrY'};
	my $currdist = $distances{$goto}{'Map'}[$y-1][$x-1];
	my @choices = ();

	# West?
	if (($x - 1 > 0) && ($distances{$goto}{'Map'}[$y-1][$x-2] < $currdist))
	{
		push @choices, "W";
	}

	# East?
	if (($x < $width) && ($distances{$goto}{'Map'}[$y-1][$x] < $currdist))
	{
			push @choices, "E";
	}

	# South?
	if (($y - 1 > 0) && ($distances{$goto}{'Map'}[$y-2][$x-1] < $currdist))
	{
			push @choices, "S";
	}

	# North?
	if (($y < $height) &&($distances{$goto}{'Map'}[$y][$x-1] < $currdist))
	{
			push @choices, "N";
	}

	# There should always be at least one choice
	if ($#choices == -1)
	{
		$orders = "1 Move S";
	}
	else
	{
		my $pick = int (rand() * ($#choices + 1));
		$orders =  "1 Move $choices[$pick]";
	}
}
sub SendOrders
{
	$orders = "";

	# are there any other robots in my vicinity?  Yikes!
	#CheckRadar();

	# Any packages ready for drop off?
	if ($orders eq "")
	{
		DropOff();
	}
	# Am I on a square with one or more packages available for pickup?
	# Maybe pick one up...
	my @pkgshere = keys %packageshere;
	if ($orders eq "" && $#pkgshere > -1)
	{
		PickUp();
		#print "PickUp wants to: $orders\n";
	}
	# If I am carrying packages, head for a package destination
	my @pkgscarried = @{$packagescarried{$id}};
	if ($orders eq "" && $#pkgscarried > -1)
	{
		PickADelivery();
		#print "PickADelivery wants to: $orders\n";
	}
	# If I have no packages, head for a base
	if ($orders eq "")
	{
		PickABase();
		#print "PickABase wants to: $orders\n";
	}

	# Modify orders to handle bump conditions
	# Can I be bumped at new location?
	# It would really be nice to adjust my path instead of this 
	# lame stuff.
	if ($orders =~ /Move (.)/)
	{
		my $dir = $1;
		my $newx = $players{$id}{'CurrX'};
		my $newy = $players{$id}{'CurrY'};
		my $newy++ if ($dir eq "N");
		my $newx++ if ($dir eq "E");
		my $newy-- if ($dir eq "S");
		my $newx-- if ($dir eq "W");
		my $bval = Bumpable ($newx, $newy);
		#print "Bumpable at desired destination ($newx,$newy) = $bval\n" if ($bval != 0);
		if ($bval > 0)
		{
			$orders =~ s/^-*\d+/-3/;
		}
		if ($bval < 0)
		{
			$orders =~ s/^-*\d+/-10/;
		}
	}

	my $bval = Bumpable ($players{$id}{'CurrX'}, $players{$id}{'CurrY'});
	if ($bval != 0)
	{
		#print "Bumpable at current location = $bval\n" if ($bval !=0);
		my $x = $players{$id}{'CurrX'};
		my $y = $players{$id}{'CurrY'};
		# get list of available moves
		my @choices = ();
		push @choices, "N" if (($y < $height) && ($map[$y][$x-1] eq '.'));
		push @choices, "E" if (($x < $width) && ($map[$y-1][$x] eq '.'));
		push @choices, "S" if (($y > 1) && ($map[$y-2][$x-1] eq '.'));
		push @choices, "W" if (($x > 1) && ($map[$y-1][$x-2] eq '.'));
		# fatal bump possible!  Change to high priority dodge or pushback
		if (($bval < 0) || ($bval > 0 && $bumped > 0))
		{
			my $spend = int ($money / 4);
			my $odir;
			$odir = "N" if (abs ($bval) == 1);
			$odir = "E" if (abs ($bval) == 2);
			$odir = "S" if (abs ($bval) == 3);
			$odir = "W" if (abs ($bval) == 4);
			
			@choices = grep (!/$odir/, @choices);
			if ($#choices == -1)
			{
				$orders = "$spend Move $odir";
			}
			else
			{
				my $pick = int (rand() * ($#choices + 1));
				$orders = "$spend Move $choices[$pick]";
			}
		}
		# non fatal bump possible.  Increase priority of multi-package pickup
		# or drop
		if ($bval > 0)
		{
			my $spend = int ($money / 10);
			if ($orders =~ /(Pick|Drop) (.*)/)
			{
				my $cmd = $1;
				my $pkgs = $2;
				my @pkgs = split(/\s+/, $pkgs);
				if ($cmd eq "Pick" && $#pkgs > 0)
				{
					$orders = "$spend Pick $pkgs";
				}
				my @mypkgs = @{$packagescarried{$id}};
				if ($cmd eq "Drop" && $#mypkgs > 0)
				{
					$orders = "$spend Drop $pkgs";
				}
			}
		}
	}

	#print "Sending Orders: $orders\n";

	print $handle "$orders\n";
	my $remainder;
	my $spent;
	($spent, $remainder) = split (/\s+/, $orders, 2);
	$money -= abs($spent);

	if ($remainder =~ /Move (.)/)
	{
		$wanted = $1;
	}
	elsif ($remainder =~ /Pick/)
	{
		$wanted = "P";
	}
	else
	{
		$wanted = "D";
	}
	#print "Sending Orders: $orders\n";
}

sub DistanceMap
{
	my $x = shift;
	my $y = shift;
	my $name = "$x,$y";

	my $range;
	my ($minx, $miny, $maxx, $maxy);
	if (defined ($range = shift))
	{
		$minx = $x - $range;
		$minx = 1 if ($minx < 1);
		$miny = $y - $range;
		$miny = 1 if ($miny < 1);
		$maxx = $x + $range;
		$maxx = $width if ($maxx > $width);
		$maxy = $y + $range;
		$maxy = $height if ($maxy > $height);
	}
	else
	{
		$range = 999999;
		$minx = 1;
		$miny = 1;
		$maxx = $width;
		$maxy = $height;
	}

	# don't make the same map twice!
	if (!defined $distances{$name} || $distances{$name}{'Range'} < $range)
	{
		$distances{$name}{'Range'} = $range;
		# initialize map
		for my $r ($miny-1..$maxy-1)
		{
			for my $w ($minx-1..$maxx-1)
			{
				$distances{$name}{'Map'}[$r][$w] = 999999;
			}
		}
	
		my $dist = 0;
		$distances{$name}{'Map'}[$y-1][$x-1] = 0;
		my @distlistx = ($x);
		my @distlisty = ($y);
	
		my @olddistlistx;
		my @olddistlisty;
	
		# still points to process?
		while ($#distlistx >= 0 && $dist < $range)
		{
			$dist++;
			@olddistlistx = @distlistx;
			@olddistlisty = @distlisty;
			undef @distlistx;
			undef @distlisty;
			for my $p (0..$#olddistlistx)
			{
				my $dx = $olddistlistx[$p];
				my $dy = $olddistlisty[$p];
				DistancePoint ($name, $dx, $dy, $dist, \@distlistx, \@distlisty);
			}
		}
	}
}

# Mark passable points around $x, $y as distance $d from start point on map
# $name.  Add points marked to $listxref and $listyref arrays so they can be
# inspected during the next round of distance mapping.
sub DistancePoint
{
	my ($name, $x, $y, $d, $listxref, $listyref) = @_;

	if ($x - 1 > 0)
	{
		if (($distances{$name}{'Map'}[$y-1][$x-2] == 999999) && ($map[$y-1][$x-2] eq '.'))
		{
			$distances{$name}{'Map'}[$y-1][$x-2] = $d;
			push @{$listxref}, $x-1;
			push @{$listyref}, $y;
		}
	}

	if ($x < $width)
	{
		if (($distances{$name}{'Map'}[$y-1][$x] == 999999) && ($map[$y-1][$x] eq '.'))
		{
			$distances{$name}{'Map'}[$y-1][$x] = $d;
			push @{$listxref}, $x+1;
			push @{$listyref}, $y;
		}
	}

	if ($y - 1 > 0)
	{
		if (($distances{$name}{'Map'}[$y-2][$x-1] == 999999) && ($map[$y-2][$x-1] eq '.'))
		{
			$distances{$name}{'Map'}[$y-2][$x-1] = $d;
			push @{$listxref}, $x;
			push @{$listyref}, $y-1;
		}
	}

	if ($y < $height)
	{
		if (($distances{$name}{'Map'}[$y][$x-1] == 999999) && ($map[$y][$x-1] eq '.'))
		{
			$distances{$name}{'Map'}[$y][$x-1] = $d;
			push @{$listxref}, $x;
			push @{$listyref}, $y+1;
		}
	}

}

# Is there someone in position to bump before I make my move?
# 0 = no, 1 = from N, 2 = E, 3 = S, 4 = W.
# negative number if push wuold be fatal
# if more than one pusher, too ad (but return fatal
# pusher before non-fatal)
sub Bumpable
{
	my $x = shift;
	my $y = shift;
	my $retval = 0;

	foreach my $n (keys %players)
	{
		# don't check myself
		last if ($n == $id);

		# don't check unless next to me
		my ($nx, $ny) = ($players{$n}{'CurrX'}, $players{$n}{'CurrY'});
		last if ((abs($nx-$x) + abs($ny-$y)) > 1);

		# opponent to north
		if (($y > 1) && ($y == $ny - 1))
		{
			$retval = 1 if ($retval >= 0);
			$retval = -1 if ($map[$y-2][$x-1] eq "~");
		}
		# opponent to east
		if (($x > 1) && ($x == $nx - 1))
		{
			$retval = 2 if ($retval >= 0);
			$retval = -2 if ($map[$y-1][$x-2] eq "~");
		}
		# opponent to south
		if (($y < $height) && ($y == $ny + 1))
		{
			$retval = 3 if ($retval >= 0);
			$retval = -3 if ($map[$y][$x-1] eq "~");
		}
		# opponent to west
		if (($x < $width) && ($x == $nx + 1))
		{
			$retval = 4 if ($retval >= 0);
			$retval = -4 if ($map[$y-1][$x] eq "~");
		}
	}
}
sub CheckRadar
{
	my $range = 3;
	undef @radar;
	my $x = $players{$id}{'CurrX'};
	my $y = $players{$id}{'CurrY'};
	# make list of possibles - those robots with $range squares ignoring obstacles
	my @possibles = ();
	foreach my $pid (keys %players)
	{
		if ($pid != $id)
		{
			my $rx = $players{$pid}{'CurrX'};
			my $ry = $players{$pid}{'CurrY'};
			push @possibles, $pid if (abs ($x - $rx) <= $range && abs ($y - $ry) <= $range);
		}
	}
	# If there is anyone to watch, make up a map to check their distance
	if ($#possibles > -1)
	{
		DistanceMap($x, $y, $range);
	}
	my $name = "$x,$y";
	# @near are really close, taking obstacles into account
	my @near = grep ($distances{$name}{'Map'}[$players{$_}{'CurrY'}-1][$players{$_}{'CurrX'}-1] <= $range, @possibles);

	# Near robot implies emergency action may be required.  This part of the
	# code could really use some improvement...
	if ($#near != -1)
	{
		# if opponent has me backed up to water, try to dodge.  If
		# not possible, move to opponent's square with high priority

		my $spend = int ($money / 4);
		foreach my $n (@near)
		{
			# opponent to east
			if (($x > 1) && ($x == $players{$n}{'CurrX'} - 1) && (($map[$y-1][$x-2] eq '~') || ($bumped > 2))) {
				# try north
				if (($y < $height) && ($map[$y][$x-1] eq '.'))
				{
					$orders = "$spend Move N";
				}
				# south
				elsif (($y > 1) && ($map[$y-2][$x-1] eq '.'))
				{
					$orders = "$spend Move S";
				}
				# push him back!
				else
				{
					$orders = "$spend Move E";
				}
			}
			# opponent to west
			if (($x < $width - 1) && ($x == $players{$n}{'CurrX'} + 1) && (($map[$y-1][$x] eq '~') || ($bumped > 2))) {
				# try north
				if (($y < $height) && ($map[$y][$x-1] eq '.'))
				{
					$orders = "$spend Move N";
				}
				# south
				elsif (($y > 1) && ($map[$y-2][$x-1] eq '.'))
				{
					$orders = "$spend Move S";
				}
				# push him back!
				else
				{
					$orders = "$spend Move W";
				}
			}
			# opponent to north
			if (($y > 1) && ($y == $players{$n}{'CurrY'} - 1) && (($map[$y-2][$x-1] eq '~') || ($bumped > 2))) {
				# try east
				if (($x < $width) && ($map[$y-1][$x] eq '.'))
				{
					$orders = "$spend Move E";
				}
				# west
				elsif (($x > 1) && ($map[$y-1][$x-2] eq '.'))
				{
					$orders = "$spend Move W";
				}
				# push him back!
				else
				{
					$orders = "$spend Move N";
				}
			}
			# opponent to north
			if (($y < $height - 1) && ($y == $players{$n}{'CurrY'} + 1) && (($map[$y][$x-1] eq '~') || ($bumped > 2))) {
				# try east
				if (($x < $width) && ($map[$y-1][$x] eq '.'))
				{
					$orders = "$spend Move E";
				}
				# west
				elsif (($x > 1) && ($map[$y-1][$x-2] eq '.'))
				{
					$orders = "$spend Move W";
				}
				# push him back!
				else
				{
					$orders = "$spend Move S";
				}
			}
		}
	}
}

die "Usage: $0 server port\n" if ($#ARGV != 1);

my ($server, $port) = @ARGV;

$handle = IO::Socket::INET->new (Proto	=> "tcp",
	PeerAddr => $server,
	PeerPort => $port)
	or die "Can't connect to port $port on $server: $!";
$handle->autoflush(1);

srand;

print $handle "Player\n";

$turn = 0;

ReadBoard;
ReadConfig;
# Get initial positions
ReadResponse;

@deliveryorder = ();
$bumped = 0;

# Main Loop
while (1)
{
	ReadPackages;

	RemoveInactiveBase;

	SendOrders;

	ReadResponse;

	#foreach my $pid (keys %players)
	#{
	#	print "Player $pid is at ($players{$pid}{'CurrX'},$players{$pid}{'CurrY'})\n";
	#}
}

close $handle;
