123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546 |
- /*
- 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 <iostream>
- #include <list>
- #include <vector>
- #include <cstdlib>
- #include <sys/time.h>
- #include <math.h>
- #include <stdio.h>
- #include <unistd.h>
- /*
- * Header files for X functions
- */
- #include <X11/Xlib.h>
- #include <X11/Xutil.h>
- 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;
- const int blockSize = 25;
- /*
- * 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 Fruit : public Displayable
- {
- public:
- virtual void paint(XInfo & xinfo)
- {
- XFillRectangle(xinfo.display, xinfo.window, xinfo.gc[0], x, y, 25, 25);
- }
- Fruit()
- {
- newspot();
- }
- void newspot()
- {
- x = (rand() % 800);
- y = (rand() % 600);
- }
- // ** 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;
- };
- class Section: public Displayable
- {
- public:
- virtual void paint(XInfo & xinfo)
- {
- switch(direction)
- {
- case 1:
- XFillRectangle(xinfo.display, xinfo.window, xinfo.gc[0], x, y, blockSize, len * blockSize);
- break;
- case 2:
- XFillRectangle(xinfo.display, xinfo.window, xinfo.gc[0], (x - len * blockSize), y, len * blockSize, blockSize);
- break;
- case 3:
- XFillRectangle(xinfo.display, xinfo.window, xinfo.gc[0], x, (y - len * blockSize), blockSize, len * blockSize);
- break;
- case 4:
- XFillRectangle(xinfo.display, xinfo.window, xinfo.gc[0], x, y, len * blockSize, blockSize);
- break;
- }
- }
- void increment(int x)
- {
- len += x;
- }
- void decrement()
- {
- --len;
- }
- bool empty()
- {
- return (!len);
- }
- void grow()
- {
- switch(direction)
- {
- case 1:
- y -= blockSize;
- break;
- case 2:
- x += blockSize;
- break;
- case 3:
- y += blockSize;
- break;
- case 4:
- x -= blockSize;
- break;
- }
- ++len;
- }
- int getx()
- {
- return x;
- }
- int gety()
- {
- return y;
- }
- Section(int x, int y, int dir, int len = 1): x(x), y(y), direction(dir), len(len)
- {
- blockSize = 25;
- }
- private:
- int x;
- int y;
- int len;
- int blockSize;
- int direction;
- };
- class Snake : public Displayable
- {
- public:
- virtual void paint(XInfo & xinfo)
- {
- for (Section * each: sections)
- {
- each->paint(xinfo);
- }
- }
- void move(XInfo & xinfo)
- {
- // turn if a turn happens
- if (direction)
- {
- Section * newsec = new Section(sections.front()->getx(), sections.front()->gety(), direction, 0);
- direction = 0;
- sections.insert(sections.begin(), newsec);
- }
- // move 1 block forwards
- sections.front()->grow();
- // see if fruit was eaten
- if (sections.front()->getx() == 50 || sections.front()->gety() == 50)
- {
- fruit = 1;
- }
- // add fruit, remove 1 from end, delete if empty
- sections.back()->increment(fruit);
- if (fruit) fruity->newspot();
- fruit = 0;
- sections.back()->decrement();
- if (sections.back()->empty())
- {
- delete sections.back();
- sections.pop_back();
- }
- if (sections.front()->getx() > width || sections.front()->gety() < 0)
- {
- printf("you're shit\n");
- delete this;
- exit(0);
- }
- if (sections.front()->getx() < 0 || sections.front()->gety() > height)
- {
- printf("you're shit\n");
- delete this;
- exit(0);
- }
- // ** 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.
- }
- /*
- * ** 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 moveup()
- {
- if (direction == 4 || direction == 2 || direction == 0) direction = 1;
- }
- void moveright()
- {
- if (direction == 1 || direction == 3 || direction == 0) direction = 2;
- }
- void movedown()
- {
- if (direction == 4 || direction == 2 || direction == 0) direction = 3;
- }
- void moveleft()
- {
- if (direction == 1 || direction == 3 || direction == 0) direction = 4;
- }
- Snake(int x, int y, Fruit * fruity): x(x), y(y), fruity(fruity)
- {
- Section * newsec = new Section(x, y, 2, 2);
- sections.insert(sections.begin(), newsec);
- direction = 0;
- blockSize = 25;
- speed = 1;
- }
- ~Snake()
- {
- for (auto each = sections.begin(); each != sections.end(); ++each)
- {
- delete *(sections.erase(each));
- }
- }
- private:
- int x;
- int y;
- vector<Section *> sections;
- int blockSize;
- int direction;
- int speed;
- int fruit;
- Fruit * fruity;
- };
- list<Displayable *> dList; // list of Displayables
- Fruit fruity;
- Snake snake(100, 450, &fruity);
- /*
- * 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<Displayable *>::const_iterator begin = dList.begin();
- list<Displayable *>::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]);
- switch(text[0])
- {
- case 'q':
- error("Terminating normally.");
- break;
- }
- }
- if (key == XK_Up)
- {
- snake.moveup();
- }
- if (key == XK_Right)
- {
- snake.moveright();
- }
- if (key == XK_Down)
- {
- snake.movedown();
- }
- if (key == XK_Left)
- {
- snake.moveleft();
- }
- }
- 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(&fruity);
- 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);
- }
|