//========================================================================= #define PROGRAMMER "SOLUTIONS, NoFrills" #define PROG_CODE "soln" #define COURSE "ECE-1021" #define YEAR (2003) #define TERM "Fall" #define SECTION (0) #define ASSIGNMENT "HW #5A" #define REVISION (0) #define TITLE "Railway Stack" #define SUBTITLE "Problem 7.14" #define EMAIL "wbahn@eas.uccs.edu" #define FILENAME "05a0soln.txt" //========================================================================= //========================================================================= // NOTE: Assignment #5A Pseudocode starts at line 277 (approx) //========================================================================= // PROBLEM: // // Write a program that allows the user to enter commands to move cars // around a simply railway section. The section consists of a rail with // ten slots and a three-slot siding that connect to the main rail at // the fifth position from the left. // // The user can enter the following command: // // 1) exit // 2) push // 3) pop // 4) move to // // Where is the string that exactly matches the name of one of // the case and is one of the ten positions on the main rail. // // Once the user enters a command, the command string will be moved up // one line and any prior command there will be erased. If the program // can complete the requested action, it will print out [FINISHED] after // the command otherwise it will print [UNFISHED]. An unfinished request // should not change the position of any cars. // // The railway will be represented on the screen as follows: // // TEE // Postion: 1 2 3 4 5 6 7 8 9 10 // ====CAR1========CAR2==========================CAR3================ // || // 3: || // || // 2: CAR5 // || // 1: CAR4 // SIDING == // // move CAR3 to 8 [FINISHED] // Command: // // The top level pseudocode is as follows: // REM: two data blocks, one for main rail and one for siding // 1) TASK: Initialize the data structures. // 1.1) CALL: Initialize(mainrail, siding) // 2) TASK: Draw the initial configuration. // 2.1) CALL: DrawScreen(mainrail, siding) // 3) WHILE( exitflag != GetAndDoCommand(main, siding) ) // At this point, the following functions need to be implemented: // // 1) Initialize(mainrail, siding) // 2) DrawScreen(mainrail, siding) // 3) GetAndDoCommand(main, siding) // FUNCTION: DrawScreen() // TASK: Draw the initial configuration. // 1) TASK: Draw Main Rail Portion // 1.1) TASK: Draw Main Rail // 1.1.1) CALL: DrawMainRail() // 1.2) TASK: Draw Main Rail Labels // 1.2.1) CALL: DrawMainRailLabels() // 1.3) TASK: Draw Cars on Main Rail // 1.3.1) CALL: DrawMainRailCars(mainrail) // 2) TASK: Draw Siding Rail Portion // 2.1) TASK: Draw Siding Rail // 2.1.1) CALL: DrawSidingRail() // 2.2) TASK: Draw Siding Rail Labels // 2.2.1) CALL: DrawSidingRailLabels() // 2.3) TASK: Draw Cars on Siding Rail // 2.3.1) CALL: DrawSidingRailCars(siding) // 3) TASK: Draw Command Line // 3.1) CALL: DrawCommandLine() // FUNCTION: GetAndDoCommand() // 1) CALL: GetCommand(message) // 2) SET: success = ExecuteCommand(message) // 4) CALL: UpdateCommandLine(message, success) // 5) RETURN: success // FUNCTION: GetCommand(char *msg) // 1) TASK: Scan a string into msg // 2) RETURN: msg // FUNCTION: UpdateCommandLine(message, success) // 1) TASK: Erase old command from line above the command line // 1.1) CALL: EraseOldCommand() // 2) TASK: Erase the new command from the command line // 2.1) CALL: EraseNewCommand() // 3) TASK: Print the new command to the line above the command line // 3.1) CALL: MakeNewCommandOld() // 4) TASK: Print the command status (on line above the command line) // 4.1) CALL: PrintCommandStatus(success) // 5) RETURN: // FUNCTION: PrintCommandStatus(int success) // 1) IF: (success) // 1.1) PUT: "[FINISHED]" after command (on line above the command line) // 2) ELSE: // 2.1) PUT: "[UNFINISHED]" after command (on line above the command line) // 3) RETURN: // FUNCTION: ExecuteCommand(message) // 1) TASK: Determine what the command type is // 2) SET: success = 0 // 3) IF: (command is to push) // 3.1) SET: success = ExecutePush(message) // 4) IF: (command is to pop) // 4.1) SET: success = ExecutePop(message) // 5) IF: (command is to move) // 5.1) SET: success = ExecuteMove(message) // 6) IF: (command is to exit) // 6.1) SET: success = ExecuteExit(message) // 7: RETURN: success // FUNCTION: ExecuteExit(char *msg) // 1) TASK: Perform any pre-exit tasks // 2) RETURN: -1 // FUNCTION: ExecuteMove(char *msg) // 1) TASK: Extract the car name from the command string // 1.1) SET: carname = Word(msg, 2) (second word in command string) // 2) TASK: Extract the destination from the command string // 2.1) SET: to = atoi(Word(msg, 4)) (fourth word in command string) // 3) TASK: Attempt to move the car // 3.1) success = MoveCar(carname, to) // 4) RETURN: success // FUNCTION MoveCar(char *carname, int to) // TASK: Attempt to move a car along main rail. // // To move a car, the following conditions must all be true: // 1) The car must be on the main rail // 2) The target position must be empty // 3) There must be no cars between the car and the target position // // 1) TASK: Check if car is on main rail // 1.1) SET: success = from = WhereIsCarOnRail(carname) // 0 if not on rail // 2) TASK: If command okay so far, check if "to" is a valid position // 2.1) IF: (success) // car is on main rail // 2.1.1) SET: success = IsPositionOnRailEmpty(to) // 3) TASK: If command okay so far, check if no cars in the way // 3.1) IF: (success) // car is on main rail and target is empty // 3.1.1) SET: success = IsNoCarsBetween(from, to) // 4) TASK: If everything okay so far, safe to move car // 4.1) IF: (success) // car is on main rail and target is empty // 4.1.1) success = MoveCarPrimitive(from, to) (should always succeed) // 5) RETURN: success // We will handle the other two commands in a similar way. // FUNCTION ExecutePop(char *msg) // 1) TASK: Extract the car name from the command string // 1.1) SET: carname = Word(msg, 2) (second word in command string) // 2) TASK: Attempt to move the car // 2.1) success = PopCar(carname, to) // 4) RETURN: success // FUNCTION PopCar(char *carname, int to) // TASK: Attempt to pop a car off the siding (to the rail TEE) // // To successfully pop a car off the siding, the following conditions // must all be true: // // 1) The TEE (Position #5) must be empty // 2) The car must be the first car on the siding // // 1) TASK: Check if TEE is empty // 1.1) SET: success = IsTeeEmpty(mainrail) // 2) TASK: If command okay so far, check if any cars on siding // 2.1) IF: (success) // car is on main rail // 2.1.1) SET: success = IsSidingEmpty(siding) // 3) TASK: If command okay so far, pop car off siding // 3.1) IF: (success) // car is on main rail and target is empty // 3.1.1) SET: success = PopPrimitive(mainrail, siding) (should succeed) // 4) TASK: Check if car popped was desired car // 4.1) IF: (success) // A car was popped - but might be wrong car // 4.1.1) IF: (TEE != WhereIsCarOnRail(carname)) // 4.1.1.1) PushPrimitive(mainrail, siding) // 4.1.1.2) SET: success = 0 // 5) RETURN: success // FUNCTION ExecutePush(char *msg) // 1) TASK: Extract the car name from the command string // 1.1) SET: carname = Word(msg, 2) (second word in command string) // 2) TASK: Attempt to move the car // 2.1) success = PushCar(carname, to) // 4) RETURN: success // FUNCTION PushCar(char *carname, int to) // TASK: Attempt to push a car onto the siding (to the rail TEE) // // To successfully push a car onto the siding, the following conditions // must all be true: // // 1) The car must be at the TEE (Position #5) // 2) The siding must not be full // // 1) TASK: Check if car is at TEE // 1.1) success = (TEE != WhereIsCarOnRail(carname)) // 2) TASK: If command okay so far, check if empty space on siding // 2.1) IF: (success) // car is at TEE // 2.1.1) SET: success = !(IsSidingFull(siding)) // 3) TASK: If command okay so far, safe to push car onto siding // 3.1) IF: (success) // // 3.1.1) SET: success = PushPrimitive(mainrail, siding) (should succeed) // 5) RETURN: success // At this point, the following functions need to be implemented: // // Initialize(mainrail, siding) // DrawMainRail() // DrawMainRailLabels() // DrawMainRailCars(mainrail) // DrawSidingRail() // DrawSidingRailLabels() // DrawSidingRailCars(siding) // DrawCommandLine() // UpdateCommandLine(message, success) // EraseOldCommand() // EraseNewCommand() // MakeNewCommandOld() // Word(msg, 2) // WhereIsCarOnRail(carname) // IsPositionOnRailEmpty(to) // IsNoCarsBetween(from, to) // MoveCarPrimitive(from, to) // IsTeeEmpty(mainrail) // IsSidingEmpty(siding) // PopPrimitive(mainrail, siding) // PushPrimitive(mainrail, siding) // IsSidingFull(siding) // While this is a long list, each of these functions is very simple // and basic - many only being one or two lines of code. // // For your pseudocode submission, flesh out these functions to the // point where they can be translated to code easily. Keep in mind that // the arguments that are needed are not necessary all listed with // the function name above - the pseudocode represents the logic and // it is frequently necessary to pass housekeeping information to // functions. To the degree that you can identify what information must // be passed and list it with the function's pseudocode, you should. It // will make coding that much easier. But, when all is said and done, // this is strictly an implementation issue and pseudocode is ideally // implementation independent. // // For instance: // // FUNCTION: EraseOldCommand(int cmdline) // TASK: Simplest way is to erase the entire line above the commandline. // 1) SET: col = 1 // 2) LOOP: // 2.1) CALL: gotoxy(col, cmdline-1) // 2.2) PUT: ' ' // 2.1) SET: col++ // 2.3) WHILE: (col <= 80) // 3) RETURN: //========================================================================= // PSEUDOCODE FOR FUNCTIONS NOT PROVIDED //========================================================================= // Following Diagram copied from above for reference. // // TEE // Postion: 1 2 3 4 5 6 7 8 9 10 // ====CAR1========CAR2==========================CAR3================ // || // 3: || // || // 2: CAR5 // || // 1: CAR4 // SIDING == // // move CAR3 to 8 [FINISHED] // Command: // // FUNCTION: Initialize(mainrail, siding, siding_ptr) // 1) TASK: Initialize Main Rail // 1.1) TASK: Put cars in first five slots // 1.1.1) SET: slot = 0 // 1.1.2) WHILE: slot < 5 // 1.1.2.1) mainrail[slot] = "CAR"+(i+1) (e.g., CAR1, CAR5) // 1.2) TASK: Clear out remaining slots // 1.2.1) SET: slot = 6 // 1.2.2) WHILE: slot < 10 // 1.2.2.1) mainrail[slot] = "" // 2) TASK: Initialize Siding // 2.1) CALL: FlushSiding(siding, siding_ptr) // FUNCTION: DrawMainRail() // 1) TASK: Draw Stubs at ends of track // 1.1) SET: cursor to x,y = 9,13 // 1.2) PUT: "===" // 1.1) SET: cursor to x,y = 72,13 // 1.2) PUT: "===" // 2) TASK: Draw empty rail segments at all positions // 2.1) SET: slot = 0 // 2.2) WHILE: slot < 10 // 2.2.1) SET: cursor to x,y = 6*slot+12, 13 // 2.2.2) PUT: "======" // FUNCTION: DrawMainRailLabels() // 1) TASK: Draw text labels // 1.1) SET: cursor to x,y = 3, 12 // 1.2) PUT: "Position:" // 1.3) SET: cursor to x,y = 37, 11 // 1.4) PUT: "TEE" // 2) TASK: Draw slot labels // 2.1) SET: slot = 0 // 2.2) WHILE: slot < 10 // 2.2.1) SET: cursor to x,y = 6*slot+14, 12 // 2.2.2) PUT: (i+1) (field width of 2) // FUNCTION: DrawMainRailCars(mainrail) // 1) TASK: Loop through all slots and draw car/slot // 1.1) SET: slot = 0; // 1.2) WHILE: slot < 10 // 1.2.1) CALL: DrawMailRailCar(mainrail[slot], slot) // FUNCTION: DrawMainRailCar(carname, slot) // 1) SET: cursor to x,y = 6*slot+12, 13 // 2) PUT: "=" // 3) IF: carname = "" // 3.1) PUT: "====" // 4) ELSE: // 4.1) PUT: carname // 5) PUT: "=" // FUNCTION: DrawSidingRail() // 1) TASK: Draw Siding End // 1.1) SET: cursor to x,y = 38,20 // 1.2) PUT: "==" // 2) TASK: Draw Siding segments // 2.1) SET: slot = 0 // 2.2) WHILE: slot < 3 // 2.2.1) SET: cursor to x,y = 38, 19-2*slot // 2.2.2) PUT: "||" // 2.2.1) SET: cursor to x,y = 38, 18-2*slot // 2.2.2) PUT: "||" // // FUNCTION: DrawSidingRailLabels() // 1) TASK: Draw Text Labels // 1.1) SET: cursor to x,y = 31,20 // 1.2) PUT: "SIDING" // 2) TASK: Draw Slot Labels // 2.1) SET: slot = 0 // 2.2) WHILE: slot < 3 // 2.2.1) SET: cursor to x,y = 33, 19-2*slot // 2.2.2) PUT: slot+":" // FUNCTION: DrawSidingRailCars(siding) // Initially empty - Will rely on the Stack Functions to update // FUNCTION: DrawSidingRailCar(carname, slot) // 1) SET: cursor to x,y = 37, 19-2*slot // 2) IF: carname = "" // 2.1) PUT: " || " // 3) ELSE: // 3.1) PUT: carname // FUNCTION: DrawCommandLine() // 1) SET: cursor to x,y = 3, CMDLINE // 2) PUT: "Command: " // FUNCTION: UpdateCommandLine(message, success) // The pseudocode for this was already above and is copied below since it // defines the behavior of four other functions // for the following // 1) TASK: Erase old command from line above the command line // 1.1) CALL: EraseOldCommand() // 2) TASK: Erase the new command from the command line // 2.1) CALL: EraseNewCommand() // 3) TASK: Print the new command to the line above the command line // 3.1) CALL: MakeNewCommandOld() // 4) TASK: Print the command status (on line above the command line) // 4.1) CALL: PrintCommandStatus(success) // 5) RETURN: // FUNCTION: EraseOldCommand() // TASK: Erase old command from line above the command line // REM: Simplest way is to erase the entire line above the commandline. // 1) SET: col = 1 // 2) LOOP: // 2.1) CALL: gotoxy(col, CMDLINE-1) // 2.2) PUT: ' ' // 2.1) SET: col++ // 2.3) WHILE: (col <= 80) // 3) RETURN: // FUNCTION: EraseNewCommand() // TASK: Erase the new command from the command line // REM: Simplest way is to erase everything from command prompt on // 1) SET: col = CMDCOL // 2) LOOP: // 2.1) SET: cursor to x,y = col, CMDLINE // 2.2) PUT: ' ' // 2.1) SET: col++ // 2.3) WHILE: (col <= 80) // 3) RETURN: // FUNCTION: MakeNewCommandOld(msg) // TASK: Print the new command to the line above the command line // 1) SET: cursor to x,y = CMDCOL, CMDLINE-1 // 2) PUT: msg // 3) RETURN: // FUNCTION: PrintCommandStatus(success) // TASK: Print the command status (on line above the command line) // 1) SET: cursor to x,y = STATUS, CMDLINE-1 // 2) IF: success // 2.1) PUT: [ FINISHED ] // 3) ELSE: // 3.1) PUT: [ UNFINISHED ] // 4) RETURN: // FUNCTION: Word(msg, 2) // FUNCTION: WhereIsCarOnRail(mainrail, carname) // 1) TASK: Scan mainrail and return slot number (1-10) if found // 1.1) SET: slot = 0 // 1.2) WHILE: slot < 10 // 1.2.1) IF: mainrail[slot] == carname // 1.2.1.1) RETURN: slot+1 // 2) TASK: Car not on mainrail if this point reached - return 0 // 2.1) RETURN: 0 // FUNCTION: IsPositionOnRailEmpty(mainrail, position) // 1) IF(mainrail[position-1] == "") // 1.1) RETURN: TRUE // 2) ELSE: // 2.1) RETURN: FALSE // FUNCTION: IsNoCarsBetween(from, to) // TASK: Walk from "from" to "to" and return TRUE only if all slots empty // 1) TASK: Set start and stop based on direction // 1.2) IF: (from < to) // 1.2.1) SET: FirstPositionToCheck = from+1 // to right of from // 1.2.2) SET: LastPositionToCheck = to-1 // to left of to // 1.3) ELSE: // 1.2.1) SET: FirstPositionToCheck = (to-1)+1 // to right of to // 1.2.2) SET: LastPositionToCheck = (from-1)-1 // to left of from // 2) TASK: Check the psotions, return FALSE if a car is found // 2.1) SET: position = FirstPositionToCheck // 2.2) LOOP: // 2.2.1) IF: NOT(IsPositionOnRailEmpty(mainrail, position)) // 2.2.1.1) RETURN: FALSE // 2.2.2) SET: position++ // 2.3) WHILE: position < LastPositionToCheck // 2.4) RETURN: TRUE // no car found if this line reached // FUNCTION: MoveCarPrimitive(mainrail, from, to) // 1) TASK: Copy Name of Car in "from" position to the "to" position // 1.1) CALL: strcpy(mainrail[to-1], mainrail[from-1]) // 2) TASK: Clear out Name of Car in "from" position // 2.1) CALL: strcpy(mainrail[to-1], "") // 3) TASK: Draw the new values for the two positions // 1.2) CALL: DrawMainRailCar(mainrail[from-1], from-1) // 1.2) CALL: DrawMainRailCar(mainrail[to-1], to-1) // FUNCTION: IsTeeEmpty(mainrail) // 1) RETURN: IsPositionOnRailEmpty(TEE) // FUNCTION: IsSidingEmpty(siding_ptr) // 1) IF: (siding_ptr > 0) // 1.1) RETURN: FALSE // 2) ELSE: // 2.1) RETURN: TRUE // FUNCTION: PopPrimitive(mainrail, siding, siding_ptr) // 1) TASK: Place Car on siding at TEE // 1.1) SET: siding_ptr-- // 1.2) CALL: strcpy(mainrail[TEE-1], siding[siding_ptr]) // 1.3) DrawSidingRailCar("", sidint_ptr) // 2) TASK: Draw Car at the TEE // 2.1) CALL: strcpy(mainrail[TEE-1], "") // 2.2) CALL: DrawMainRailCar(mainrail[TEE-1], TEE-1) // FUNCTION: PushPrimitive(mainrail, siding, siding_ptr) // 1) TASK: Place Car at TEE onto siding // 1.1) CALL: strcpy(siding[siding_ptr], mainrail[TEE-1]) // 1.2) DrawSidingRailCar(siding[siding_ptr], sidint_ptr) // 1.2) SET: siding_ptr++ // 2) TASK: Clear Car from TEE // 2.1) CALL: strcpy(mainrail[TEE-1], "") // 2.2) CALL: DrawMainRailCar(mainrail[TEE-1], TEE-1)) // FUNCTION: IsSidingFull(siding_ptr) // 1) IF: (siding_ptr < 3) // 1.1) RETURN: FALSE // 2) ELSE: // 2.1) RETURN: TRUE