snake.cc 9.3 KB

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