123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707 |
- /*
- 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;
- // Globals for xlib
- Colormap colours;
- XColor xcolour;
- char black[] = "rgb:00/00/00";
- char white[] = "rgb:ff/ff/ff";
- char lblue[] = "rgb:00/ff/ff";
- char orange[] = "rgb:FF/45/00";
- /*
- * Global game state variables
- */
- const int Border = 1;
- const int BufferSize = 10;
- int FPS = 30;
- const int width = 800;
- const int height = 600;
- const int blockSize = 25;
- int velocity = 5;
- bool paused = true;
- bool starting = true;
- bool dead = false;
- /*
- * 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;
- Pixmap buffer;
- } Xinfo;
- // get microseconds
- unsigned long now()
- {
- timeval tv;
- gettimeofday(&tv, NULL);
- return tv.tv_sec * 1000000 + tv.tv_usec;
- }
- /*
- * Function to put out a message on error exits.
- */
- void error(string str)
- {
- cerr << str << endl;
- exit(0);
- }
- // change the drawing coloursvoid changecolour(char colour[])
- void changecolour(XInfo & xinfo, char colour[])
- {
- XParseColor(xinfo.display, colours, colour, &xcolour);
- XAllocColor(xinfo.display, colours, &xcolour);
- XSetForeground(xinfo.display, xinfo.gc[0], xcolour.pixel);
- XFreeColormap(xinfo.display, colours);
- }
- /*
- * 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)
- {
- changecolour(xinfo, orange);
- XFillArc(xinfo.display, xinfo.buffer, xinfo.gc[0], x, y, 25, 25, 0, 360 * 64);
- }
- Fruit()
- {
- newspot();
- }
- int getx()
- {
- return x;
- }
- int gety()
- {
- return y;
- }
- void newspot()
- {
- x = (rand() % 32) * 25;
- y = (rand() % 24) * 25;
- }
- // ** 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)
- {
- changecolour(xinfo, black);
- switch(direction)
- {
- case 1:
- XFillRectangle(xinfo.display, xinfo.buffer, xinfo.gc[0], x, y, blockSize, (len + 1) * blockSize);
- break;
- case 2:
- XFillRectangle(xinfo.display, xinfo.buffer, xinfo.gc[0], (x - len * blockSize), y, (1 + len) * blockSize, blockSize);
- break;
- case 3:
- XFillRectangle(xinfo.display, xinfo.buffer, xinfo.gc[0], x, (y - len * blockSize), blockSize, ((1 + len) * blockSize));
- break;
- case 4:
- XFillRectangle(xinfo.display, xinfo.buffer, xinfo.gc[0], x, y, (len + 1) * 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;
- }
- int getdir()
- {
- return direction;
- }
- int getlen()
- {
- return len;
- }
- 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)
- {
- if ((now() - lastmove < speed) || paused)
- {
- return;
- }
- // 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() == fruity->getx() && sections.front()->gety() == fruity->gety())
- {
- 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)
- {
- dead = true;
- paused = true;
- }
- if (sections.front()->getx() < 0 || sections.front()->gety() > height)
- {
- dead = true;
- paused = true;
- }
- if (sections.size() <= 3) goto SKIP;
- for (auto each = sections.begin() + 1; each != sections.end(); ++each)
- {
- int tempdir = (*each)->getdir();
- int frontx = sections.front()->getx();
- int fronty = sections.front()->gety();
- int eachx = (*each)->getx();
- int eachy = (*each)->gety();
- int eachlen = (*each)->getlen();
- switch(tempdir)
- {
- case 1:
- if (frontx == eachx && (fronty >= eachy && fronty <= (eachy + blockSize * eachlen)))
- {
- dead = true;
- paused = true;
- }
- break;
- case 2:
- if ((frontx >= (eachx - eachlen * blockSize) && frontx <= eachx) && fronty == eachy)
- {
- dead = true;
- paused = true;
- }
- break;
- case 3:
- if (frontx == eachx && (fronty <= eachy && fronty >= (eachy - blockSize * eachlen)))
- {
- dead = true;
- paused = true;
- }
- break;
- case 4:
- if ((frontx <= (eachx - eachlen * blockSize) && frontx >= eachx) && fronty == eachy)
- {
- dead = true;
- paused = true;
- }
- break;
- }
- }
- SKIP:
- lastmove = now();
- }
- 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;
- }
- void changespeed(unsigned long x)
- {
- speed = 250000 / x;
- }
- void reset(int fast)
- {
- int size = sections.size();
- for (int i = 0; i < size; i++)
- {
- delete sections.at(i);
- sections.erase(sections.begin() + i);
- }
- Section * newsec = new Section(x, y, 2, 2);
- sections.insert(sections.begin(), newsec);
- direction = 0;
- blockSize = 25;
- speed = 250000 / fast;
- lastmove = 0;
- }
- Snake(int x, int y, Fruit * fruity, unsigned long fast): x(x), y(y), fruity(fruity)
- {
- Section * newsec = new Section(x, y, 2, 2);
- sections.insert(sections.begin(), newsec);
- direction = 0;
- blockSize = 25;
- speed = 250000 / fast;
- lastmove = 0;
- }
- ~Snake()
- {
- int size = sections.size();
- for (int i = 0; i < size; i++)
- {
- delete sections.at(i);
- sections.erase(sections.begin() + i);
- }
- }
- private:
- int x;
- int y;
- vector<Section *> sections;
- int blockSize;
- int direction;
- unsigned long speed;
- int fruit;
- Fruit * fruity;
- unsigned long lastmove;
- };
- list<Displayable *> dList; // list of Displayables
- Fruit fruity;
- Snake snake(100, 450, &fruity, velocity);
- /*
- * 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);
- colours = DefaultColormap(xInfo.display, xInfo.screen);
- 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
- int depth = DefaultDepth(xInfo.display, DefaultScreen(xInfo.display));
- xInfo.buffer = XCreatePixmap(xInfo.display, xInfo.window, hints.width, hints.height, depth);
- /*
- * Create Graphics Contexts
- */
- int i = 0;
- xInfo.gc[i] = XCreateGC(xInfo.display, xInfo.window, 0, 0);
- XSetBackground(xInfo.display, xInfo.gc[0], 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); // for resize events
- /*
- * Put the window on the screen.
- */
- XMapRaised( xInfo.display, xInfo.window );
- XFlush(xInfo.display);
- }
- void splash(XInfo & xinfo)
- {
- changecolour(xinfo, white);
- XFillRectangle(xinfo.display, xinfo.buffer, xinfo.gc[0], 100, 100, 600, 400);
- changecolour(xinfo, black);
- XFontStruct * font = XLoadQueryFont(xinfo.display, "12x24");
- XSetFont(xinfo.display, xinfo.gc[0], font->fid);
- XDrawString(xinfo.display, xinfo.buffer, xinfo.gc[0], 350, 200, "Snake", 5);
- XDrawString(xinfo.display, xinfo.buffer, xinfo.gc[0], 340, 240, "tsdedhar", 8);
- XDrawString(xinfo.display, xinfo.buffer, xinfo.gc[0], 340, 280, "20621325", 8);
- XDrawString(xinfo.display, xinfo.buffer, xinfo.gc[0], 265, 320, "Use arrow keys to turn", 22);
- XDrawString(xinfo.display, xinfo.buffer, xinfo.gc[0], 200, 360, "Collect fruit to grow your snake", 32);
- }
- void endgame(XInfo & xinfo)
- {
- changecolour(xinfo, white);
- XFillRectangle(xinfo.display, xinfo.buffer, xinfo.gc[0], 100, 100, 600, 400);
- changecolour(xinfo, black);
- XFontStruct * font = XLoadQueryFont(xinfo.display, "12x24");
- XSetFont(xinfo.display, xinfo.gc[0], font->fid);
- XDrawString(xinfo.display, xinfo.buffer, xinfo.gc[0], 347, 200, "Game Over!", 10);
- XDrawString(xinfo.display, xinfo.buffer, xinfo.gc[0], 340, 240, "Press r to restart", 18);
- XDrawString(xinfo.display, xinfo.buffer, xinfo.gc[0], 342, 280, "Press q to quit", 15);
- }
- /*
- * Function to repaint a display list
- */
- void repaint(XInfo & xinfo)
- {
- list<Displayable *>::const_iterator begin = dList.begin();
- list<Displayable *>::const_iterator end = dList.end();
- // 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;
- changecolour(xinfo, lblue);
- XFillRectangle(xinfo.display, xinfo.buffer, xinfo.gc[0], 0, 0, width, height);
- changecolour(xinfo, black);
- // draw display list
- while (begin != end)
- {
- Displayable * d = *begin;
- d->paint(xinfo);
- begin++;
- }
- if (starting) splash(xinfo);
- if (dead)
- {
- endgame(xinfo);
- }
- }
- void handleKeyPress(XInfo & xinfo, XEvent & event)
- {
- KeySym key;
- char text[BufferSize];
- 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
- );
- if (i == 1)
- {
- printf("Got key press -- %c\n", text[0]);
- switch(text[0])
- {
- case 'q':
- error("Terminating normally.");
- break;
- case ' ':
- paused = !paused;
- starting = false;
- break;
- case 'r':
- snake.reset(velocity);
- paused = true;
- starting = true;
- dead = false;
- 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)
- {
- snake.move(xinfo);
- }
- 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;
- XWindowAttributes w;
- XGetWindowAttributes(xinfo.display, xinfo.window, &w);
- while(1)
- {
- if (XPending(xinfo.display) > 0)
- {
- XNextEvent(xinfo.display, &event);
- switch(event.type)
- {
- case KeyPress:
- handleKeyPress(xinfo, event);
- break;
- case EnterNotify:
- inside = 1;
- break;
- case LeaveNotify:
- inside = 0;
- break;
- }
- }
- unsigned long end = now();
- if (end - lastRepaint > 1000000 / FPS)
- {
- handleAnimation(xinfo, inside);
- repaint(xinfo);
- XCopyArea(xinfo.display, xinfo.buffer, xinfo.window, xinfo.gc[0], 0, 0 , w.width, w.height, 0, 0);
- XFlush(xinfo.display);
- lastRepaint = now();
- }
- if (XPending(xinfo.display) == 0)
- {
- usleep(1000000 / FPS - (end - lastRepaint));
- }
- }
- }
- /*
- * 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[])
- {
- if (argc == 1)
- {
- }
- else if (argc == 3)
- {
- FPS = atoi(argv[1]);
- velocity = atoi(argv[2]);
- if (atoi(argv[2]) < 1 || atoi(argv[2]) > 10) goto BADARGS;
- }
- else
- {
- BADARGS:
- printf("Incorrect arguments supplied\n");
- exit(0);
- }
- snake.changespeed(velocity);
- XInfo xInfo;
- initX(argc, argv, xInfo);
- eventLoop(xInfo);
- XCloseDisplay(xInfo.display);
- }
|