// Copyright 1999 // College of Computer Science // Northeastern University Boston MA 02115 // This software may be used for educational purposes as long as this copyright // notice is retained at the top of all files // Should this software be modified, the words "Modified from Original" must be // included as a comment below this notice // All publication rights are retained. This software or its documentation may // not be published in any media either in whole or in part. /////////////////////////////////////////////////////////////////////////////// // Shell.cpp // // The common start program for student and faculty development /////////////////////////////////////////////////////////////////////////////// // The standard include files that include traditional C and C++ headers and // many other Core Tools headers ... see CHeaders.h for additional details #include "IOTools.h" #include "Graphics.h" #include "Random.h" #include "RGBNames.h" // Enter project specific include files here as well as classes and functions // that you choose to define in the main shell rather than in separate files enum { CellSize = 23, // size of underwater maze cell and fish picture GridSize = 24, // size of underwater maze grid InsetSize = 9, // amount to inset cell for visit mark Rows = 320 / GridSize, // number of rows in maze structure Cols = 640 / GridSize, // number of cols in maze structure X0 = (640 - GridSize * Cols) / 2, // X coordinate of upper left of grid Y0 = (320 - GridSize * Rows) / 2, // Y coordinate of upper left of grid Empty = 0, // value assigned to empty maze cell Solid = 1, // value assigned to solid maze cell LargeFishID = 128, // resource ID of picture of large fish SmallFishID = 129, // resource ID of picture of school of small fish Up = 0, Down = 1, Left = 2, Right = 3 }; // ¥¥¥ UTILITY ROUTINES // adjust shifts row, col location (r, c) // within bounds of maze inline void Adjust(short& r, short& c) { if (r < 0) r = 0; if (r >= Rows) r = Rows - 1; if (c < 0) c = 0; if (c >= Cols) c = Cols - 1; } // return x or y coordinate of col, row grid position // concentrates maze to screen transforms in one spot inline short X(short col) { return (X0 + GridSize * col); } inline short Y(short row) { return (Y0 + GridSize * row); } // ¥¥¥ CLASS DEFINITIONS // structure for location or cell within maze class Spot { short row; // row location in maze short col; // col location in maze public: // constructor Spot(short r = 0, short c = 0) { Adjust(r, c); row = r; col = c; }; // update data Spot& Set(short r = 0, short c = 0) { Adjust(r, c); row = r; col = c; return *this; }; // return data void Get(short& r, short& c) { r = row; c = col; }; // return cell rect corresponding to spot RectData Cell() { short x1 = X(col) + 1; // offset by 1 to miss grid line short y1 = Y(row) + 1; // offset by 1 to miss grid line short x2 = x1 + CellSize; // shift by cell size short y2 = y1 + CellSize; // shift by cell size RectData R; SetRect(R, x1, y1, x2, y2); return R; }; // move spot Up, Down, Left, or Right Spot& Move(short direction) { int r = row; int c = col; switch (direction) { case Up: r--; break; case Down: r++; break; case Left: c--; break; case Right: c++; break; default: break; } return Set(r, c); }; friend bool operator== (Spot S1, Spot S2) { return (S1.row == S2.row) && (S1.col == S2.col); }; }; class Fish { short id; // resource ID of image Spot spot; // location of fish in maze public: // constructor Fish(short ID): id(ID) { // id = LargeFish or SmallFish id = ID; }; // destructor // default // set location Fish& SetSpot(Spot s) { spot = s; return *this; }; // get location Spot GetSpot() { return spot; }; // draw fish at its current spot Fish& Draw() { RectData R = spot.Cell(); // get image boundary rectangle int Lft, Top, Rht, Btm; R.Get(Lft, Top, Rht, Btm); ///*** draw here *** if (id == LargeFishID){ SetForeColor(255, 255, 255); // white PaintRect(R); // clear the surrounding rectangle SetForeColor(100, 100, 255); // blue PaintOval(Lft + 1, Top + 5, Rht - 1, Btm - 5); // body DrawLine(Lft + 1, Top + 5, Lft + 1, Btm - 5); // tail DrawLine(Lft + 2, Top + 6, Lft + 2, Btm - 6); // tail } else if (id == SmallFishID){ SetForeColor(0, 255, 255); PaintOval(Lft + 3, Top + 3, Lft + 13, Top + 8); // top left fish PaintOval(Lft + 3, Btm - 8, Lft + 13, Btm - 3); // bottom left fish PaintOval(Rht - 13, Top + 10, Rht - 3, Top + 15); } SetForeColor(0, 0, 0); return *this; }; // move fish location Up, Down, Left, or Right // this function simply changes internal data // graphics refresh requires knowledge of maze // and will therefore be done later Fish& Move(short direction) { spot.Move(direction); return *this; }; }; class Maze { short Block[Rows][Cols]; // Each block should contain Empty or Solid public: Maze() { // Initialize Block to Empty for (short row = 0; row < Rows; row++) for (short col = 0; col < Cols; col++) Block[row][col] = Empty; }; Maze& MakeMaze(); // Create maze // Define this routine below short Data(Spot S) { // Return cell data short row; short col; S.Get(row, col); return Block[row][col]; }; short Data(short row, short col) { // Return cell data return Block[row][col]; }; Maze& DrawCell(Spot S) { // Draw the cell short row; short col; S.Get(row, col); if (Block[row][col] == Solid) // Set color SetForeColor(RGBblack); else SetForeColor(RGBwhite); RectData R = S.Cell(); // Find cell PaintRect(R); // Paint cell SetForeColor(RGBblack); // Reset color to black return *this; }; Maze& DrawCell(short row, short col) { // Draw the cell return DrawCell(Spot(row, col)); }; Maze& DrawGrid() { // Draw maze grid SetForeColor(RGBblack); // Set color to match solid color // row lines for (short row = 0; row <= Rows; row++) { short y = Y(row); short x1 = X(0); short x2 = X(Cols); DrawLine(x1, y, x2, y); } // col lines for (short col = 0; col <= Cols; col++) { short x = X(col); short y1 = Y(0); short y2 = Y(Rows); DrawLine(x, y1, x, y2); } return *this; }; Maze& DrawData() { // Draw maze data for (short row = 0; row < Rows; row++) for (short col = 0; col < Cols; col++) DrawCell(row, col); return *this; }; Maze& Draw() { // Draw both maze grid and maze data DrawGrid(); DrawData(); return *this; }; bool FreeToMove(Spot S, short direction) { // Check freedom to move bool result = false; // default is false short row; short col; S.Get(row, col); switch (direction) { case Up: if (row > 0) result = Block[--row][col] == Empty; break; case Down: if (row < (Rows - 1)) result = Block[++row][col] == Empty; break; case Left: if (col > 0) result = Block[row][--col] == Empty; break; case Right: if (col < (Cols - 1)) result = Block[row][++col] == Empty; break; default: break; } return result; }; }; // Definition of Maze::MakeMaze Maze& Maze::MakeMaze() { short Lower[Cols]; // lower index of an empty cell in a particular col short Upper[Cols]; // upper index of an empty cell in a particular col short min = 2; // minimum number of empty cells in col short max = 8; // maximum number of empty cells in col short n; // number of empty cells in current col // stage 1: randomly set lower and upper bounds of empty cells // col 0 n = RandomLong(min, max); Lower[0] = RandomLong(0, Rows - n); Upper[0] = Lower[0] + n - 1; // other cols for (short col = 1; col < Cols; col++) { // initial random setting n = RandomLong(min, max); Lower[col] = RandomLong(0, Rows - n); Upper[col] = Lower[col] + n - 1; // shift if needed to insure overlap with previous col int p = col - 1; // previous col if (Upper[col] < Lower[p]) { // upper too low? short s = Lower[p] - Upper[col]; Lower[col] += s; Upper[col] += s; } else if (Upper[p] < Lower[col]) { // lower too high? short s = Lower[col] - Upper[p]; Lower[col] -= s; Upper[col] -= s; } } // stage 2: fill maze data structure for (col = 0; col < Cols; col++) for (int row = 0; row < Rows; row++) if (Lower[col] <= row && row <= Upper[col]) Block[row][col] = Empty; else Block[row][col] = Solid; return *this; } // ¥¥¥ GLOBAL OBJECTS & VARIABLES Maze Cave; // environments Fish LargeFish(LargeFishID); // predator Fish SmallFish(SmallFishID); // prey // Function Prototypes void MainInit(); // general purpose inits void SwimmingFish(); // swimming fish driver void SFInit(); // swimming fish inits void Search(bool fast); // search driver // ¥?¥?¥ The main search-for-food routines to be designed by students void FastSearch(); void CompleteSearch(); // The three tools to be used in Search void MoveFish(short direction); // move large fish in this direction bool FreeToMove(short direction); // is large fish free to move in this direction? bool FoundFood(); // has large fish found small fish? // A convenient utility void MarkVisit(Spot S); // mark spot visited by large fish // Function Definitions int main(int argc, char* argv[]) { // Use the following line if you choose NOT to open any graphics windows // InitializeConsole(); // Build graphics window 0 GraphicsWindow GW0(635, 317); // Move the console below graphics window 0 ConsolePlaceBelow(0); // Give the console the focus for user interaction MakeConsoleForeground(); // Start up the random number generator SetRandomSeed(); SwimmingFish(); // play swimming fish game ////////// // The lines below make sure that the graphics windows remain open just // before the program terminates PressReturn("\nThe main program is about to terminate\n"); return 0; } void SwimmingFish(void) { // swimming fish driver // opening message cout << "Search for Food" << endl << endl; bool fast = true; // main loop to create game and search many times do { SFInit(); // initialize cave and fish fast = Confirm("Fast Search?", fast); // request type of search Search(fast); // search for food } while (Confirm("Another Search?", true)); } // SwimmingFish void SFInit() { // swimming fish inits // create cave Cave.MakeMaze().Draw(); // insert fish int row; int col; // find an empty location in left column for large fish col = 0; // left column do { row = RandomLong(0, Rows - 1); // random row } while (Cave.Data(row, col) != Empty); // stop when cell empty LargeFish.SetSpot(Spot(row, col)).Draw(); // find an empty location in right column for small fish col = Cols - 1; // right column do { row = RandomLong(0, Rows - 1); // random row } while (Cave.Data(row, col) != Empty); // stop when cell empty SmallFish.SetSpot(Spot(row, col)).Draw(); } // SFInit void Search(bool fast) { // catch UserAbort from delay tools here // permits fast exit from search if bugs detected at run time if (fast) FastSearch(); else CompleteSearch(); } // Search // ¥?¥?¥ The main search-for-food routines to be designed by students void FastSearch () { // ¥?¥?¥ Fill in code here } void CompleteSearch () { // ¥?¥?¥ Fill in code here if time permits } // The three tools to be used in Search void MoveFish(short direction) { // move large fish in this direction // find info about old location Spot S = LargeFish.GetSpot(); // draw cave background (solid or empty) in old location Cave.DrawCell(S); // mark visit of large fish at old location MarkVisit(S); // move and draw large fish in new location LargeFish.Move(direction).Draw(); Sleep(50); } // MoveFish bool FreeToMove(short direction) { // is large fish free to move in this direction? return Cave.FreeToMove(LargeFish.GetSpot(), direction); } // FreeToMove bool FoundFood() { // has large fish found small fish? return LargeFish.GetSpot() == SmallFish.GetSpot(); } // FoundFood // A convenient utility void MarkVisit(Spot S) { SetForeColor(RGBred); // set color RectData R = S.Cell(); // find cell of spot InsetRect(R, InsetSize, InsetSize); // shrink cell PaintRect(R); // draw smaller cell SetForeColor(RGBblack); // reset color to black } // MarkVisit