123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882 |
- #!/usr/bin/perl
- # TODO: More work on AI, make it smarter and less random
- # ** Keep track of where it's already missed and whether or not opponent moves
- # TODO: Handle situation where player or AI can place ships that 'wrap' around the map, ie coordinates
- # like 20,21,22 which would place the end of a cruiser in the first row, and the next two sections of it
- # in the second row. This doesn't really break the game at all, but it does look weird on the map and doesn't seem
- # to be a mature implimentation if it exists
- # TODO: Handle the fact that player can input random coordinates so that they could potentially have 1 third
- # of a ship in 3 different coordinates, or just have a ship occupy 1 tile by entering the same coordinate
- # TODO: 'Productionize' the code: error handling, more input sanitation, etc
- # ** Optimze placement so we dont have to check it each time, ie check at placement
- # ** Consolidate redundant subs
- # TODO: Improve readability, game play feel
- #
- # KNOWN BUGS:
- # TODO: &clearUnocTiles issue -- see sub comment
- # ** Not sure this is really an issue, but leaving it here to remind myself anyways
- # Basic implimentation of 'battleship' to teach myself more about programming
- # I don't know the actual rules of the game, this is my stab at
- # something in the 'spirit' of it
- #
- # Player takes turns against computer trying to hit one of their ships.
- # Can only perform 1 action per turn:
- # - Move
- # - Attack
- #
- # Three types of ships:
- # * Cruiser
- # - Hull Points: 2
- # - Size: 3x1
- # - Attack Power: 1
- # * Carrier
- # - Hull Points: 3
- # - Size: 5x1
- # - Attack Power: 2
- # * Submarine
- # - Hull Points: 1
- # - Size 2x1
- # - Attack Power: 3
- #
- # 5x5 map grid for each player
- # Cruiser = *
- # Carrier = @
- # Submarine = ~
- # Ocean/Empty Space = .
- use strict;
- use warnings;
- use lib "/home/swatson/Repos/battleship-perl";
- #use MapTools;
- use Term::ANSIColor qw(:constants);
- my $version = 0.1;
- if ( $ARGV[0] && $ARGV[0] =~ /version/ ) {
- print "$version\n";
- exit 0;
- }
- # Maps
- my %p1map;
- my %p2map;
- # Stats trackers
- my @p1Attacks;
- my @p2Attacks;
- # Ships - surely there is a better way to do this
- my %p1cruiser = ( 'hp' => '2', 'size' => '3', 'ap' => '1', 'loc' => '', 'sym' => '*', 'mc' => 0 );
- my %p1carrier = ( 'hp' => '3', 'size' => '5', 'ap' => '2', 'loc' => '', 'sym' => '@', 'mc' => 0 );
- my %p1subm = ( 'hp' => '1', 'size' => '2', 'ap' => '3', 'loc' => '', 'sym' => '~', 'mc' => 0 );
- my %p1ships = ( 'cru' => \%p1cruiser, 'car' => \%p1carrier, 'subm' => \%p1subm );
- my %p2cruiser = ( 'hp' => '2', 'size' => '3', 'ap' => '1', 'loc' => '', 'sym' => '*', 'mc' => 0 );
- my %p2carrier = ( 'hp' => '3', 'size' => '5', 'ap' => '2', 'loc' => '', 'sym' => '@', 'mc' => 0 );
- my %p2subm = ( 'hp' => '1', 'size' => '2', 'ap' => '3', 'loc' => '', 'sym' => '~', 'mc' => 0 );
- my %p2ships = ( 'cru' => \%p2cruiser, 'car' => \%p2carrier, 'subm' => \%p2subm );
- sub initMap {
- foreach my $number ( 1 .. 50 ) {
-
- $p1map{$number} = ".";
- $p2map{$number} = ".";
- }
- }
- sub clearUnocTiles {
- # Bug where sometimes after a ship is moved one of the old tiles it was on
- # is not reset despite the &shipPosition function reporting that it is
- # Thus far, I've been unable to figure out why that is happening, so
- # for now am providing this function, which will check the location of all ships
- # and reset any incorrect tiles for both the player and the AI
-
- my @p1usedTiles;
- my @p2usedTiles;
- # Get in use tiles for ship hashes
- foreach my $ship ( keys %p1ships ) {
- if ( ! $p1ships{$ship} ) {
- next;
- }
- my $shipRef = $p1ships{$ship};
- my $location = ${$shipRef}{loc};
- my @inUseTiles = split(",", $location);
- foreach my $iut ( @inUseTiles ) {
- push(@p1usedTiles, $iut);
- }
-
- }
- # Clean the tiles
- foreach my $key ( keys %p1map ) {
- if ( grep { $_ eq $key } @p1usedTiles ) {
- next;
- } else {
- $p1map{$key} = ".";
- }
-
- }
- # Now the same for the AI map
- foreach my $ship ( keys %p2ships ) {
-
- if ( ! $p2ships{$ship} ) {
- next;
- }
- my $shipRef = $p2ships{$ship};
- my $location = ${$shipRef}{loc};
- my @inUseTiles = split(",", $location);
- foreach my $iut ( @inUseTiles ) {
- push(@p2usedTiles, $iut);
- }
-
- }
- # Clean the tiles
- foreach my $key ( keys %p2map ) {
- if ( grep { $_ eq $key } @p2usedTiles ) {
- next;
- } else {
- $p2map{$key} = ".";
- }
- }
- }
- sub printMap {
- my $count = 1;
- print "^ Player Map ^\n";
- foreach my $key ( sort { $a <=> $b } keys %p1map ) {
- # Probably a better way to do this
- if ( $count != 10 && $count != 20 && $count != 30 && $count != 40 && $count != 50 ) {
- if ( $p1map{$key} eq "*" ) {
- print YELLOW, "$p1map{$key}", RESET;
- } elsif ( $p1map{$key} eq "@" ) {
- print RED, "$p1map{$key}", RESET;
- } elsif ( $p1map{$key} eq "~" ) {
- print CYAN, "$p1map{$key}", RESET;
- } else {
- print "$p1map{$key}";
- }
- } else {
- if ( $p1map{$key} eq "*" ) {
- print YELLOW, "$p1map{$key}\n", RESET;
- } elsif ( $p1map{$key} eq "@" ) {
- print RED, "$p1map{$key}\n", RESET;
- } elsif ( $p1map{$key} eq "~" ) {
- print CYAN, "$p1map{$key}\n", RESET;
- } else {
- print "$p1map{$key}\n";
- }
- }
- $count++;
- }
- }
-
- sub printPlayerStats {
- # Print stats from main turn menu
- print "\n";
- foreach my $key ( keys %p1ships ) {
- my $shipHref = $p1ships{$key};
- if ( ! defined $p1ships{$key} ) {
- print MAGENTA, "^^^ Ship: $key ^^^ \n", RESET;
- print RED, "| SUNK! | \n", RESET;
- } else {
- print MAGENTA, "^^^ Ship: $key ^^^ \n", RESET;
- print RED, "| HP: ${$shipHref}{hp} | AP: ${$shipHref}{ap} | Location: ${$shipHref}{loc} |\n", RESET;
- }
- }
- print MAGENTA, "Coordinates attacked since last AI move:\n", RESET;
- my $atkArSize = scalar @p1Attacks;
- if ( $atkArSize > 0 ) {
- foreach my $coor ( @p1Attacks ) {
- print RED, "$coor ", RESET;
- }
- } else {
- print "No attacks since last AI move";
- }
- print "\n";
- }
- sub shipPosition {
- # Map ship to position via grid mapping
- # 1 2 3 4 5 6 7 8 9 10
- # . . . . . . . . . .
- # 11 12 13 14 15 16 17 18 19 20
- # . . . . . . . . . .
- # Etc.
-
- # Function should recieve ship hashRef and new grid location as input
- my $shipHref = shift;
- my $newLocation = shift;
- my $currentLocation = ${$shipHref}{loc};
- my @currentLoc = split(/,/, $currentLocation);
- my @newLoc = split(/,/, $newLocation);
- # This ended up working better than old loop
- &clearUnocTiles;
-
- # Now update new positon
- foreach my $tile ( @newLoc ) {
- $p1map{$tile} = ${$shipHref}{sym};
- }
- # Update shipHref with valid location
- ${$shipHref}{loc} = join(',', @newLoc);
-
- # Update move counter -- CANT DO THIS HERE AS WE USE THIS SUB IN INIT
- # ${$shipHref}{mc} = 1;
- }
- # TODO: Consolidate with above sub
- sub AiShipPosition {
- # Map ship to position via grid mapping
- # 1 2 3 4 5 6 7 8 9 10
- # . . . . . . . . . .
- # 11 12 13 14 15 16 17 18 19 20
- # . . . . . . . . . .
- # Etc.
-
- # Function should recieve ship hashRef and new grid location as input
- my $shipHref = shift;
- my $newLocation = shift;
- my $currentLocation = ${$shipHref}{loc};
- my @currentLoc = split(/,/, $currentLocation);
- my @newLoc = split(/,/, $newLocation);
- # This ended up working better than old loop
- &clearUnocTiles;
- # Now update new positon
- foreach my $tile ( @newLoc ) {
- $p2map{$tile} = ${$shipHref}{sym};
- }
- # Update shipHref with valid location
- ${$shipHref}{loc} = join(',', @newLoc);
- # Update move counter -- CANT DO THIS HERE AS WE USE THIS SUB IN INIT
- # ${$shipHref}{mc} = 1;
- }
- sub updateMap {
- foreach my $key ( keys %p1ships ) {
- my $shipHref = $p1ships{$key};
- my @mapPoints = split(/,/, ${$shipHref}{loc});
- foreach my $mpoint ( @mapPoints ) {
- my $symbol = ${$shipHref}{sym};
- $p1map{$mpoint} = $symbol;
- }
- }
-
- foreach my $key ( keys %p2ships ) {
- my $shipHref = $p2ships{$key};
- my @mapPoints = split(/,/, ${$shipHref}{loc});
- foreach my $mpoint ( @mapPoints ) {
- my $symbol = ${$shipHref}{sym};
- $p1map{$mpoint} = $symbol;
- }
- }
- }
- sub checkLocation {
- # Given a set of coordinates, determine if they are already occupied
- my $taken = 0;
- my $coordinates = shift;
- if ( $coordinates !~ /^[0-9]*,[0-9]*$/ && $coordinates !~ /^[0-9]*,[0-9]*,[0-9]*$/ && $coordinates !~ /^[0-9]*,[0-9]*,[0-9]*,[0-9]*,[0-9]*$/ ) {
- print "These coordinates look incorrect, you shouldnt see this error...\n";
- $taken = $taken + 1;
- }
- my @coors = split(/,/, $coordinates);
- foreach my $coor ( @coors ) {
- if ( $p1map{$coor} ne "." ) {
- print "coordinate $coor contains $p1map{$coor}\n";
- $taken = $taken + 1;
- }
- }
- if ( $taken >= 1 ) {
- return 1;
- } else {
- return 0;
- }
- }
- sub placeShips {
- while() {
- # Init map at the top as failure will kick you back here
- &initMap;
- print "Where do you want to place your cruiser? : ";
- my $cruLoc = <STDIN>;
- chomp $cruLoc;
-
- ###
- ### TODO : Not actually checking location on any of the below blocks
- ### For whatever reason, it doesn't work as expected, and return coordinates that
- ### are taken despite them being empty. I don't understand the behavior, and need to revisit this
- ###
- if ( $cruLoc !~ /^[0-9]*,[0-9]*,[0-9]*$/ ) {
- #|| ! eval &checkLocation($cruLoc) ) {
- print "Input looks wrong, or coordinates are taken, try again\n";
- next;
- }
- print "Where do you want to place your carrier? : ";
- my $carLoc = <STDIN>;
- chomp $carLoc;
- if ( $carLoc !~ /^[0-9]*,[0-9]*,[0-9]*,[0-9]*,[0-9]*$/ ) {
- # || ! eval &checkLocation($carLoc) ) {
- print "Input looks wrong, or coordiantes are taken, try again\n";
- next;
- }
- print "Where do you want to place your submarine? : ";
- my $submLoc = <STDIN>;
- chomp $submLoc;
- if ( $submLoc !~ /^[0-9]*,[0-9]*$/ ) {
- # || ! eval &checkLocation($submLoc) ) {
- print "Input looks wrong, I need 2 comma seperated coordinates, try again\n";
- next;
- }
- print "Coordinates are:\n";
- print "Cruiser: $cruLoc\n";
- print "Carrier: $carLoc\n";
- print "Submarine: $submLoc\n";
- print GREEN, "Type yes to confirm or type redo to redo: ", RESET;
- my $confirm = <STDIN>;
- chomp $confirm;
- if ( $confirm eq "redo" ) {
- next;
- } elsif ( $confirm eq "yes" ) {
- my $cruRef = $p1ships{cru};
- my $carRef = $p1ships{car};
- my $submRef = $p1ships{subm};
- if ( ! eval &checkLocation($cruLoc) ) {
- &shipPosition($cruRef, $cruLoc);
- } else {
- print "Cruiser eval check failed\n";
- &printMap;
- next;
- }
- if ( ! eval &checkLocation($carLoc) ) {
- &shipPosition($carRef, $carLoc);
- } else {
- print "Carrier eval check failed\n";
- &printMap;
- next;
- }
- if ( ! eval &checkLocation($submLoc) ) {
- &shipPosition($submRef, $submLoc);
- } else {
- print "Submarine eval check failed\n";
- &printMap;
- next;
- }
- last;
- }
- }
- }
- sub randomLocation {
- # Used by AI
- # Pass in ship type and come up with a random location
- my $shipType = shift;
- my $size;
- if ( $shipType eq "cru" ) { $size = 3; }
- if ( $shipType eq "car" ) { $size = 5; }
- if ( $shipType eq "subm" ) { $size = 2; }
- # Where to randomly look in the map index ( keys %p2map ) - between 1 and 50
- my @fakeMap = ( 1 .. 50 );
- my $random_num = int(1 + rand(50 - 1));
- # Need to use splice so that numbers are sequential
- # TODO: Can still cause a situation where ships 'wrap' around edges of the map
- my @newLocs = splice(@fakeMap, $random_num, $size);
- # Make sure we don't end up with an empty/short location set
- while ( scalar(@newLocs) < $size ) {
- print "Re-rolling AI ship position due to conflict\n";
- $random_num = int(1 + rand(50 - 1));
- @newLocs = splice(@fakeMap, $random_num, $size);
- }
-
- my $newLocs = join(",", @newLocs);
- return $newLocs;
- }
- # TODO: This is stupid, main subroutine should be adjusted to take player map arg
- sub checkAILocation {
- my $coor = shift;
- my @coors = split(/,/, $coor);
- my $taken = 0;
- foreach my $coor ( @coors ) {
- if ( $p2map{$coor} ne "." ) {
- print "coordinate $coor contains $p2map{$coor}\n";
- $taken = $taken + 1;
- }
- }
- if ( $taken >= 1 ) {
- return 1;
- } else {
- return 0;
- }
- }
- sub initAI {
- print MAGENTA, "Initialzing opponent..\n", RESET;
- # AI equivelant of placeShips()
- my $cruLoc = &randomLocation("cru");
- my $carLoc = &randomLocation("car");
- my $submLoc = &randomLocation("subm");
-
- #print "AI cru loc = $cruLoc\n";
- #print "AI car loc = $carLoc\n";
- #print "AI subm loc = $submLoc\n";
- # Hash refs for ships
- my $cruHref = $p2ships{cru};
- my $carHref = $p2ships{car};
- my $submHref = $p2ships{subm};
- # Update Locations with new locations
- if ( ! eval &checkAILocation($cruLoc) ) {
- ${$cruHref}{loc} = $cruLoc;
- } else { print "Something went wrong with AI init, exiting\n"; exit 0; }
- if ( ! eval &checkAILocation($carLoc) ) {
- ${$carHref}{loc} = $carLoc;
- } else { print "Something went wrong with AI init, exiting\n"; exit 0; }
- if ( ! eval &checkAILocation($carLoc) ) {
- ${$submHref}{loc} = $submLoc;
- } else { print "Something went wrong with AI init, exiting\n"; exit 0; }
- print "Done\n";
- }
- sub AiTurn {
- # General subroute to have the AI do something after the player takes their turn
- # Main AI turn logic lives here -- extremely basic to start
- # Should not take any arguments
- print MAGENTA, "Starting AI's turn\n", RESET;
- sleep 1;
- # This used to be 50/50, but testing has found having the AI
- # constantly moving around makes the game pretty boring, so make it less likely the AI will move
- my @outcomes = (0,1,2,3,4);
- my $randomNum = int(rand(@outcomes));
- #my $randomNum = 1;
- # Get random ship key and href
- my @availShips;
- foreach my $key ( keys %p2ships ) {
- if ( ! defined $p2ships{$key} ) {
- next;
- } else {
- push(@availShips,$key);
- }
- }
- my $randomShipKey = $availShips[rand @availShips];
- #print "AI's random ship is : $randomShipKey\n";
- my $shipHref = $p2ships{$randomShipKey};
- # Make sure AI doesn't try to 'move' if it has no available moves left
- print "Checking available AI moves\n";
- my @availMovers;
- foreach my $key ( keys %p2ships ) {
- my $shipRef = $p2ships{$key};
- if ( ! defined $p2ships{$key} ) {
- next;
- } elsif ( ${$shipRef}{mc} == 1 ) {
- next;
- } else {
- push(@availMovers, $key);
- }
- }
- my $availM = scalar @availMovers;
- if ( $availM == 0 ) {
- #print "Bumping random number because we're out of moves\n";
- $randomNum = 1;
- }
- if ( $randomNum == 0 ) {
- # Move
- print MAGENTA, "AI is moving!\n", RESET;
- # Get new random location
- my $newRandomLocation = &randomLocation($randomShipKey);
- while ( eval &checkAILocation($newRandomLocation) ) {
- #print "Conflict in AI random location, rerolling\n";
- $newRandomLocation = &randomLocation($randomShipKey);
- }
-
- #print "AI's new random location is : $newRandomLocation\n";
- # Move ship to that location
- if ( ! eval &checkAILocation($newRandomLocation) ) {
- #print "Setting AI's new location to $newRandomLocation\n";
- ${$shipHref}{loc} = $newRandomLocation;
- ${$shipHref}{mc} = 1;
- print "Updating/cleaning maps\n";
- @p1Attacks = ("Coors: ");
- &clearUnocTiles;
- }
- } else {
- # Attack
- # Same logic copy and pasted from player attack sub, with vars changed
- print RED, "AI is attacking!\n", RESET;
- my $randomCoor = int(1 + rand(50 - 1));
- print RED, "AI's chosen attack coordinate is $randomCoor\n", RESET;
- my $ap = ${$shipHref}{ap};
- foreach my $key ( keys %p1ships ) {
- if ( ! $p1ships{$key} ) {
- next;
- }
- my $playerShipRef = $p1ships{$key};
- my $playerShipLocation = ${$playerShipRef}{loc};
- my @playerShipCoors = split(",", $playerShipLocation);
- if ( grep { $_ eq $randomCoor } @playerShipCoors ) {
- # Hit !
- print RED, "Hit!\n", RESET;
- print RED, "The AI hit your $key for $ap !\n", RESET;
- # Deterime damage to hull
- my $playerShipHp = ${$playerShipRef}{hp};
- my $newPlayerHullValue = $playerShipHp - $ap;
- if ( $newPlayerHullValue <= 0 ) {
- print RED, "The AI sunk your $key !\n", RESET;
- # Clear player map of ship and then set ship key to undef
- my @sunkenLocation = split(",", ${$playerShipRef}{loc});
- foreach my $tile (@sunkenLocation) {
- $p1map{$tile} = ".";
- }
- $p1ships{$key} = undef;
- } else {
- ${$playerShipRef}{hp} = $newPlayerHullValue;
- print RED, "Your $key now has ${$playerShipRef}{hp} hp !\n", RESET;
- }
-
- last;
- } else {
- # Miss
- print GREEN, "AI Miss\n", RESET;
- }
- }
- }
- print "\n";
- }
- sub playerAttackAI {
- # Perform attack against AI. Takes a coordinate, and ship hashRef as an arg
- # atkCoor is the coordinate to attack
- # $shipHref is a href to the ship that * is attacking *
- #
- # NOTE: This was a more generalized &attack subroutine, but perl
- # didn't like me trying to iterate over a scalar hash dereference, so
- # figured seperate subroutes for each player attack would be the 'easiest' way to
- # do this, as opposed to building a working hash and then repopulating
- # the real map/ships hashes with the updated values from the working hash
- # ... open to suggestions for better ways to do this
- #
- my $atkCoor = shift;
- my $shipHref = shift;
- # Grab attack power
- my $ap = ${$shipHref}{ap};
- # Look at opponents ships and figure out where they are --
- # if the supplied coordinate matches any ship location, start the 'hit' logic, else, miss
- foreach my $key ( keys %p2ships ) {
- if ( ! $p2ships{$key} ) {
- next;
- }
- my $aiShipRef = $p2ships{$key};
- my $aiShipLocation = ${$aiShipRef}{loc};
- my @AiShipCoors = split(",", $aiShipLocation);
- if ( grep { $_ eq $atkCoor } @AiShipCoors ) {
- # Hit !
- print GREEN, "Hit!\n", RESET;
- print "You hit the AI's $key for $ap !\n";
- # Deterime damage to hull
- my $aiShipHp = ${$aiShipRef}{hp};
- my $newAiHullValue = $aiShipHp - $ap;
- if ( $newAiHullValue <= 0 ) {
- print "You sunk the AI's $key !\n";
- $p2ships{$key} = undef;
- } else {
- ${$aiShipRef}{hp} = $newAiHullValue;
- print "AI's $key now has ${$aiShipRef}{hp} hp !\n";
- }
- last;
- } else {
- # Miss
- print RED, "Player Miss\n", RESET;
- }
- }
- }
- sub printMenu {
- print <<EOF
- Swatson Battleship
- Type 'start','help', or 'quit'
- EOF
- }
- sub printHelp {
- print <<EOF
- How To Play:
- This is a turn based battleship game. Your objective is to destory the AI ships.
- Each turn you can either attack with 1 ship or move 1 ship.
- To attack type: attack
- To move type: move
- To see stats type: stats
- Press Ctrl+C to exit any time.
- You have 3 ships:
- * Cruiser - Hull Points 2, Size 3, Attack Power 1
- * Carrier - Hull Points 3, Size 5, Attack Power 2
- * Submarine - Hull Points 1, Size 2, Attack Power 3
- Each turn you will be prompted to either move or attack.
- * When attacking, provide a coordinate number ( 1 - 50 ) to fire at
- * When moving, provide a comma seperated list of coordinates to move to
- * * For cruiser, provide 3 coordinates
- * * For carrier, provide 5 coordinates
- * * For submarine, provide 2 coordinates
- EOF
- }
- &initMap;
- &printMap;
- &updateMap;
- &printMap;
- # Menu loop
- while () {
- my $count = 0;
- if ( $count == 0 ) {
- &printMenu;
- }
- print "Select option: ";
- my $input = <STDIN>;
- chomp $input;
- if ( $input eq "quit" ) {
- print "Quitting\n";
- exit 0;
- }
- if ( $input eq "help" ) {
- &printHelp;
- }
- if ( $input eq "start" ) {
- my $gameCounter = 0;
- my $aiCounter = 1;
- while () {
- print "\n\n";
- # Main game loop
- if ( $gameCounter == 0 ) {
- &initAI;
- &placeShips;
- &clearUnocTiles;
- $gameCounter++;
- next;
- }
- if ( ! defined $p2ships{cru} && ! defined $p2ships{subm} && ! defined $p2ships{car} ) {
- print "You won! Exiting...\n";
- exit 0;
- } elsif ( ! defined $p1ships{cru} && ! defined $p1ships{subm} && ! defined $p1ships{car} ) {
- print "The brain dead AI beat you! Exiting...\n";
- exit 0;
- }
- print GREEN, "! TURN: $gameCounter !\n", RESET;
- sleep 1;
- my @opponentRemaining;
- foreach my $key ( keys %p2ships ) {
- if ( defined $p2ships{$key} )
- { push(@opponentRemaining, $key)
- }
- }
- # Make sure the AI doesn't take an additional turn if
- # the player makes a typing mistake or calls the stats sub
- if ( $aiCounter == $gameCounter ) {
- &AiTurn;
- $aiCounter++;
- }
- my $opShipsLeft = scalar @opponentRemaining;
- print "\n";
- print GREEN, "--AI has $opShipsLeft ships left--\n", RESET;
- &printMap;
- print "Move or attack: ";
- my $gameInput = <STDIN>;
- chomp $gameInput;
- if ( $gameInput eq "quit" ) {
- print "Are you sure? : ";
- my $answer = <STDIN>;
- chomp $answer;
- if ( $answer eq "yes" ) {
- exit 0;
- } else {
- next;
- }
- }
- if ( $gameInput eq "move" ) {
- print "What ship do you want to move? : ";
- my $shipInput = <STDIN>;
- chomp $shipInput;
- my @validInputs;
- foreach my $key ( keys %p1ships ) {
- my $shipHref = $p1ships{$key};
- my $moveCounter = ${$shipHref}{mc};
- if ( ! defined $p1ships{$key} ) {
- next;
- } elsif ( $moveCounter == 1 ) {
- next;
- } else {
- push(@validInputs,$key);
- }
- }
- if ( ! grep { $_ eq $shipInput } @validInputs ) {
- print "That input looks wrong, try again\n";
- next;
- } else {
- print "New coordinates: ";
- my $newCoor = <STDIN>;
- chomp $newCoor;
- if ( $shipInput eq "cru" && $newCoor !~ /^[0-9]*,[0-9]*,[0-9]*$/ ) {
- print "Bad coordinates, try again\n";
- next;
- } elsif ( $shipInput eq "car" && $newCoor !~ /^[0-9]*,[0-9]*,[0-9]*,[0-9]*,[0-9]*$/ ) {
- print "Bad coordiantes, try again\n";
- next;
- } elsif ( $shipInput eq "subm" && $newCoor !~ /^[0-9]*,[0-9]*$/ ) {
- print "Bad coordinates, try again\n";
- next;
- }
- if ( eval &checkLocation($newCoor) ) {
- print "Coordinates occupied, try again\n";
- next;
- }
- my $shipHref = $p1ships{$shipInput};
- &shipPosition($shipHref, $newCoor);
- ${$shipHref}{mc} = 1;
- &clearUnocTiles;
- print "\n";
- }
- } elsif ( $gameInput eq "attack" ) {
- print "What ship do you want to attack with? : ";
- my $attackShip = <STDIN>;
- chomp $attackShip;
- my @validInputs;
- foreach my $key ( keys %p1ships ) {
- if ( ! defined $p1ships{$key} ) {
- next;
- } else {
- push(@validInputs,$key);
- }
- }
- if ( ! grep { $_ eq $attackShip } @validInputs ) {
- print "That input looks wrong, try again\n";
- next;
- } else {
- print "Select a single coordinate to attack: ";
- my $atkCoor = <STDIN>;
- chomp $atkCoor;
- my @validCoors = ( 0 .. 50 );
- if ( ! grep { $_ eq $atkCoor } @validCoors ) {
- print "That doesn't look like a real coordinate, try again\n";
- next;
- } else {
- &playerAttackAI($atkCoor,$p1ships{$attackShip});
- push(@p1Attacks,$atkCoor);
- print "\n";
- }
- }
- } elsif ( $gameInput eq "stats" ) {
- &printPlayerStats;
- next;
- } elsif ( $gameInput eq "help" ) {
- &printHelp;
- print "\n";
- next;
- } else {
- next;
- }
- $gameCounter++;
- }
- }
- $count++;
- }
|