//========================================================================= #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.c" //========================================================================= // 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: // //== INCLUDE FILES ======================================================== #include // printf() #include // gotoxy() #include // strcpy(), strncmp(), strncpy() #include // atoi() //== MACRO DEFINITIONS ==================================================== #define EXITFLAG (-1) #define FALSE (0) #define TRUE (!FALSE) //== TOP LEVEL SUPPORE FUNCTION PROTOTYPES ================================ void PrintHeader(void); void Initialize(char mainrail[][5], int *siding_sp); void DrawScreen(char mainrail[][5]); int GetAndDoCommand(char mainrail[][5], char siding[][5], int *siding_sp); //== MAIN FUNCTION ======================================================== int main(void) { char mainrail[10][5]; char siding[3][5]; int siding_sp; PrintHeader(); // Initialize the data structures Initialize(mainrail, &siding_sp); // Draw the initial configuration DrawScreen(mainrail); // Perform User Commands until user enters "exit" while( EXITFLAG != GetAndDoCommand(mainrail, siding, &siding_sp) ); return(0); } //== SUPPORT FUNCTIONS ==================================================== void PrintHeader(void) { printf("========================================" "=======================================\n"); printf("Course....... %s-%i (%s %i)\n", COURSE, SECTION, TERM, YEAR); printf("Programmer... %s (%s)\n", PROGRAMMER, PROG_CODE); printf("Assignment... %s (Rev %i) (Source Code in %s)\n", ASSIGNMENT, REVISION, FILENAME); printf("Description.. %s\n", TITLE); printf(" %s\n", SUBTITLE); printf("========================================" "=======================================\n"); return; } //== LOWER LEVEL FUNCTION PROTOTYPES ====================================== // Main Rail Graphics Routines void DrawMainRail(void); void DrawMainRailLabels(void); void DrawMainRailCars(char mainrail[][5]); void DrawMainRailCar(char *carname, int slot); // Siding Rail Graphics Routines void DrawSidingRail(void); void DrawSidingRailLabels(void); void DrawSidingRailCar(char *carname, int slot); // Command Line Graphics Routines #define CMDLINE (23) #define CMDCOL (12) #define STATUSCOL (32) void DrawCommandLine(void); void EraseOldCommand(void); void EraseNewCommand(void); void MakeNewCommandOld(char *msg); void PrintCommandStatus(int success); // Command Line Processor #define CMD_UNKN (0) #define CMD_PUSH (1) #define CMD_POP (2) #define CMD_MOVE (3) #define CMD_EXIT (4) #define STRINGSMATCH (0) char *GetCommand(char *msg); int CommandType(char *msg); int ExecuteCommand(char main[][5], char side[][5], int *sp, char *msg); int ExecutePush(char main[][5], char side[][5], int *sp, char *msg); int PushCar(char mainrail[][5], char siderail[][5], int *sp, char *carname); int ExecutePop(char main[][5], char side[][5], int *sp, char *msg); int PopCar(char mainrail[][5], char siderail[][5], int *sp, char *carname); int ExecuteMove(char main[][5], char *msg); int MoveCar(char main[][5], char *carname, int to); int ExecuteExit(void); void UpdateCommandLine(char *msg, int success); // Stack Management Primitives #define TEE (5) void FlushSiding(int *sp); int IsSidingEmpty(int *sp); int IsSidingFull(int *sp); void PopPrimitive(char mainrail[][5], char siding[][5], int *sp); void PushPrimitive(char mainrail[][5], char siding[][5], int *sp); // Other Action Primitives void MoveCarPrimitive(char mainrail[][5], int from, int to); int WhereIsCarOnRail(char mainrail[][5], char *carname); int IsPositionOnRailEmpty(char mainrail[][5], int position); int IsTeeEmpty(char mainrail[][5]); int IsNoCarsBetween(char mainrail[][5], int from, int to); //== TOP LEVEL SUPPORT FUNCTION DEFINITIONS =============================== void Initialize(char mainrail[][5], int *sp) { int slot; // TASK: Initialize Main Rail // Put cars in first five slots for(slot = 0; slot < 5; slot++) { strcpy(mainrail[slot], "CAR "); mainrail[slot][3] = (slot+1) + '0'; } // Clear out remaining slots for(slot = 5; slot < 10; slot++) { strcpy(mainrail[slot], ""); } // TASK: Initialize Siding FlushSiding(sp); return; } void DrawScreen(char mainrail[][5]) { // TASK: Draw Main Rail Portion DrawMainRail(); DrawMainRailLabels(); DrawMainRailCars(mainrail); // TASK: Draw Siding Rail Portion DrawSidingRail(); DrawSidingRailLabels(); // TASK: Draw Command Prompt DrawCommandLine(); return; } int GetAndDoCommand(char mainrail[][5], char siding[][5], int *sp) { char msg[81]; int success; GetCommand(msg); success = ExecuteCommand(mainrail, siding, sp, msg); UpdateCommandLine(msg, success); return(success); } // 5) RETURN: success //== LOWER LEVEL SUPPORT FUNCTION DEFINITIONS ============================= //-- Mainrail Graphics Routines ------------------------------------------- void DrawMainRail(void) { int slot; // TASK: Draw Stubs at ends of track gotoxy(9,13); printf("==="); gotoxy(72,13); printf("==="); // TASK: Draw empty rail segments at all positions for(slot = 0; slot < 10; slot++) { gotoxy(6*slot+12, 13); printf("======"); } return; } void DrawMainRailLabels(void) { int slot; // TASK: Draw text labels gotoxy( 3,12); printf("Position:"); gotoxy(37,11); printf("TEE"); // TASK: Draw slot labels for(slot = 0; slot < 10; slot++) { gotoxy(6*slot+14, 12); printf("%2i", slot+1); } return; } void DrawMainRailCars(char mainrail[][5]) { int slot; // TASK: Loop through all slots and draw car/slot for(slot = 0; slot < 10; slot++) DrawMainRailCar(mainrail[slot], slot); return; } void DrawMainRailCar(char *carname, int slot) { gotoxy(6*slot+12, 13); printf("="); if('\0' == carname[0]) printf("===="); else printf("%s", carname); printf("="); return; } //-- Siding Rail Graphics Routines ---------------------------------------- void DrawSidingRail(void) { int slot; // TASK: Draw Siding End gotoxy(38,20); printf("=="); // TASK: Draw Siding segments for(slot = 0; slot < 3; slot++) { gotoxy(38, 19-2*slot); printf("||"); gotoxy(38, 18-2*slot); printf("||"); } return; } void DrawSidingRailLabels(void) { int slot; // TASK: Draw Text Labels gotoxy(31,20); printf("SIDING"); for(slot = 0; slot < 3; slot++) { gotoxy(33, 19-2*slot); printf("%i:", slot+1); } return; } void DrawSidingRailCar(char *carname, int slot) { gotoxy(37, 19-2*slot); if('\0' == carname[0]) printf(" || "); else printf("%s", carname); return; } //-- Command Line Graphics Routines --------------------------------------- void DrawCommandLine(void) { gotoxy(3, CMDLINE); printf("Command: "); return; } void EraseOldCommand(void) { int col; // TASK: Erase old command from line above the command line // REM: Simplest way is to erase the entire line above the commandline. for(col = 1; col <=80; col++) { gotoxy(col, CMDLINE-1); printf(" "); } return; } void EraseNewCommand(void) { int col; // TASK: Erase the new command from the command line for(col = CMDCOL; col <=80; col++) { gotoxy(col, CMDLINE); printf(" "); } // Place cursor at command prompt ready for user to enter next command gotoxy(CMDCOL, CMDLINE); return; } void MakeNewCommandOld(char *msg) { // TASK: Print the new command to the line above the command line gotoxy(CMDCOL, CMDLINE-1); printf("%s", msg); return; } void PrintCommandStatus(int success) { // TASK: Print the command status (on line above the command line) gotoxy(STATUSCOL, CMDLINE-1); if(success) printf("[ FINISHED ]"); else printf("[ UNFINISHED ]"); return; } //-- Command Line Processor ----------------------------------------------- char *GetCommand(char *msg) { // Get command From keyboard and make all upper case fgets(msg, 81, stdin); strupr(msg); return(msg); } int CommandType(char *msg) { if(STRINGSMATCH == strncmp(msg, "PUSH", 4)) return(CMD_PUSH); if(STRINGSMATCH == strncmp(msg, "POP" , 3)) return(CMD_POP); if(STRINGSMATCH == strncmp(msg, "MOVE", 4)) return(CMD_MOVE); if(STRINGSMATCH == strncmp(msg, "EXIT", 4)) return(CMD_EXIT); return(CMD_UNKN); } int ExecuteCommand(char main[][5], char side[][5], int *sp, char *msg) { switch(CommandType(msg)) { case CMD_PUSH: return(ExecutePush(main, side, sp, msg)); case CMD_POP : return(ExecutePop (main, side, sp, msg)); case CMD_MOVE: return(ExecuteMove(main, msg)); case CMD_EXIT: return(ExecuteExit()); default : return(FALSE); } } int ExecutePush(char main[][5], char side[][5], int *sp, char *msg) { char carname[5]; // TASK: Extract the car name from the command string strncpy(carname, &msg[5], 4); carname[4] = '\0'; // TASK: Attempt to move the car return(PushCar(main, side, sp, carname)); } int PushCar(char mainrail[][5], char siderail[][5], int *sp, char *carname) { int success; // 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 // TASK: Check if car is at TEE success = (TEE == WhereIsCarOnRail(mainrail, carname)); // TASK: If command okay so far, check if empty space on siding if(success) // car is at TEE success = !(IsSidingFull(sp)); // TASK: If command okay so far, safe to push car onto siding if(success) PushPrimitive(mainrail, siderail, sp); return(success); } int ExecutePop(char main[][5], char side[][5], int *sp, char *msg) { char carname[5]; // TASK: Extract the car name from the command string strncpy(carname, &msg[4], 4); carname[4] = '\0'; // TASK: Attempt to move the car return(PopCar(main, side, sp, carname)); } int PopCar(char mainrail[][5], char siderail[][5], int *sp, char *carname) { int success; // 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 // TASK: Check if TEE is empty success = IsTeeEmpty(mainrail); // TASK: If command okay so far, check if any cars on siding if(success) // TEE is empty success = !(IsSidingEmpty(sp)); // TASK: If command okay so far, pop car off siding if(success) // TEE is empty and at least one car is on siding { // TASK: Pop the top car off - may not be the right car PopPrimitive(mainrail, siderail, sp); // TASK: Check if car popped was desired car if(TEE != WhereIsCarOnRail(mainrail, carname)) // Wrong car { PushPrimitive(mainrail, siderail, sp); success = FALSE; } else success = TRUE; } return(success); } int ExecuteMove(char mainrail[][5], char *msg) { char carname[5]; int to; // TASK: Extract the car name from the command string strncpy(carname, &msg[5], 4); carname[4] = '\0'; // 1.1) SET: carname = Word(msg, 2) (second word in command string) // TASK: Extract the destination from the command string to = atoi(&msg[13]); // 2.1) SET: to = atoi(Word(msg, 4)) (fourth word in command string) // TASK: Attempt to move the car return(MoveCar(mainrail, carname, to)); } int MoveCar(char mainrail[][5], char *carname, int to) { int success; int from; // 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 // // TASK: Check if car is on main rail success = from = WhereIsCarOnRail(mainrail, carname); // 0 if not on rail // TASK: If command okay so far, check if "to" is a valid position if(success) // car is on main rail success = IsPositionOnRailEmpty(mainrail, to); //TASK: If command okay so far, check if no cars in the way if(success) // car is on main rail and target is empty success = IsNoCarsBetween(mainrail, from, to); // TASK: If everything okay so far, safe to move car if(success) // car is on main rail and target is empty MoveCarPrimitive(mainrail, from, to); return(success); } int ExecuteExit(void) { return(-1); } void UpdateCommandLine(char *msg, int success) { EraseOldCommand(); MakeNewCommandOld(msg); PrintCommandStatus(success); EraseNewCommand(); return; } //========================================================================= // Stack Management Primitives for Siding //========================================================================= void FlushSiding(int *sp) { *sp = 0; } int IsSidingEmpty(int *sp) { if(*sp > 0) return(FALSE); return(TRUE); } int IsSidingFull(int *sp) { if(*sp < 3) return(FALSE); return(TRUE); } void PopPrimitive(char mainrail[][5], char siding[][5], int *sp) { // TASK: Place Car on siding at TEE (*sp)--; strcpy(mainrail[TEE-1], siding[*sp]); DrawSidingRailCar("", *sp); // TASK: Draw Car at the TEE strcpy(mainrail[TEE-1], siding[*sp]); DrawMainRailCar(mainrail[TEE-1], TEE-1); return; } void PushPrimitive(char mainrail[][5], char siding[][5], int *sp) { // TASK: Place Car at TEE onto siding strcpy(siding[*sp], mainrail[TEE-1]); DrawSidingRailCar(siding[*sp], *sp); (*sp)++; // TASK: Clear Car from TEE strcpy(mainrail[TEE-1], ""); DrawMainRailCar(mainrail[TEE-1], TEE-1); return; } //-- Other Action Primitives ---------------------------------------------- // FUNCTION: Word(msg, 2) void MoveCarPrimitive(char mainrail[][5], int from, int to) { // TASK: Copy Name of Car in "from" position to the "to" position strcpy(mainrail[to-1], mainrail[from-1]); // TASK: Clear out Name of Car in "from" position strcpy(mainrail[from-1], ""); // TASK: Draw the new values for the two positions DrawMainRailCar(mainrail[from-1], from-1); DrawMainRailCar(mainrail[to-1], to-1); return; } int WhereIsCarOnRail(char mainrail[][5], char *carname) { int slot; // TASK: Scan mainrail and return position number (1-10) if found for(slot = 0; slot < 10; slot++) if(STRINGSMATCH == strncmp(mainrail[slot], carname, 4)) return(slot+1); // TASK: Car not on mainrail if this point reached - return 0 return(0); } int IsPositionOnRailEmpty(char mainrail[][5], int position) { if('\0' == mainrail[position-1][0]) return(TRUE); return(FALSE); } int IsTeeEmpty(char mainrail[][5]) { return(IsPositionOnRailEmpty(mainrail, TEE)); } int IsNoCarsBetween(char mainrail[][5], int from, int to) { int position; int FirstPositionToCheck; int LastPositionToCheck; // Walk from "from" to "to" and return TRUE only if all slots empty // TASK: Set start and stop based on direction if(from < to) { FirstPositionToCheck = from + 1; // just right of "from" LastPositionToCheck = to - 1; // just left of "to" } else { FirstPositionToCheck = to + 1; // just right of to LastPositionToCheck = from - 1; // just left of from } // TASK: Check the positions, return FALSE if a car is found position = FirstPositionToCheck; while(position <= LastPositionToCheck) { if(!(IsPositionOnRailEmpty(mainrail, position))) return(FALSE); position++; } // no car found if this line reached return(TRUE); }