Description
1. To give you experience in using arrays, pointers, structs, enums, different I/O streams, and writing program that takes arguments. 2. To let you have fun with an application that is extremely captivating. II. Introduction 1. Overview The simple world program we will write for this project simulates a number of creatures running around in a simple world. The world is an m-by-n two-dimensional grid of squares (The number m represents the height of the grid and the number n represents the width of the grid.). Each creature lives in one of the squares, faces in one of the major compass directions (north, east, south, or west), and belongs to a particular species, which determines how that creature behaves. Each square also has a specific terrain type. la ho fl fl ho P P P F F F F P L L P P H H H H Figure 1. A 4-by-4 grid, which contains five creatures. Figure 1 shows a 4-by-4 grid populated by five creatures. Two of them belong to the species flytrap (whose short name is “fl”), two belong to the species hop (whose short name is “ho”), and one belongs to the species landmine (whose short name is “la”). The direction of each creature is represented by the direction of the arrow. For example, the flytrap at the top row is facing east and the flytrap at the bottom row is facing west. The character in each square indicates the terrain type of the square. There are four terrain types: plain (P), lake (L), forest (F), and hill (H) (See Section II-3 for more details). Each creature belongs to one species and each species has an associated program which controls how each creature of that species behaves (See Section II-2 for more details). Each creature may also have some special abilities, including flying and archery (See Section II-4 for more details). Table 1. The list of instructions and their explanations. hop The creature moves forward. If moving forward would put the creature outside the boundaries of the grid, would cause the creature that cannot fly to land in a “lake” square, or would cause the creature to land on top of another creature, the hop instruction does nothing. left The creature turns left 90 degrees to face in a new direction. right The creature turns right 90 degrees to face in a new direction. infect If the square immediately in front of this creature is occupied by a creature of a different species (which we refer to as an “enemy”), that enemy creature is infected to become the same as the infecting species. When a creature is infected, it keeps its position and orientation, but changes its internal species indicator and begins executing the same program as the infecting creature, starting at step 1. If the square immediately in front of this creature is empty, outside the grid, being a forest square, or occupied by a creature of the same species, the infect instruction does nothing. If the creature has the archery ability, its infecting action is different (see Section II-4 for more details). ifempty n If the square in front of the creature is inside the grid boundary and unoccupied, jump to step n of the program; otherwise, go on with the next instruction in sequence. ifenemy n If the square the creature is facing is not a “forest” square and is occupied by a creature of an enemy species, jump to step n; otherwise, go on with the next instruction. ifsame n If the square the creature is facing is not a “forest” square and is occupied by a creature of the same species, jump to step n; otherwise, go on with the next instruction. ifwall n If the creature is facing the border of the grid (which we imagine as consisting of a huge wall), or the creature is facing a lake square and it cannot fly, jump to step n of the program; otherwise, go on with the next instruction in sequence. go n This instruction always jumps to step n, independent of any condition. 2. Species Instructions Each species has an associated program which controls how each creature of that species behaves. Programs are composed of a sequence of instructions. The instructions that can be part of a program are listed in Table 1. There are nine legal instructions in total. The last five instructions have an additional integer argument. Program is an attribute associated with species. Creatures of the same species have the same program. However, different species have different programs. For example, the program of the species flytrap is composed of the following five instructions: ifenemy 4 left go 1 infect go 1 The meaning of each instruction for this example is commented below: (step 1) ifenemy 4 # If there is an enemy ahead, go to step 4 (step 2) left # Turn left (step 3) go 1 # Go to step 1 (step 4) infect # Infect the adjacent creature (step 5) go 1 # Go to step 1 We will simulate the behaviors of all the creatures for a user specified number of rounds. In each round, creatures take their turns one by one, starting from the first creature. After the first creature finishes its turn, the second creature begins its turn. So on and so forth. One round ends with the last creature finishing its turn. Then the next round begins with the first creature taking its turn. Note that during the simulation, a creature may infect another creature so that the infected one changes its species. However, the simulation order of the infected creature does not change. Each creature also maintains a variable called program counter which stores the index of the instruction it is going to execute. On each turn of a creature, it executes a number of instructions of its program, starting from the step indicated by the program counter. A program ordinarily continues with each new instruction in sequence, although this order can be changed by certain instructions in the program such as the if*** instructions. In each turn, a creature can execute any number of if*** or go instructions without relinquishing this turn. Its turn ends only when the creature executes one of the instructions: hop, left, right, or infect. After its turn ends, the creature updates the program counter to point to the next instruction, which will be executed at the beginning of its next turn. Note that each creature maintains its own program counter, so that two different creatures belonging to the same species can have different program counters. The indices of the instructions start from one, i.e., the first instruction of each program is “step 1”. At the very beginning of the simulation process, the program counters of all the creatures are set to their first instructions. 3. Terrain Information In order to simulate the geographical environment of the world, the simple world has the following four terrain types. Each square in the grid belongs to one of these four types. 1. Plain (P): A square of the plain type is represented as P in the grid. A plain square does not have any side effects to a creature in it. 2. Lake (L): A square of the lake type is represented as L in the grid. If a creature cannot fly, it cannot move into a lake square; otherwise, it can move into a lake square. Therefore, a lake square is treated as an impassible wall for a creature that cannot fly. In summary, if a creature is facing a lake and it cannot fly, the ifwall instruction should return true. If the creature is facing a lake and it can fly, the ifwall instruction should return false. 3. Forest (F): A square of the forest type is represented as F in the grid. When a creature is in a forest square, it cannot be seen by another creature. Therefore, a creature in a forest square cannot be infected by an enemy creature, unless the enemy has the archery ability (see Section II-4 for more details on the archery ability). If a creature (no matter whether it has the archery ability or not) is facing another creature that is in a forest square, the ifempty, ifsame, and ifenemy instructions should all return false. 4. Hill (H): A square of the hill type is represented as H in the grid. In a hill square, it is rocky and by default, a creature in that square cannot act quickly. Therefore, a creature in a hill square can do simulation once every two rounds. For example, if a creature just moves from a non-hill square into a hill square in one round (assuming that this is the first round), it should stay idle (i.e., do not execute any instructions) in the second round. Then, in the third round, the creature continues to simulate the next instruction. If, by the end of the third round, the creature still stays in a hill square, it stays idle in the fourth round. It simulates the next instruction in the fifth round. On the other hand, if, by the end of the third round, the creature is in a non-hill square, it simulates the next instruction in the fourth round. Note that if the creature is in the hill square initially, it should be treated as staying idle in the first round; it simulates the next instruction in the second round. Also note that the side effect of a hill square is not applied to a creature that can fly. 4. Special Ability of Creature Each creature might have special abilities (hereafter, referred to as abilities) in the simple world. There are two kinds of abilities: flying (f) and archery (a). Creatures may have no abilities. They may also have multiple abilities. The abilities of a creature will be maintained throughout the whole simulation, even if the creature is infected. Creatures of the same species may have different sets of abilities. Below are the details of these two abilities. 1. Flying (f): A creature with the flying ability can override the restriction of a lake square and a hill square. It can move into a lake square. It can simulate its behavior once every round even if it is in a hill square. If it is in a forest square, the effect of a forest square on it does not change. 2. Archery (a): For a creature A with the archery ability, if it infects, it will infect the first creature of an enemy species (if existing) in its facing direction no matter how far they are away from each other and no matter whether there are creatures of the same species as A in between them. A creature with archery ability can see enemies in forest. Therefore, it can infect the enemy even if the enemy is in a forest square. III. Available Types In completing this project, you will have the following types available to you. They are defined in the file world_type.h. const unsigned int MAXSPECIES = 10; // Max number of species in the // world const unsigned int MAXPROGRAM = 40; // Max size of a species program const unsigned int MAXCREATURES = 50; // Max number of creatures in // the world const unsigned int MAXHEIGHT = 20; // Max height of the grid const unsigned int MAXWIDTH = 20; // Max width of the grid enum direction_t { EAST, SOUTH, WEST, NORTH, DIRECT_SIZE }; /* // Type: direction_t // —————- // This type is used to represent direction, which can take one of // the four values: East, South, West, and North. The last one, // DIRECT_SIZE, can be used to indicate the end of this enum type. // This convention is applied to the other enum types defined below. */ enum terrain_t { PLAIN, LAKE, FOREST, HILL, TERRAIN_SIZE }; /* // Type: terrain_t // —————- // This type is used to represent terrain type of a square, which // can take one of the four values: Plain, Lake, Forest, Hill. */ enum ability_t { FLY, ARCH, ABILITY_SIZE }; /* // Type: ability_t // —————- // This type is used to represent special abilities of a creature, // which can take one of the two values: Flying and Archery. */ enum opcode_t { HOP, LEFT, RIGHT, INFECT, IFEMPTY, IFENEMY, IFSAME, IFWALL, GO, OP_SIZE }; /* // Type: opcode_t // ————- // The type opcode_t is an enumeration of all of the legal // command names. */ struct point_t { int r; int c; }; /* // Type: point_t // ———— // This type is used to represent a point in the grid. // Its component r corresponds to the row number; its component // c corresponds to the column number; */ const string directName[] = {“east”, “south”, “west”, “north”}; // An array of strings representing the direction name. const string directShortName[] = {“e”, “s”, “w”, “n”}; // An array of strings representing the short name of direction. const string terrainName[] = {“plain”, “lake”, “forest”, “hill”}; // An array of strings representing the terrain type name. const string terrainShortName[] = {“P”, “L”, “F”, “H”}; // An array of strings representing the short name of terrain type. const string abilityName[] = {“fly”, “arch”}; // An array of strings representing the ability name. const string abilityShortName[] = {“f”, “a”}; // An array of strings representing the short name of ability. const string opName[] = {“hop”, “left”, “right”, “infect”, “ifempty”, “ifenemy”, “ifsame”, “ifwall”, “go”}; // An array of strings representing the command name. struct instruction_t { opcode_t op; unsigned int address; }; /* // Type: instruction_t // —————— // The type instruction_t is used to represent an // instruction and consists of a pair of an operation // code and an integer. For some operation code, the // integer stores the address of the instruction it jumps // to (e.g., n in the instruction “ifempty n”). The // address is optional. */ struct species_t { string name; unsigned int programSize; instruction_t program[MAXPROGRAM]; }; /* // Type: species_t // —————— // The type species_t is used to represent a species // and consists of a string, an unsigned int, and an array // of instruction_t. The string gives the name of the // species. The unsigned int gives the number of instructions // in the program of the species. The array stores all the // instructions in the program according to their sequence. */ struct creature_t { point_t location; direction_t direction; species_t *species; unsigned int programID; bool ability[ABILITY_SIZE]; bool hillActive; }; /* // Type: creature_t // —————— // The type creature_t is used to represent a creature. It // consists of a point_t, a direction_t, a pointer to // species_t, an unsigned int, a bool array, and a bool variable. // The point_t variable location gives the location of the // species. The direction_t variable direction gives the direction // of the species. The pointer to species_t variable species // points to the species the creature belongs to. The unsigned // int programID gives the index of the instruction to be executed // in the instruction_t array of the species. The bool array // ability indicates the set of abilities the creature has. For // example, if the creature only has FLY ability, you should set // ability[FLY] to true and ability[ARCH] to false. The bool // variable hillActive indicates if the creature is able to simulate // in the current round if the creature is in a hill square. It does // not serve any other purpose if the creature is not in a hill // square. */ struct grid_t { unsigned int height; unsigned int width; creature_t *squares[MAXHEIGHT][MAXWIDTH]; terrain_t terrain[MAXHEIGHT][MAXWIDTH]; }; /* // Type: grid_t // —————— // The type grid_t consists of the height and the width of // the grid, a two-dimensional array of pointers to creature_t, // and a two-dimensional array of terrain_t type. If there is // a creature at the point (r, c) in the grid, then squares[r][c] // stores a pointer to that creature. If point (r, c) is not // occupied by any creature, then squares[r][c] is a NULL pointer. // The two-dimensional array terrain stores the terrain type for // each square in the grid. */ struct world_t { unsigned int numSpecies; species_t species[MAXSPECIES]; unsigned int numCreatures; creature_t creatures[MAXCREATURES]; grid_t grid; }; /* // Type: world_t // ————– // This type consists of two unsigned ints, an array of species_t, // an array of creature_t, and a grid_t object. The first unsigned // int numSpecies specifies the number of species in the creature // world. The second unsigned int numCreatures specifies the number // of creatures in the world. All the species are stored in the array // species and all the creatures are stored in the array creatures. // The grid is given in the object grid. */ IV. File Input All the species, the programs for all the species, and the initial layout of the creature world are stored in files and these files will be read by your program to set up the simulation environment. Note: when you read files, you must use input file stream ifstream. Otherwise, since the files are read-only on our online judge, you may fail to read the files. As we described before, each species has an associated program. The program for each species is stored in a separate file whose name is just the name of that species. For example, the program for the species flytrap is stored in a file called flytrap. A file describing a program contains all the instructions of that program in order. Each line lists just one instruction. The first line lists the first instruction; the second line lists the second instruction; so on and so forth. Each instruction is one of the nine legal instructions described in Table 1. The program ends with the end of file or a blank line. Comments may appear after the blank line or at the end of each instruction line. For example, the program file for the flytrap species looks like: ifenemy 4 If there is an enemy, go to step 4. left If no enemy, turn left. go 1 infect go 1 The flytrap sits in one place and spins. It infects anything which comes in front. Flytraps do well when they clump. Note that in writing functions for reading these program files, you should handle the comments correctly, which means that you should ignore these comments when setting up the program for a species. Since there are many species, we stored all of their program files in a directory. To help you get all the species and their program files, we also have a file telling the directory where the program files are stored and listing all the species. We call this file a species summary. The first line of this file shows the directory where all of the program files are stored. The next lines list all the species, with one species per line. For example, the following is a species summary file: creatures flytrap hop landmine From this file, we can learn that the program files are stored in the directory called creatures within the current working directory. We have three species to simulate, which are flytrap, hop, and landmine. By first reading the species summary file, you will know where to find the program file for each species. Finally, there is a file describing the initial state of the creature world. We call it a world file. The first line of this file gives the height of the two-dimensional grid (i.e., the number of rows) and the second line gives the width of the grid (i.e., the number of columns). Afterwards, it is the terrain layout of the grid. The terrain layout is indicated by a two-dimensional array of characters, with each character being one of ‘P’, ‘L’, ‘F’, and ‘H’, corresponding to plain, lake, forest, and hill types, respectively. The remaining lines of this file describe all the creatures to simulate and their initial directions, locations, and abilities, with one creature per line. Each of these lines has the following format: [ability-1] [ability-2] where is one of the species from the species summary file, describes the initial direction and is one of the strings “east”, “south”, “west”, and “north”. describes the initial row location of the creature. We use the convention that the top-most row of the grid is row 0 and the row number increases from top to bottom. describes the initial column location of the creature. We use the convention that the left-most column of the grid is column 0 and the column number increases from left to right. [ability-i] is optional. It describes the i-th ability of the creature and is one of the characters ‘f’ and ‘a’, corresponding to the flying and archery abilities, respectively. Note there could be no ability entry. There is no specific order on which type should appear first. An example of a world file looks like: 4 4 PPLL PPLL FFHH FFHH hop east 2 0 f a flytrap east 2 2 It says that the size of the grid is 4-by-4 and the top left, top right, bottom left, and bottom right quadrants are plains, lakes, forests, and hills, respectively. There are two creatures in the world. The first creature belongs to the species hop. It faces east and lives at point (2, 0) initially. It has the abilities of flying and archery. The second creature belongs to the species flytrap. It faces east and lives at point (2, 2) initially. It has no special abilities. In the simulation, the order on the creatures to simulate is important. This order is determined by the order that these creatures appear in the world file. V. Program Arguments Your program will obtain the names of the species summary file and the world file via program arguments. Additionally, your program will be told the number of rounds to simulate and whether it should print the simulation result verbosely or not. The expected order of arguments is: [v|verbose] The first three arguments are mandatory. They give the name of the species summary file, the name of the world file, and the number of simulation rounds, respectively. The fourth argument is optional. If the fourth argument is the string “v” or the string “verbose”, your program should print the simulation result verbosely, which will be explained later. Otherwise, if it is omitted or is any other string, your program should print the result concisely, which will also be explained later. If there are more than four arguments, the remaining arguments are ignored. Suppose that you program is called p3. It may be invoked by typing in a terminal: ./p3 species world 10 v Then your program should read the species summary from the file called “species” and the world file from the file called “world”. The number of simulation rounds is 10. Your program should print the simulation information verbosely. VI. Error Checking and Error Message Your program should check for errors before it starts to simulate the moves of the creatures. If any error happens, your program should issue an error message and then exit. If there are no errors happening, then the initial state of the creature world is legal and your program can start simulating the simple world. We require you to do the following error checking and print the error message in exactly the same way as described below. Note that some of the output error message has two lines and each error message should be ended with a newline character. All error messages should be sent to the standard output stream cout; none to the standard error stream cerr. 1. Check whether the number of arguments is less than three. If it is less than three, then one of the mandatory arguments is missing. You should print the following error message: Error: Missing arguments! Usage: ./p3 [v|verbose] 2. Check whether the value supplied by the user is negative. If it is negative, you should print the following error message: Error: Number of simulation rounds is negative! 3. Check whether file open is successful. If opening species summary file, world file, or any species program file fails (for example, the file to be opened does not exist), print the following error message: Error: Cannot open file ! where should be replaced by the name of the file that fails to be opened. If that file is not in the same directory as your program, you need to include its path in the . As you may know, there are multiple ways to specify a path. For us, the path name should be specified in the most basic way, i.e., “

