/* 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. */ struct XInfo { Display *display; int screen; Window window; GC gc[3]; int width; // size of window int height; }; /* * 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( true ) { /* * 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); }