snake.cc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572
  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. XSetForeground(xInfo.display, xInfo.gc[i], BlackPixel(xInfo.display, xInfo.screen));
  315. XSetBackground(xInfo.display, xInfo.gc[i], WhitePixel(xInfo.display, xInfo.screen));
  316. XSetFillStyle(xInfo.display, xInfo.gc[i], FillSolid);
  317. XSetLineAttributes(xInfo.display, xInfo.gc[i],
  318. 1, LineSolid, CapButt, JoinRound);
  319. XSelectInput(xInfo.display, xInfo.window,
  320. ButtonPressMask | KeyPressMask |
  321. PointerMotionMask |
  322. EnterWindowMask | LeaveWindowMask |
  323. StructureNotifyMask); // for resize events
  324. /*
  325. * Put the window on the screen.
  326. */
  327. XMapRaised( xInfo.display, xInfo.window );
  328. XFlush(xInfo.display);
  329. }
  330. /*
  331. * Function to repaint a display list
  332. */
  333. void repaint(XInfo & xinfo)
  334. {
  335. list<Displayable *>::const_iterator begin = dList.begin();
  336. list<Displayable *>::const_iterator end = dList.end();
  337. // get height and width of window (might have changed since last repaint)
  338. XWindowAttributes windowInfo;
  339. XGetWindowAttributes(xinfo.display, xinfo.window, &windowInfo);
  340. unsigned int height = windowInfo.height;
  341. unsigned int width = windowInfo.width;
  342. // big black rectangle to clear background
  343. // draw display list
  344. while (begin != end)
  345. {
  346. Displayable * d = *begin;
  347. d->paint(xinfo);
  348. begin++;
  349. }
  350. XFlush(xinfo.display);
  351. }
  352. void handleKeyPress(XInfo & xinfo, XEvent & event)
  353. {
  354. KeySym key;
  355. char text[BufferSize];
  356. int i = XLookupString
  357. (
  358. (XKeyEvent *)&event, // the keyboard event
  359. text, // buffer when text will be written
  360. BufferSize, // size of the text buffer
  361. &key, // workstation-independent key symbol
  362. NULL
  363. );
  364. if (i == 1)
  365. {
  366. printf("Got key press -- %c\n", text[0]);
  367. switch(text[0])
  368. {
  369. case 'q':
  370. error("Terminating normally.");
  371. break;
  372. }
  373. }
  374. if (key == XK_Up)
  375. {
  376. snake.moveup();
  377. }
  378. if (key == XK_Right)
  379. {
  380. snake.moveright();
  381. }
  382. if (key == XK_Down)
  383. {
  384. snake.movedown();
  385. }
  386. if (key == XK_Left)
  387. {
  388. snake.moveleft();
  389. }
  390. }
  391. void handleAnimation(XInfo & xinfo, int inside)
  392. {
  393. /*
  394. * ADD YOUR OWN LOGIC
  395. * This method handles animation for different objects on the screen and readies the next frame before the screen is re-painted.
  396. */
  397. snake.move(xinfo);
  398. }
  399. // get microseconds
  400. unsigned long now()
  401. {
  402. timeval tv;
  403. gettimeofday(&tv, NULL);
  404. return tv.tv_sec * 1000000 + tv.tv_usec;
  405. }
  406. void eventLoop(XInfo & xinfo)
  407. {
  408. // Add stuff to paint to the display list
  409. dList.push_front(&snake);
  410. dList.push_front(&fruity);
  411. XEvent event;
  412. unsigned long lastRepaint = 0;
  413. int inside = 0;
  414. XWindowAttributes w;
  415. XGetWindowAttributes(xinfo.display, xinfo.window, &w);
  416. int depth = DefaultDepth(xinfo.display, DefaultScreen(xinfo.display));
  417. // represents thing to draw
  418. Pixmap buffer = XCreatePixmap(xinfo.display, xinfo.window, w.width, w.height, depth);
  419. bool usebuffer = 1;
  420. while(1)
  421. {
  422. if (XPending(xinfo.display) > 0)
  423. {
  424. XNextEvent(xinfo.display, &event);
  425. switch(event.type)
  426. {
  427. case KeyPress:
  428. handleKeyPress(xinfo, event);
  429. break;
  430. case EnterNotify:
  431. inside = 1;
  432. break;
  433. case LeaveNotify:
  434. inside = 0;
  435. break;
  436. }
  437. }
  438. unsigned long end = now();
  439. if (end - lastRepaint > 1000000 / FPS)
  440. {
  441. Pixmap pixmap; // represents thing in buffer
  442. if (usebuffer)
  443. {
  444. pixmap = buffer;
  445. XSetForeground(xinfo.display, xinfo.gc[0], BlackPixel(xinfo.display, xinfo.screen));
  446. XSetBackground(xinfo.display, xinfo.gc[0], WhitePixel(xinfo.display, xinfo.screen));
  447. XFillRectangle(xinfo.display, pixmap, xinfo.gc[0], 0, 0, w.width, w.height);
  448. }
  449. else
  450. {
  451. pixmap = xinfo.window; // set this to the windowInfo
  452. XClearWindow(xinfo.display, pixmap);
  453. }
  454. //XSetForeground(xinfo.display, xinfo.gc[0], BlackPixel(xinfo.display, xinfo.screen));
  455. //XSetBackground(xinfo.display, xinfo.gc[0], WhitePixel(xinfo.display, xinfo.screen));
  456. handleAnimation(xinfo, inside);
  457. if (usebuffer)
  458. {
  459. XCopyArea(xinfo.display, pixmap, xinfo.window, xinfo.gc[0], 0, 0 , w.width, w.height, 0, 0);
  460. }
  461. XFlush(xinfo.display);
  462. lastRepaint = now();
  463. }
  464. if (XPending(xinfo.display) == 0)
  465. {
  466. usleep(1000000 / FPS - (end - lastRepaint));
  467. }
  468. }
  469. }
  470. /*
  471. * Start executing here.
  472. * First initialize window.
  473. * Next loop responding to events.
  474. * Exit forcing window manager to clean up - cheesy, but easy.
  475. */
  476. int main (int argc, char * argv[])
  477. {
  478. XInfo xInfo;
  479. initX(argc, argv, xInfo);
  480. eventLoop(xInfo);
  481. XCloseDisplay(xInfo.display);
  482. }