UNIX> gcc -Wall -Wextra -o chain_heal chain_heal.c -lm UNIX>
Your job as Urgosa is to keep your companions alive with your healing spell called Chain Heal. Chain Heal allows Urgosa to target a player on the battlefield and heal him/her. Afterward, the spell then jumps from that player to another player that is within a certain range and heals them as well. It continues to jump to subsequent players in this fashion. What does it mean to heal them? Well, every player has a both a current amount of pacification points (PP) and a maximum amount of PP. Chain Heal can restore PP up to the maximum amount for that player.
For example, suppose Chain Heal can restore 300PP and it hits a player with 100PP and a max of 500PP. That player will now have 400PP. Pretty simple, but if that player were to be healed again they would end up with 500PP, not 700PP.
Chain Heal has the following limitations:
chain_heal initial_range jump_range num_jumps initial_power power_reduction < input_file
Each command line argument is an integer with the exception of power_reduction, which is a double. Chain_heal reads information about the players on the battlefield from stdin. Each line contains information about a single player and is composed of exactly 5 words:
Here is the file for the earlier example, small.txt:
0 0 100 100 Urgosa_the_Healing_Shaman 2 0 100 500 Adam_the_Warrior 3 0 400 450 Catherine_the_Great 4 0 45 400 Chad_the_Priest 4 1 300 600 James_the_Lightning_Lord |
You do not have to error-check the input. You may assume that it is in the correct format and that there are no two players with the exact same name.
Once you have determined the optimal path, you print the path, one player per line. Each line will have the name of the player healed followed by a space and the amount of healing done to them. In addition, you print a final line with the word "Total_Healing" followed by a space and the total amount of healing done by the Chain Heal.
Here's is an example of my output on small.txt:
UNIX> ./chain_heal 2 1 4 500 0.25 < small.txt Adam_the_Warrior 400 Catherine_the_Great 50 Chad_the_Priest 281 James_the_Lightning_Lord 211 Total_Healing 942
You have to write this program in C in a single file, with no extra "helper" programs. That means no standard template library, no fields library and no libfdr library. I'll give some help below.
To make this explicit, you may have the following includes in your program:
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <math.h> /* If you include this, you need to compile with -lm */ |
When you read standard input, go ahead and use scanf() to read words rather than lines. You can do this because we are guaranteeing that input is in the proper format.
You are not allowed to make any assumptions on the size of the input file (i.e. on the number of nodes in the graph).
Once you create the graph, you determine each of the nodes that is within initial_range of Urgosa. For each of these nodes, you perform a depth-first search to find the optimal healing path starting from the node. The depth-first search enumerates all paths starting from the node. You maintain the best path, and at the end of the program, you print it.
For example, suppose I call:
UNIX> ./chain_heal 2 1 4 500 0.25 < small.txtHere's a picture of the graph, with the potential starting nodes colored green:
There are only two potential paths for the Chain Heal:
Instead, suppose I call
UNIX> ./chain_heal 1 2 4 500 0.25 < small.txtNow, the graph looks as follows:
There are now four possible 4-node paths:
UNIX> ./chain_heal 1 2 4 500 0.25 < small.txt Urgosa_the_Healing_Shaman 0 Adam_the_Warrior 375 Chad_the_Priest 281 James_the_Lightning_Lord 211 Total_Healing 867 UNIX>
Reading the input: The first part should be simply reading the command line and reading the input file. Do this without storing anything, just so you can be sure your use of scanf() is correct.
Creating the Nodes and putting them into an array: As a second part, define a Node struct for each person. We're not going to worry about hooking the nodes together yet. We just want to read them in. As such, each node should have the following fields:
typedef struct node { char *name; int x, y; int cur_PP, max_PP; struct node *prev; } Node; |
To read in the nodes, go ahead and read the five words. Then allocate a new node with malloc(). Initialize the first five fields, and set prev to be the previous node that you read (obviously, with the first node, have its prev field be NULL). When you're done reading, you can traverse the nodes from most recent to least recent by chasing the prev pointers. What I did at this point was create an array of node pointers (Node **), and assigned nodes to it by traversing my linked nodes. I knew the size of this array, because its the number of nodes that I allocated (obviously, I kept track).
At this point, you no longer need the prev field in the nodes. You can traverse all of the nodes by traversing the array. This is some pretty old-school C programming, and you'll note how inconvenient it is compared to vectors in the STL. With vectors, you get that lovely push_back() method, which allows you to incrementally create vectors of arbitrary size. With C, you need to use a linked data structure to read an arbitraty number of elements, and when you're done, you can allocate and create an array.
You should go ahead and test this program to make sure you're not making any mistakes. Then move onto:
Creating the Graph: I added two more fields to my nodes at this point:
int adj_size; struct node **adj; |
These define the adjacency lists for the nodes. I created the actual lists in three steps.
At this point, print out each node with its adjacency list and double-check yourself.
Doing the DFS: Now you should add a visited field to each node for your DFS. You should write a DFS() procedure, which has three arguments:
UNIX> ./a.out 2 1 4 500 0.25 < small.txt Node:Urgosa_the_Healing_Shaman Hop 1 Node:Adam_the_Warrior Hop 1 Node:Catherine_the_Great Hop 2 Node:Chad_the_Priest Hop 3 Node:James_the_Lightning_Lord Hop 4 UNIX> ./a.out 1 2 4 500 0.25 < small.txt Node:Urgosa_the_Healing_Shaman Hop 1 Node:Adam_the_Warrior Hop 2 Node:Catherine_the_Great Hop 3 Node:Chad_the_Priest Hop 4 Node:James_the_Lightning_Lord Hop 4 Node:Chad_the_Priest Hop 3 Node:Catherine_the_Great Hop 4 Node:James_the_Lightning_Lord Hop 4 UNIX> ./a.out 1 10 4 500 .25 < small.txt Node:Urgosa_the_Healing_Shaman Hop 1 Node:Adam_the_Warrior Hop 2 Node:Catherine_the_Great Hop 3 Node:Chad_the_Priest Hop 4 Node:James_the_Lightning_Lord Hop 4 Node:Chad_the_Priest Hop 3 Node:Catherine_the_Great Hop 4 Node:James_the_Lightning_Lord Hop 4 Node:James_the_Lightning_Lord Hop 3 Node:Catherine_the_Great Hop 4 Node:Chad_the_Priest Hop 4 Node:Catherine_the_Great Hop 2 Node:Adam_the_Warrior Hop 3 Node:Chad_the_Priest Hop 4 Node:James_the_Lightning_Lord Hop 4 Node:Chad_the_Priest Hop 3 Node:Adam_the_Warrior Hop 4 Node:James_the_Lightning_Lord Hop 4 Node:James_the_Lightning_Lord Hop 3 Node:Adam_the_Warrior Hop 4 Node:Chad_the_Priest Hop 4 Node:Chad_the_Priest Hop 2 Node:Adam_the_Warrior Hop 3 Node:Catherine_the_Great Hop 4 Node:James_the_Lightning_Lord Hop 4 Node:Catherine_the_Great Hop 3 Node:Adam_the_Warrior Hop 4 Node:James_the_Lightning_Lord Hop 4 Node:James_the_Lightning_Lord Hop 3 Node:Adam_the_Warrior Hop 4 Node:Catherine_the_Great Hop 4 Node:James_the_Lightning_Lord Hop 2 Node:Adam_the_Warrior Hop 3 Node:Catherine_the_Great Hop 4 Node:Chad_the_Priest Hop 4 Node:Catherine_the_Great Hop 3 Node:Adam_the_Warrior Hop 4 Node:Chad_the_Priest Hop 4 Node:Chad_the_Priest Hop 3 Node:Adam_the_Warrior Hop 4 Node:Catherine_the_Great Hop 4 UNIX>
Calculating the total healing: Now that you are convinced that you are enumerating all the paths, add a parameter total_healing to your DFS(), and a best_healing to your global information. Use this to calculate and store the best total healing for each path. Print it out at the end. Note that you're not maintaining the path at this point -- just the best total healing. Test it against the program in the lab directory:
UNIX> ./chain_heal 2 1 4 500 0.25 < small.txt | tail -n 1 Total_Healing 942 UNIX> ./chain_heal 1 2 4 500 0.25 < small.txt | tail -n 1 Total_Healing 867 UNIX> ./chain_heal 2 2 4 500 0.25 < small.txt | tail -n 1 Total_Healing 1086 UNIX> ./chain_heal 10 10 4 500 0.25 < small.txt | tail -n 1 Total_Healing 1086 UNIX> ./chain_heal 10 10 5 500 0.25 < small.txt | tail -n 1 Total_Healing 1086 UNIX> ./chain_heal 10 10 5 500 0.1 < small.txt | tail -n 1 Total_Healing 1105 UNIX> ./chain_heal 10 10 5 500 0.5 < small.txt | tail -n 1 Total_Healing 825 UNIX>Finally, maintain the best path. What I did here was add three more fields to my global information:
int best_path_length; Node **best_path; int *healing; |
Both best_path and healing are allocated to be num_jumps in size.
I also added a healing integer to each node. Finally, I added a from node to my DFS call. Now, during the DFS, each node maintains its current healing value, plus a pointer to the previous node in the path by reusing the prev field from above.
When a new "best" path is found, the nodes are stored in best_path (using the prev fields), and their healing values are stored in the healing array. The size of the path is stored in best_path_length. At the end of the program, these are used to print out the final best path.
UNIX> ./random_hero_gen 10 10000 10000 10000 -8599 945 5234 8678 Urgosa_the_Healing_Shaman -7118 -9489 429 3830 Spinny-McCrazyPants_the_Bladesinger -1015 7421 1712 2300 Danielle_the_Gladiator -2466 6917 1382 6477 Moon-Moon_the_Monk 7429 -2081 691 7731 Hector_the_Delver -2003 7930 3876 4071 Sabastian_the_Artificer 60 -9294 703 760 Elizabeth_the_Shin-Kicker 276 6332 1168 2066 Aldaricht_the_Hero,-Born-Under-Justice -5935 -3947 3553 5955 Allen_the_Dragon-Knight 7855 -2953 332 1746 Varrus_the_Sorcerer UNIX> ./random_hero_gen 10 10000 10000 10000 -7075 7146 493 2890 Urgosa_the_Healing_Shaman 3977 -2357 4144 6778 Chad_the_Rapper 8477 -8470 5818 9776 Rob_the_Witchhunter 9263 -161 6470 7377 Rock-Party_the_Cavalier 6996 9754 2083 6215 Fist-RockBone_the_Smelly -7449 -5631 2636 3179 Chad_the_Tiny -4738 -273 41 4156 Chunkhead_the_Swashbuckler 3570 -7102 1256 2998 Sabastian_the_Shaman 1309 6582 2385 2439 Luigi_the_Adept -6540 2149 6471 7318 Dylan_the_Grumpy-Pants UNIX>The program chain_heal_check can be used to check your output. It takes the same command line arguments as chain_heal, plus the name of the file used as standard input for chain_heal. On standard input, it takes the standard output of the chain_heal call. If standard input specifies a correct path (with the correct healings), then chain_heal_check simply exits. Otherwise, it specifies the error.
Note that chain_heal_check does not verify that the path is optimal. Just that it is legal.
UNIX> ./chain_heal 2 1 4 500 0.25 < small.txt | chain_heal_check 2 1 4 500 0.25 small.txt UNIX> ./chain_heal 2 1 4 500 0.25 < small.txt | sed 's/James/Shmames/' Adam_the_Warrior 400 Catherine_the_Great 50 Chad_the_Priest 281 Shmames_the_Lightning_Lord 211 Total_Healing 942 UNIX> ./chain_heal 2 1 4 500 0.25 < small.txt | sed 's/James/Shmames/' | chain_heal_check 2 1 4 500 0.25 small.txt ERROR: Shmames_the_Lightning_Lord is not in the original input file UNIX> ./chain_heal 2 1 4 500 0.25 < small.txt | sed 's/211/212/' | chain_heal_check 2 1 4 500 0.25 small.txt ERROR: Incorrect healing amount on line 4 Read 212 but should be 211 UNIX>The grading script works as in CS140 / CS302. If you are not familiar with these kinds of grading scripts, please talk with your TA's.
The way that the grading script works is that it calls the program in the lab directory to get the total healing value. Then it calls your program and checks to see if your total healing value is equal to the correct one. Then it calls chain_heal_check to make sure that your path is a correct one. As you'll note, your path does not have to equal mine -- it simply has to be legal and give the optimal total healing.