/* CS 349 A1 Skeleton Code - Snake - - - - - - - - - - - - - - - - - - - - - - Commands to compile and run: g++ -o snake snake.cpp -L/usr/X11R6/lib -lX11 -lstdc++ ./snake Note: the -L option and -lstdc++ may not be needed on some machines. */ #include #include #include #include #include #include #include /* * Header files for X functions */ #include #include using namespace std; /* * Global game state variables */ const int Border = 1; const int BufferSize = 10; const int FPS = 30; const int width = 800; const int height = 600; /* * Information to draw on the window. */ typedef struct XInfo { Display * display; int screen; Window window; GC gc[3]; int width; // size of window int height; } Xinfo; /* * Function to put out a message on error exits. */ void error(string str) { cerr << str << endl; exit(0); } /* * An abstract class representing displayable things. */ class Displayable { public: virtual void paint(XInfo & xinfo) = 0; }; class Snake : public Displayable { public: virtual void paint(XInfo & xinfo) { XFillRectangle(xinfo.display, xinfo.window, xinfo.gc[0], x, y, 25, blockSize); } void move(XInfo &xinfo) { x = x + direction; if (x < 0 || x > width) { direction = -direction; } // ** ADD YOUR LOGIC ** // Here, you will be performing collision detection between the snake, // the fruit, and the obstacles depending on what the snake lands on. } int getX() { return x; } int getY() { return y; } /* * ** ADD YOUR LOGIC ** * Use these placeholder methods as guidance for implementing the snake behaviour. * You do not have to use these methods, feel free to implement your own. */ void didEatFruit() { } void didHitObstacle() { } void turnLeft() { } void turnRight() { } Snake(int x, int y): x(x), y(y) { direction = 5; blockSize = 10; } private: int x; int y; int blockSize; int direction; }; class Fruit : public Displayable { public: virtual void paint(XInfo & xinfo) { XFillRectangle(xinfo.display, xinfo.window, xinfo.gc[0], x, y, 10, 10); } Fruit() { // ** ADD YOUR LOGIC ** // generate the x and y value for the fruit x = 50; y = 50; } // ** ADD YOUR LOGIC ** /* * The fruit needs to be re-generated at new location every time a snake eats it. See the assignment webpage for more details. */ private: int x; int y; }; list dList; // list of Displayables Snake snake(100, 450); Fruit fruit; /* * Initialize X and create a window */ void initX(int argc, char * argv[], XInfo & xInfo) { XSizeHints hints; unsigned long white, black; /* * Display opening uses the DISPLAY environment variable. * It can go wrong if DISPLAY isn't set, or you don't have permission. */ xInfo.display = XOpenDisplay(""); if (!xInfo.display) { error("Can't open display."); } /* * Find out some things about the display you're using. */ xInfo.screen = DefaultScreen(xInfo.display); white = XWhitePixel(xInfo.display, xInfo.screen); black = XBlackPixel(xInfo.display, xInfo.screen); hints.x = 100; hints.y = 100; hints.width = 800; hints.height = 600; hints.flags = PPosition | PSize; xInfo.window = XCreateSimpleWindow ( xInfo.display, // display where window appears DefaultRootWindow( xInfo.display ), // window's parent in window tree hints.x, hints.y, // upper left corner location hints.width, hints.height, // size of the window Border, // width of window's border black, // window border colour white ); // window background colour XSetStandardProperties ( xInfo.display, // display containing the window xInfo.window, // window whose properties are set "animation", // window's title "Animate", // icon's title None, // pixmap for the icon argv, argc, // applications command line args &hints ); // size hints for the window /* * Create Graphics Contexts */ int i = 0; xInfo.gc[i] = XCreateGC(xInfo.display, xInfo.window, 0, 0); XSetForeground(xInfo.display, xInfo.gc[i], BlackPixel(xInfo.display, xInfo.screen)); XSetBackground(xInfo.display, xInfo.gc[i], WhitePixel(xInfo.display, xInfo.screen)); XSetFillStyle(xInfo.display, xInfo.gc[i], FillSolid); XSetLineAttributes(xInfo.display, xInfo.gc[i], 1, LineSolid, CapButt, JoinRound); XSelectInput(xInfo.display, xInfo.window, ButtonPressMask | KeyPressMask | PointerMotionMask | EnterWindowMask | LeaveWindowMask | StructureNotifyMask); // for resize events /* * Put the window on the screen. */ XMapRaised( xInfo.display, xInfo.window ); XFlush(xInfo.display); } /* * Function to repaint a display list */ void repaint( XInfo &xinfo) { list::const_iterator begin = dList.begin(); list::const_iterator end = dList.end(); XClearWindow( xinfo.display, xinfo.window ); // get height and width of window (might have changed since last repaint) XWindowAttributes windowInfo; XGetWindowAttributes(xinfo.display, xinfo.window, &windowInfo); unsigned int height = windowInfo.height; unsigned int width = windowInfo.width; // big black rectangle to clear background // draw display list while (begin != end) { Displayable * d = *begin; d->paint(xinfo); begin++; } XFlush(xinfo.display); } void handleKeyPress(XInfo & xinfo, XEvent & event) { KeySym key; char text[BufferSize]; /* * Exit when 'q' is typed. * This is a simplified approach that does NOT use localization. */ int i = XLookupString ( (XKeyEvent *)&event, // the keyboard event text, // buffer when text will be written BufferSize, // size of the text buffer &key, // workstation-independent key symbol NULL ); // pointer to a composeStatus structure (unused) if (i == 1) { printf("Got key press -- %c\n", text[0]); if (text[0] == 'q') { error("Terminating normally."); } } } void handleAnimation(XInfo & xinfo, int inside) { /* * ADD YOUR OWN LOGIC * This method handles animation for different objects on the screen and readies the next frame before the screen is re-painted. */ snake.move(xinfo); } // get microseconds unsigned long now() { timeval tv; gettimeofday(&tv, NULL); return tv.tv_sec * 1000000 + tv.tv_usec; } void eventLoop(XInfo & xinfo) { // Add stuff to paint to the display list dList.push_front(&snake); dList.push_front(&fruit); XEvent event; unsigned long lastRepaint = 0; int inside = 0; while(1) { /* * This is NOT a performant event loop! * It needs help! */ if (XPending(xinfo.display) > 0) { XNextEvent(xinfo.display, &event); cout << "event.type=" << event.type << "\n"; switch( event.type ) { case KeyPress: handleKeyPress(xinfo, event); break; case EnterNotify: inside = 1; break; case LeaveNotify: inside = 0; break; } } usleep(1000000/FPS); handleAnimation(xinfo, inside); repaint(xinfo); } } /* * Start executing here. * First initialize window. * Next loop responding to events. * Exit forcing window manager to clean up - cheesy, but easy. */ int main (int argc, char * argv[]) { XInfo xInfo; initX(argc, argv, xInfo); eventLoop(xInfo); XCloseDisplay(xInfo.display); }