snake.cc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565
  1. /*
  2. CS 349 A1 Skeleton Code - Snake
  3. - - - - - - - - - - - - - - - - - - - - - -
  4. Commands to compile and run:
  5. g++ -o snake snake.cpp -L/usr/X11R6/lib -lX11 -lstdc++
  6. ./snake
  7. Note: the -L option and -lstdc++ may not be needed on some machines.
  8. */
  9. #include <iostream>
  10. #include <list>
  11. #include <vector>
  12. #include <cstdlib>
  13. #include <sys/time.h>
  14. #include <math.h>
  15. #include <stdio.h>
  16. #include <unistd.h>
  17. /*
  18. * Header files for X functions
  19. */
  20. #include <X11/Xlib.h>
  21. #include <X11/Xutil.h>
  22. using namespace std;
  23. /*
  24. * Global game state variables
  25. */
  26. const int Border = 1;
  27. const int BufferSize = 10;
  28. const int FPS = 30;
  29. const int width = 800;
  30. const int height = 600;
  31. const int blockSize = 25;
  32. /*
  33. * Information to draw on the window.
  34. */
  35. typedef struct XInfo
  36. {
  37. Display * display;
  38. int screen;
  39. Window window;
  40. GC gc[3];
  41. int width; // size of window
  42. int height;
  43. } Xinfo;
  44. /*
  45. * Function to put out a message on error exits.
  46. */
  47. void error(string str)
  48. {
  49. cerr << str << endl;
  50. exit(0);
  51. }
  52. /*
  53. * An abstract class representing displayable things.
  54. */
  55. class Displayable
  56. {
  57. public:
  58. virtual void paint(XInfo & xinfo) = 0;
  59. };
  60. class Fruit : public Displayable
  61. {
  62. public:
  63. virtual void paint(XInfo & xinfo)
  64. {
  65. XFillRectangle(xinfo.display, xinfo.window, xinfo.gc[0], x, y, 25, 25);
  66. }
  67. Fruit()
  68. {
  69. newspot();
  70. }
  71. void newspot()
  72. {
  73. x = (rand() % 800);
  74. y = (rand() % 600);
  75. }
  76. // ** ADD YOUR LOGIC **
  77. /*
  78. * The fruit needs to be re-generated at new location every time a snake eats it. See the assignment webpage for more details.
  79. */
  80. private:
  81. int x;
  82. int y;
  83. };
  84. class Section: public Displayable
  85. {
  86. public:
  87. virtual void paint(XInfo & xinfo)
  88. {
  89. switch(direction)
  90. {
  91. case 1:
  92. XFillRectangle(xinfo.display, xinfo.window, xinfo.gc[0], x, y, blockSize, len * blockSize);
  93. break;
  94. case 2:
  95. XFillRectangle(xinfo.display, xinfo.window, xinfo.gc[0], (x - len * blockSize), y, len * blockSize, blockSize);
  96. break;
  97. case 3:
  98. XFillRectangle(xinfo.display, xinfo.window, xinfo.gc[0], x, (y - len * blockSize), blockSize, len * blockSize);
  99. break;
  100. case 4:
  101. XFillRectangle(xinfo.display, xinfo.window, xinfo.gc[0], x, y, len * blockSize, blockSize);
  102. break;
  103. }
  104. }
  105. void increment(int x)
  106. {
  107. len += x;
  108. }
  109. void decrement()
  110. {
  111. --len;
  112. }
  113. bool empty()
  114. {
  115. return (!len);
  116. }
  117. void grow()
  118. {
  119. switch(direction)
  120. {
  121. case 1:
  122. y -= blockSize;
  123. break;
  124. case 2:
  125. x += blockSize;
  126. break;
  127. case 3:
  128. y += blockSize;
  129. break;
  130. case 4:
  131. x -= blockSize;
  132. break;
  133. }
  134. ++len;
  135. }
  136. int getx()
  137. {
  138. return x;
  139. }
  140. int gety()
  141. {
  142. return y;
  143. }
  144. Section(int x, int y, int dir, int len = 1): x(x), y(y), direction(dir), len(len)
  145. {
  146. blockSize = 25;
  147. }
  148. private:
  149. int x;
  150. int y;
  151. int len;
  152. int blockSize;
  153. int direction;
  154. };
  155. class Snake : public Displayable
  156. {
  157. public:
  158. virtual void paint(XInfo & xinfo)
  159. {
  160. for (Section * each: sections)
  161. {
  162. each->paint(xinfo);
  163. }
  164. }
  165. void move(XInfo & xinfo)
  166. {
  167. // turn if a turn happens
  168. if (direction)
  169. {
  170. Section * newsec = new Section(sections.front()->getx(), sections.front()->gety(), direction, 0);
  171. direction = 0;
  172. sections.insert(sections.begin(), newsec);
  173. }
  174. // move 1 block forwards
  175. sections.front()->grow();
  176. // see if fruit was eaten
  177. if (sections.front()->getx() == 50 || sections.front()->gety() == 50)
  178. {
  179. fruit = 1;
  180. }
  181. // add fruit, remove 1 from end, delete if empty
  182. sections.back()->increment(fruit);
  183. if (fruit) fruity->newspot();
  184. fruit = 0;
  185. sections.back()->decrement();
  186. if (sections.back()->empty())
  187. {
  188. delete sections.back();
  189. sections.pop_back();
  190. }
  191. if (sections.front()->getx() > width || sections.front()->gety() < 0)
  192. {
  193. printf("you're shit\n");
  194. delete this;
  195. exit(0);
  196. }
  197. if (sections.front()->getx() < 0 || sections.front()->gety() > height)
  198. {
  199. printf("you're shit\n");
  200. delete this;
  201. exit(0);
  202. }
  203. // ** ADD YOUR LOGIC **
  204. // Here, you will be performing collision detection between the snake,
  205. // the fruit, and the obstacles depending on what the snake lands on.
  206. }
  207. /*
  208. * ** ADD YOUR LOGIC **
  209. * Use these placeholder methods as guidance for implementing the snake behaviour.
  210. * You do not have to use these methods, feel free to implement your own.
  211. */
  212. void didEatFruit()
  213. {
  214. }
  215. void didHitObstacle()
  216. {
  217. }
  218. void moveup()
  219. {
  220. if (direction == 4 || direction == 2 || direction == 0) direction = 1;
  221. }
  222. void moveright()
  223. {
  224. if (direction == 1 || direction == 3 || direction == 0) direction = 2;
  225. }
  226. void movedown()
  227. {
  228. if (direction == 4 || direction == 2 || direction == 0) direction = 3;
  229. }
  230. void moveleft()
  231. {
  232. if (direction == 1 || direction == 3 || direction == 0) direction = 4;
  233. }
  234. Snake(int x, int y, Fruit * fruity): x(x), y(y), fruity(fruity)
  235. {
  236. Section * newsec = new Section(x, y, 2, 2);
  237. sections.insert(sections.begin(), newsec);
  238. direction = 0;
  239. blockSize = 25;
  240. speed = 1;
  241. }
  242. ~Snake()
  243. {
  244. for (auto each = sections.begin(); each != sections.end(); ++each)
  245. {
  246. delete *(sections.erase(each));
  247. }
  248. }
  249. private:
  250. int x;
  251. int y;
  252. vector<Section *> sections;
  253. int blockSize;
  254. int direction;
  255. int speed;
  256. int fruit;
  257. Fruit * fruity;
  258. };
  259. list<Displayable *> dList; // list of Displayables
  260. Fruit fruity;
  261. Snake snake(100, 450, &fruity);
  262. /*
  263. * Initialize X and create a window
  264. */
  265. void initX(int argc, char * argv[], XInfo & xInfo)
  266. {
  267. XSizeHints hints;
  268. unsigned long white, black;
  269. /*
  270. * Display opening uses the DISPLAY environment variable.
  271. * It can go wrong if DISPLAY isn't set, or you don't have permission.
  272. */
  273. xInfo.display = XOpenDisplay("");
  274. if (!xInfo.display)
  275. {
  276. error("Can't open display.");
  277. }
  278. /*
  279. * Find out some things about the display you're using.
  280. */
  281. xInfo.screen = DefaultScreen(xInfo.display);
  282. white = XWhitePixel(xInfo.display, xInfo.screen);
  283. black = XBlackPixel(xInfo.display, xInfo.screen);
  284. hints.x = 100;
  285. hints.y = 100;
  286. hints.width = 800;
  287. hints.height = 600;
  288. hints.flags = PPosition | PSize;
  289. xInfo.window = XCreateSimpleWindow
  290. (
  291. xInfo.display, // display where window appears
  292. DefaultRootWindow( xInfo.display ), // window's parent in window tree
  293. hints.x, hints.y, // upper left corner location
  294. hints.width, hints.height, // size of the window
  295. Border, // width of window's border
  296. black, // window border colour
  297. white
  298. ); // window background colour
  299. XSetStandardProperties
  300. (
  301. xInfo.display, // display containing the window
  302. xInfo.window, // window whose properties are set
  303. "animation", // window's title
  304. "Animate", // icon's title
  305. None, // pixmap for the icon
  306. argv, argc, // applications command line args
  307. &hints
  308. ); // size hints for the window
  309. /*
  310. * Create Graphics Contexts
  311. */
  312. int i = 0;
  313. xInfo.gc[i] = XCreateGC(xInfo.display, xInfo.window, 0, 0);
  314. XSetFillStyle(xInfo.display, xInfo.gc[i], FillSolid);
  315. XSetLineAttributes(xInfo.display, xInfo.gc[i],
  316. 1, LineSolid, CapButt, JoinRound);
  317. XSelectInput(xInfo.display, xInfo.window,
  318. ButtonPressMask | KeyPressMask |
  319. PointerMotionMask |
  320. EnterWindowMask | LeaveWindowMask); // for resize events
  321. /*
  322. * Put the window on the screen.
  323. */
  324. XMapRaised( xInfo.display, xInfo.window );
  325. XFlush(xInfo.display);
  326. }
  327. /*
  328. * Function to repaint a display list
  329. */
  330. void repaint(XInfo & xinfo)
  331. {
  332. list<Displayable *>::const_iterator begin = dList.begin();
  333. list<Displayable *>::const_iterator end = dList.end();
  334. // get height and width of window (might have changed since last repaint)
  335. XWindowAttributes windowInfo;
  336. XGetWindowAttributes(xinfo.display, xinfo.window, &windowInfo);
  337. unsigned int height = windowInfo.height;
  338. unsigned int width = windowInfo.width;
  339. // draw display list
  340. while (begin != end)
  341. {
  342. Displayable * d = *begin;
  343. d->paint(xinfo);
  344. begin++;
  345. }
  346. }
  347. void handleKeyPress(XInfo & xinfo, XEvent & event)
  348. {
  349. KeySym key;
  350. char text[BufferSize];
  351. int i = XLookupString
  352. (
  353. (XKeyEvent *)&event, // the keyboard event
  354. text, // buffer when text will be written
  355. BufferSize, // size of the text buffer
  356. &key, // workstation-independent key symbol
  357. NULL
  358. );
  359. if (i == 1)
  360. {
  361. printf("Got key press -- %c\n", text[0]);
  362. switch(text[0])
  363. {
  364. case 'q':
  365. error("Terminating normally.");
  366. break;
  367. }
  368. }
  369. if (key == XK_Up)
  370. {
  371. snake.moveup();
  372. }
  373. if (key == XK_Right)
  374. {
  375. snake.moveright();
  376. }
  377. if (key == XK_Down)
  378. {
  379. snake.movedown();
  380. }
  381. if (key == XK_Left)
  382. {
  383. snake.moveleft();
  384. }
  385. }
  386. void handleAnimation(XInfo & xinfo, int inside)
  387. {
  388. /*
  389. * ADD YOUR OWN LOGIC
  390. * This method handles animation for different objects on the screen and readies the next frame before the screen is re-painted.
  391. */
  392. snake.move(xinfo);
  393. }
  394. // get microseconds
  395. unsigned long now()
  396. {
  397. timeval tv;
  398. gettimeofday(&tv, NULL);
  399. return tv.tv_sec * 1000000 + tv.tv_usec;
  400. }
  401. void eventLoop(XInfo & xinfo)
  402. {
  403. // Add stuff to paint to the display list
  404. dList.push_front(&snake);
  405. dList.push_front(&fruity);
  406. XEvent event;
  407. unsigned long lastRepaint = 0;
  408. int inside = 0;
  409. XWindowAttributes w;
  410. XGetWindowAttributes(xinfo.display, xinfo.window, &w);
  411. int depth = DefaultDepth(xinfo.display, DefaultScreen(xinfo.display));
  412. // represents thing to draw
  413. Pixmap buffer = XCreatePixmap(xinfo.display, xinfo.window, w.width, w.height, depth);
  414. bool usebuffer = 1;
  415. while(1)
  416. {
  417. if (XPending(xinfo.display) > 0)
  418. {
  419. XNextEvent(xinfo.display, &event);
  420. switch(event.type)
  421. {
  422. case KeyPress:
  423. handleKeyPress(xinfo, event);
  424. break;
  425. case EnterNotify:
  426. inside = 1;
  427. break;
  428. case LeaveNotify:
  429. inside = 0;
  430. break;
  431. }
  432. usebuffer = !usebuffer;
  433. }
  434. unsigned long end = now();
  435. if (end - lastRepaint > 1000000 / FPS)
  436. {
  437. Pixmap pixmap; // represents thing in buffer
  438. if (usebuffer)
  439. {
  440. pixmap = buffer;
  441. XSetForeground(xinfo.display, xinfo.gc[0], BlackPixel(xinfo.display, xinfo.screen));
  442. XSetBackground(xinfo.display, xinfo.gc[0], WhitePixel(xinfo.display, xinfo.screen));
  443. XFillRectangle(xinfo.display, pixmap, xinfo.gc[0], 0, 0, w.width, w.height);
  444. }
  445. else
  446. {
  447. pixmap = xinfo.window; // set this to the windowInfo
  448. XClearWindow(xinfo.display, pixmap);
  449. }
  450. XSetForeground(xinfo.display, xinfo.gc[0], BlackPixel(xinfo.display, xinfo.screen));
  451. XSetBackground(xinfo.display, xinfo.gc[0], WhitePixel(xinfo.display, xinfo.screen));
  452. handleAnimation(xinfo, inside);
  453. repaint(xinfo);
  454. if (usebuffer)
  455. {
  456. XCopyArea(xinfo.display, pixmap, xinfo.window, xinfo.gc[0], 0, 0 , w.width, w.height, 0, 0);
  457. }
  458. XFlush(xinfo.display);
  459. lastRepaint = now();
  460. }
  461. if (XPending(xinfo.display) == 0)
  462. {
  463. usleep(1000000 / FPS - (end - lastRepaint));
  464. }
  465. }
  466. }
  467. /*
  468. * Start executing here.
  469. * First initialize window.
  470. * Next loop responding to events.
  471. * Exit forcing window manager to clean up - cheesy, but easy.
  472. */
  473. int main (int argc, char * argv[])
  474. {
  475. XInfo xInfo;
  476. initX(argc, argv, xInfo);
  477. eventLoop(xInfo);
  478. XCloseDisplay(xInfo.display);
  479. }