|
@@ -28,15 +28,26 @@ Note: the -L option and -lstdc++ may not be needed on some machines.
|
|
|
|
|
|
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;
|
|
|
-const int FPS = 30;
|
|
|
+int FPS = 30;
|
|
|
const int width = 800;
|
|
|
const int height = 600;
|
|
|
const int blockSize = 25;
|
|
|
+int velocity = 5;
|
|
|
+bool paused = true;
|
|
|
+bool starting = true;
|
|
|
|
|
|
/*
|
|
|
* Information to draw on the window.
|
|
@@ -49,9 +60,18 @@ typedef struct XInfo
|
|
|
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.
|
|
|
*/
|
|
@@ -61,6 +81,14 @@ void error(string str)
|
|
|
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.
|
|
@@ -76,7 +104,8 @@ class Fruit : public Displayable
|
|
|
public:
|
|
|
virtual void paint(XInfo & xinfo)
|
|
|
{
|
|
|
- XFillRectangle(xinfo.display, xinfo.window, xinfo.gc[0], x, y, 25, 25);
|
|
|
+ changecolour(xinfo, orange);
|
|
|
+ XFillArc(xinfo.display, xinfo.buffer, xinfo.gc[0], x, y, 25, 25, 0, 360 * 64);
|
|
|
}
|
|
|
|
|
|
Fruit()
|
|
@@ -84,6 +113,16 @@ class Fruit : public Displayable
|
|
|
newspot();
|
|
|
}
|
|
|
|
|
|
+ int getx()
|
|
|
+ {
|
|
|
+ return x;
|
|
|
+ }
|
|
|
+
|
|
|
+ int gety()
|
|
|
+ {
|
|
|
+ return y;
|
|
|
+ }
|
|
|
+
|
|
|
void newspot()
|
|
|
{
|
|
|
x = (rand() % 32) * 25;
|
|
@@ -104,22 +143,23 @@ class Section: public Displayable
|
|
|
public:
|
|
|
virtual void paint(XInfo & xinfo)
|
|
|
{
|
|
|
+ changecolour(xinfo, black);
|
|
|
switch(direction)
|
|
|
{
|
|
|
case 1:
|
|
|
- XFillRectangle(xinfo.display, xinfo.window, xinfo.gc[0], x, y, blockSize, len * blockSize);
|
|
|
+ XFillRectangle(xinfo.display, xinfo.buffer, xinfo.gc[0], x, y, blockSize, (len + 1) * blockSize);
|
|
|
break;
|
|
|
|
|
|
case 2:
|
|
|
- XFillRectangle(xinfo.display, xinfo.window, xinfo.gc[0], (x - len * blockSize), y, len * blockSize, blockSize);
|
|
|
+ XFillRectangle(xinfo.display, xinfo.buffer, xinfo.gc[0], (x - len * blockSize), y, (1 + len) * blockSize, blockSize);
|
|
|
break;
|
|
|
|
|
|
case 3:
|
|
|
- XFillRectangle(xinfo.display, xinfo.window, xinfo.gc[0], x, (y - len * blockSize), blockSize, len * blockSize);
|
|
|
+ XFillRectangle(xinfo.display, xinfo.buffer, xinfo.gc[0], x, (y - len * blockSize), blockSize, ((1 + len) * blockSize));
|
|
|
break;
|
|
|
|
|
|
case 4:
|
|
|
- XFillRectangle(xinfo.display, xinfo.window, xinfo.gc[0], x, y, len * blockSize, blockSize);
|
|
|
+ XFillRectangle(xinfo.display, xinfo.buffer, xinfo.gc[0], x, y, (len + 1) * blockSize, blockSize);
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
@@ -173,6 +213,16 @@ class Section: public Displayable
|
|
|
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;
|
|
@@ -195,10 +245,15 @@ class Snake : public Displayable
|
|
|
{
|
|
|
each->paint(xinfo);
|
|
|
}
|
|
|
+
|
|
|
}
|
|
|
|
|
|
void move(XInfo & xinfo)
|
|
|
{
|
|
|
+ if ((now() - lastmove < speed) || paused)
|
|
|
+ {
|
|
|
+ return;
|
|
|
+ }
|
|
|
// turn if a turn happens
|
|
|
if (direction)
|
|
|
{
|
|
@@ -211,7 +266,7 @@ class Snake : public Displayable
|
|
|
sections.front()->grow();
|
|
|
|
|
|
// see if fruit was eaten
|
|
|
- if (sections.front()->getx() == 50 || sections.front()->gety() == 50)
|
|
|
+ if (sections.front()->getx() == fruity->getx() && sections.front()->gety() == fruity->gety())
|
|
|
{
|
|
|
fruit = 1;
|
|
|
}
|
|
@@ -240,25 +295,60 @@ class Snake : public Displayable
|
|
|
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()
|
|
|
- {
|
|
|
+ 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)))
|
|
|
+ {
|
|
|
+ printf("you're shit\n");
|
|
|
+ delete this;
|
|
|
+ exit(0);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 2:
|
|
|
+ if ((frontx >= (eachx - eachlen * blockSize) && frontx <= eachx) && fronty == eachy)
|
|
|
+ {
|
|
|
+ printf("you're shit\n");
|
|
|
+ delete this;
|
|
|
+ exit(0);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 3:
|
|
|
+ if (frontx == eachx && (fronty <= eachy && fronty >= (eachy - blockSize * eachlen)))
|
|
|
+ {
|
|
|
+ printf("you're shit\n");
|
|
|
+ delete this;
|
|
|
+ exit(0);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 4:
|
|
|
+ if ((frontx <= (eachx - eachlen * blockSize) && frontx >= eachx) && fronty == eachy)
|
|
|
+ {
|
|
|
+ printf("you're shit\n");
|
|
|
+ delete this;
|
|
|
+ exit(0);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- }
|
|
|
+ SKIP:
|
|
|
+ lastmove = now();
|
|
|
+ }
|
|
|
|
|
|
void moveup()
|
|
|
{
|
|
@@ -280,20 +370,47 @@ class Snake : public Displayable
|
|
|
if (direction == 1 || direction == 3 || direction == 0) direction = 4;
|
|
|
}
|
|
|
|
|
|
- Snake(int x, int y, Fruit * fruity): x(x), y(y), fruity(fruity)
|
|
|
+ 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 = 1;
|
|
|
+ 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()
|
|
|
{
|
|
|
- for (auto each = sections.begin(); each != sections.end(); ++each)
|
|
|
+ int size = sections.size();
|
|
|
+
|
|
|
+ for (int i = 0; i < size; i++)
|
|
|
{
|
|
|
- delete *(sections.erase(each));
|
|
|
+ delete sections.at(i);
|
|
|
+ sections.erase(sections.begin() + i);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -303,14 +420,15 @@ class Snake : public Displayable
|
|
|
vector<Section *> sections;
|
|
|
int blockSize;
|
|
|
int direction;
|
|
|
- int speed;
|
|
|
+ unsigned long speed;
|
|
|
int fruit;
|
|
|
Fruit * fruity;
|
|
|
+ unsigned long lastmove;
|
|
|
};
|
|
|
|
|
|
list<Displayable *> dList; // list of Displayables
|
|
|
Fruit fruity;
|
|
|
-Snake snake(100, 450, &fruity);
|
|
|
+Snake snake(100, 450, &fruity, velocity);
|
|
|
|
|
|
/*
|
|
|
* Initialize X and create a window
|
|
@@ -335,6 +453,7 @@ void initX(int argc, char * argv[], XInfo & xInfo)
|
|
|
*/
|
|
|
xInfo.screen = DefaultScreen(xInfo.display);
|
|
|
|
|
|
+ colours = DefaultColormap(xInfo.display, xInfo.screen);
|
|
|
white = XWhitePixel(xInfo.display, xInfo.screen);
|
|
|
black = XBlackPixel(xInfo.display, xInfo.screen);
|
|
|
|
|
@@ -366,11 +485,14 @@ void initX(int argc, char * argv[], XInfo & xInfo)
|
|
|
&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);
|
|
@@ -387,6 +509,20 @@ void initX(int argc, char * argv[], XInfo & xInfo)
|
|
|
XFlush(xInfo.display);
|
|
|
}
|
|
|
|
|
|
+void splash(XInfo & xinfo)
|
|
|
+{
|
|
|
+ //char * title = {'S', 'n', 'a', 'k', 'e', '\0'};
|
|
|
+ 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);
|
|
|
+}
|
|
|
/*
|
|
|
* Function to repaint a display list
|
|
|
*/
|
|
@@ -396,10 +532,13 @@ void repaint(XInfo & xinfo)
|
|
|
list<Displayable *>::const_iterator end = dList.end();
|
|
|
|
|
|
// get height and width of window (might have changed since last repaint)
|
|
|
- XWindowAttributes windowInfo;
|
|
|
+ XWindowAttributes windowInfo;
|
|
|
XGetWindowAttributes(xinfo.display, xinfo.window, &windowInfo);
|
|
|
- unsigned int height = windowInfo.height;
|
|
|
+ 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)
|
|
@@ -408,6 +547,8 @@ void repaint(XInfo & xinfo)
|
|
|
d->paint(xinfo);
|
|
|
begin++;
|
|
|
}
|
|
|
+
|
|
|
+ if (starting) splash(xinfo);
|
|
|
}
|
|
|
|
|
|
void handleKeyPress(XInfo & xinfo, XEvent & event)
|
|
@@ -432,6 +573,17 @@ void handleKeyPress(XInfo & xinfo, XEvent & event)
|
|
|
case 'q':
|
|
|
error("Terminating normally.");
|
|
|
break;
|
|
|
+
|
|
|
+ case ' ':
|
|
|
+ paused = !paused;
|
|
|
+ starting = false;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'r':
|
|
|
+ snake.reset(velocity);
|
|
|
+ paused = true;
|
|
|
+ starting = true;
|
|
|
+ break;
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -458,21 +610,9 @@ void handleKeyPress(XInfo & xinfo, XEvent & event)
|
|
|
|
|
|
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
|
|
@@ -483,10 +623,6 @@ void eventLoop(XInfo & xinfo)
|
|
|
int inside = 0;
|
|
|
XWindowAttributes w;
|
|
|
XGetWindowAttributes(xinfo.display, xinfo.window, &w);
|
|
|
- int depth = DefaultDepth(xinfo.display, DefaultScreen(xinfo.display));
|
|
|
- // represents thing to draw
|
|
|
- Pixmap buffer = XCreatePixmap(xinfo.display, xinfo.window, w.width, w.height, depth);
|
|
|
- bool usebuffer = 1;
|
|
|
|
|
|
while(1)
|
|
|
{
|
|
@@ -506,37 +642,15 @@ void eventLoop(XInfo & xinfo)
|
|
|
inside = 0;
|
|
|
break;
|
|
|
}
|
|
|
- usebuffer = !usebuffer;
|
|
|
}
|
|
|
|
|
|
unsigned long end = now();
|
|
|
|
|
|
if (end - lastRepaint > 1000000 / FPS)
|
|
|
{
|
|
|
- Pixmap pixmap; // represents thing in buffer
|
|
|
-
|
|
|
- if (usebuffer)
|
|
|
- {
|
|
|
- pixmap = buffer;
|
|
|
- XSetForeground(xinfo.display, xinfo.gc[0], BlackPixel(xinfo.display, xinfo.screen));
|
|
|
- XSetBackground(xinfo.display, xinfo.gc[0], WhitePixel(xinfo.display, xinfo.screen));
|
|
|
- XFillRectangle(xinfo.display, pixmap, xinfo.gc[0], 0, 0, w.width, w.height);
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- pixmap = xinfo.window; // set this to the windowInfo
|
|
|
- XClearWindow(xinfo.display, pixmap);
|
|
|
- }
|
|
|
-
|
|
|
- XSetForeground(xinfo.display, xinfo.gc[0], BlackPixel(xinfo.display, xinfo.screen));
|
|
|
- XSetBackground(xinfo.display, xinfo.gc[0], WhitePixel(xinfo.display, xinfo.screen));
|
|
|
handleAnimation(xinfo, inside);
|
|
|
repaint(xinfo);
|
|
|
- if (usebuffer)
|
|
|
- {
|
|
|
- XCopyArea(xinfo.display, pixmap, xinfo.window, xinfo.gc[0], 0, 0 , w.width, w.height, 0, 0);
|
|
|
- }
|
|
|
-
|
|
|
+ XCopyArea(xinfo.display, xinfo.buffer, xinfo.window, xinfo.gc[0], 0, 0 , w.width, w.height, 0, 0);
|
|
|
XFlush(xinfo.display);
|
|
|
lastRepaint = now();
|
|
|
}
|
|
@@ -557,8 +671,22 @@ void eventLoop(XInfo & xinfo)
|
|
|
*/
|
|
|
int main (int argc, char * argv[])
|
|
|
{
|
|
|
- XInfo xInfo;
|
|
|
+ if (argc == 1)
|
|
|
+ {
|
|
|
+ }
|
|
|
+ else if (argc == 3)
|
|
|
+ {
|
|
|
+ FPS = atoi(argv[1]);
|
|
|
+ velocity = atoi(argv[2]);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ printf("you're shit\n");
|
|
|
+ exit(0);
|
|
|
+ }
|
|
|
|
|
|
+ snake.changespeed(velocity);
|
|
|
+ XInfo xInfo;
|
|
|
initX(argc, argv, xInfo);
|
|
|
eventLoop(xInfo);
|
|
|
XCloseDisplay(xInfo.display);
|