Lab 3 due Saturday May 21st, 2011 at 12:01am This lab is worth 30 points. You will write a program that will read data from a filename which will be given at the command line. You should NOT use the default mechanism to read from this file- instead, you must use an explicit open statement. The file will have the following rules: 1. blank lines do not count for anything and should be ignored 2. lines which begin with a # sign should be completely ignored as well - these are considered comments 3. the file will contain multiple sections that will have the following format (without the comment lines of course): # first competitor Name: Bob the Archmage POW: 20 DEF: 8 each section starts with a Name field- all lines after that pertain to that particular character, until another line is read that starts with Name. No fields will take more than one line, and no whitespace will be found before the fields. One : and one space will be found between each field and its corresponding value. Extra spaces ARE allowed after the value for each field. Field names will consist of \w characters. 4. You must simulate a "battle" between the competitors that you will read in from the file. This is to be treated as one would expect in a simple role-playing game (if anybody doesn't understand the basic idea of a battle in one of these games, please let me know). This particular battle will be between several powerful magicians. They will use a default spell called Magic Missle. Anyway, the file will contain 0, 1, 2, or more competitors. For the non-extra credit portion of the lab, you must deal with 0, 1, or 2 competitors. One of the extra credit items will deal with any additional competitors (this means you can skip anything after (and including) the third Name field in the input file that your program is given. You can also skip any fields in the file that do not correspond to Name, POW, or DEF (unless you are doing extra credit of course, and even if you are, you will just throw away any lines that have strange or misspelled field names). By skip I do NOT mean end the program- your program must be able to just throw out any fields that it doesn't recognize. Do NOT print an error for any of these either. If the file contains only 1, or even 0 competitors, then display an appropriate error message indicating this. Your program should line each competitor up against each other competitor, and run a battle according to the following formulas: a. Each round of combat consists of one of the competitors (chosen at random) getting to attack first, followed by a retaliation by the other competitor (if he/she is still around of course). The formula for this will be: POW(attacker) + random(10) - DEF (defender) where POW(attacker) means the POW value that was read in for the attacker, and the DEF is the DEF that was read in for the defender, and random(10) means a random INTEGER between 1 and 10. If this formula results in a positive number, then the defender will lose an amount of hit points equal to: (POW(attacker) + random(5)) / 4 b. all characters start with 40 hit points, and do not gain any back throughout the competition c. the competition should continue until one competitor has been reduced to < 1 hit point, at which point the other competitor is determined to be the winner (since only one attack happens at a time, there should be no way that both competitors could reach 0 hit points simultaneously. d. please note that all math should result in integers- there should not be any decimal points in the output. This means that you should use the "int" function- do not use any other method to round- this one will suffice for this lab. e. for the random(x) function, you should use the built-in function called rand. The following will give you a random integer between 1 and x:: int(rand(x))+1 5. Your program should print out the result of each half of each round of the battle, in a manner similar to the following: Bob the Archmage vs. Ryan the Wizard Round 1 Current Hit Points: Bob the Archmage: 40 Ryan the Wizard: 40 Bob the Archmae casts Magic Missle first, and misses. Ryan the Wizard casts Magic Missle, and hits for 3 points of damage Round 2 Current Hit Points: Bob the Archmage: 37 Ryan the Wizard: 40 ... ... ... Round 28 Current Hit Points: Bob the Archmage: 3 Ryan the Wizard: 1 Bob the Archmage casts Magic Missle first, and hits for 4 points of damage Bob the Archmage has won the battle 6. Your program should wait for input from the user after each round is printed (not each HALF, but each ROUND, so in most cases this will be one attack for each competitor). This is necessary, or the longer battles will scroll by WAY to fast to read... HINT: This step can be accomplished by just reading from STDIN and then not doing anything with the input. 7. for the input file: ignore any fields that are not in the following list: ("Name","POW","DEF"), unless you are doing the extra credit. Just skip these lines if you aren't. 8. the fields ("POW","DEF") are required for each Name. If one of those is not present for one of the competitors, then the file is invalid, and should be thrown out (and a message should be printed to the user indicating that the file was invalid because one of the required fields was missing. Additionally, these fields should always have a simple number following the name of the field- if there are any extra non-space characters, then the file should be thrown out, and an appropriate error message should be printed. For example, if you see the following section in a file: ... Name: Joe POW: 1 DEF: 2a then you should inform the user that the file is invalid. As I mentioned before, each line will start with a field name, followed by a :, followed by a space (not \s, but a single space instead), followed by the value. All of the values must be digits, except for the value of the Name field, which must be word characters and/or spaces (in other words, [\w ]+). Anything other than this is not a valid line. Make SURE that you don't exit the program with an error for invalid file syntax unless the field that is invalid is one of the ones that is required. For example, if one of the extra credit fields is there, and you aren't doing the extra credit, don't exit the program if it doesn't happen to have only a single digit after the ": "- like I said, such non-required lines should always be ignored unless you are doing the associated extra credit. 9. your program should print a summary of the battle(s) that it processes- this should include the competitors, their stats, and who won. This summary will be printed to a file, which will be the second argument passed to the program. So, if your program is called like this: ./lab3.pl /tmp/competitors /tmp/results Then you should read the file /tmp/competitors for the information about the competitors, and you should write the summary (stats and victor) to the file called /tmp/results. The output file is to be overwritten, not appended to. 10. your program must have a function that does all the file reading and returns a reference to an array of competitors. This function will be passed a filename, which you will open, read, validate, close, and then return the array of competitors. Here is an example to get you started (note that it does nothing with the filename- yours will need to open it and validate, etc.). You can use this function to give you an idea of how to set up your data structure- keep in mind that you must implement the reading from the input file and fill this data structure yourself- this example structure will just allow you to write the proper battle code if your file reading code is not yet finished: sub read_file { my $file = shift; my @competitors = ( { "Name" => "Joe Firebrand", "POW" => 20, "DEF" => 10, "HP" => 100, "ATT" => [ { "Spell" => "Firebolt", "Chance" => 35, "Damage" => 15 }, { "Spell" => "Firestorm", "Chance" => 15, "Damage" => 50 } ] }, { "Name" => "Igor Windwalker", "POW" => 20, "DEF" => 10, "HP" => 50, "ATT" => [ { "Spell" => "Hurricane", "Chance" => 25, "Damage" => 20 }, { "Spell" => "Gust of Wind", "Chance" => 55, "Damage" => 15 } ], "ENVIRONMENTAL" => [ { "Spell" => "Fog of Acid", "Chance" => 2, "Damage" => 10, "Duration" => 4 } ], "WIMPY" => [ { "Chance" => 10, "Threshold" => 5 } ] }); return \@competitors; } You can use this example to start writing the part that runs the battles, and then you can finish it up by reading from the file. Additional requirements/notes: 1. don't use any modules for any of this 2. -w and use strict as usual, and file should be executable... 3. The file that your program should open and read in will be given at the command line, and will be the first argument passed to the program. You should read EXACTLY this file- for example, do not add any path in front of it- I will give your program the full path. The second argument will be the file to output your summary to. 4. your lab must validate the input file to be sure that the lines follow the guidelines given above. Be sure to check for digits where there should be digits, colons where there should be colons, and a space where there should be a space. Failure to check for these things will result in a loss of points, even if the program manages to still get it right. In other words, if I give a line like this: POW 5 you should abort the program and tell me there was invalid input, since the : is missing. Extra Credit: 1. (5) Simulate battles between ALL competitors, i.e. if you have 3 people, you will need to display the results for 3 battles: (a vs. b, b vs. c, a vs. c). Display each of these battles in turn. Start each battle with full hitpoints. 2. (2) Look for the following field in the input: HP this is a numeric field, whose value will be the initial hit points for the character (instead of 40) this field will follow the same rules as the other fields with respect to punctuation. 3. (3) provide statistics for what percentage of each competitor's attacks were hits- i.e. if the combat goes for 25 rounds, and Bob the Archmage hits 20 times, then his percentage will be 80%. 4. (4) allow another type of field: ATT: 50 10 Fireball this is an alternate attack form for the character. The first number is the percentage chance that this attack will be used instead of a normal attack. The second number is an indicator for the damage allowed- it is the maximum damage that can be assigned- so plug that value into a random function to return the damage. In the example above, the damage will be 1 - 10 points. The rest of the line after the two numbers is the name of the spell- this will only include word charaters corresponding to \w and whitespace corresponding to \s. In this case, the name of the spell is Fireball. You will need to alter your output lines to say something like: Bob the Archmage casts Fireball first, and hits for 8 points of damage this will need to take into account that the spell that was used was Fireball, instead of the default attack of Magic Missle. Multiple ATT lines are allowed. If there happens to be more than one, then check them all sequentially, and stop if any of the percentages ever happen. So, if there are 2 special attacks, and the first one doesn't happen, then try the second one. If that one doesn't happen, then do the default attack of Magic Missle. However, if that first special attack worked, then don't do anything further- each character can only cast one spell per turn. Note- this extra credit is worth a lot of points, but it will take quite a bit more work in parsing the input file, since it doesn't follow the Field: value format that the other lines do. 5. (4) Allow for healing- if there is a field like this: HEAL_SPELL: 10 5 10 The first number is the percentage chance of casting a healing spell, and the second number is the maximum number of hit points that might be healed (use a random number as mentioned above with this number as the maximum). The third number indicates a hit point threshold below which the healing will be attempted. So for this example, this character would have a 10% chance of casting a healing spell INSTEAD of attacking, but only if they are below 10 hit points, and the spell will heal 1 - 5 hit points of damage. NOTE: in case you missed it, the healing happens INSTEAD of a normal attack, but only if the hit points are currently below the threshold, AND the percentage chance happens. 6. (4) Allow for a new field: WIMPY_CHANCE: 10 5 The first number is the percentage chance that the character will run away at the end of a round. The second number is the number of hit points below which he will consider running away. If his current hit points are greater than or equal to that second number, he'll stand his ground and continue the fight. If they are less than that number, then check the percentage chance to see if he will take the wimpy way out and run away from the battle, thus ending the battle and giving victory to his opponent. If both players run away, it should be considered a draw. 7. (5) Allow for continuous attacks that happen every round for several rounds: ENVIRONMENTAL: 2 10 4 Fog of Acid This item is a spell called Fog of Acid, which does 10 damage every round for four rounds. The character has a 2% chance of using this attack in this example, but ONLY if they don't have another environmental effect of their own that is still acting. These attacks replace the character's attack for the first round that they are used, but after that they happen in addition to the character's attack (normal or special or heal spell). So, you can output the result of the character's normal attack, and then also include a message about damage due to an existing environmental attack, such as: Bob the Archmage casts Fireball first, and hits for 8 points of damage Bob the Archmage's Fog of Acid spell inflicts another 10 points of damage on the first round that the Fog of Acid is cast (in this example), you would show something like: Bob the Archmage casts Fog of Acid. Bob the Archmage's Fog of Acid spell inflicts 10 points of damage SPECIAL NOTE ON EXTRA CREDIT: For extra credits 4 - 7, you should use the following algorithm to determine what happens: 1. if there is a heal spell for this character, check hit point threshold- if it is low enough, cast the heal spell, then skip to 5 2. if there are one or more alternate attack forms (ATT), check each one of their percentages, executing the first one that passes, then skip to 5 3. if there are one or more environmental attack forms, AND this character currently does not have an environmental attack already happening, check each of the environmental attack types, executing the first one that passes the percentage check- if you execute one of these, then skip to 5.- remember that the first round of an environmental attack will replace the user's normal attack for that round. 4. Execute a normal Magic Missle attack. 5. Apply damage from any existing environmental attack, and subtract one from the remaining rounds of that attack if there are any. 6. Check wimpy chances for both players. If there are multiple forms for either HEAL_SPELL or ATT or for ENVIRONMENTAL, try the percentages in the order you read them from the file.