snake.cc 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707
  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. // Globals for xlib
  24. Colormap colours;
  25. XColor xcolour;
  26. char black[] = "rgb:00/00/00";
  27. char white[] = "rgb:ff/ff/ff";
  28. char lblue[] = "rgb:00/ff/ff";
  29. char orange[] = "rgb:FF/45/00";
  30. /*
  31. * Global game state variables
  32. */
  33. const int Border = 1;
  34. const int BufferSize = 10;
  35. int FPS = 30;
  36. const int width = 800;
  37. const int height = 600;
  38. const int blockSize = 25;
  39. int velocity = 5;
  40. bool paused = true;
  41. bool starting = true;
  42. bool dead = false;
  43. /*
  44. * Information to draw on the window.
  45. */
  46. typedef struct XInfo
  47. {
  48. Display * display;
  49. int screen;
  50. Window window;
  51. GC gc[3];
  52. int width; // size of window
  53. int height;
  54. Pixmap buffer;
  55. } Xinfo;
  56. // get microseconds
  57. unsigned long now()
  58. {
  59. timeval tv;
  60. gettimeofday(&tv, NULL);
  61. return tv.tv_sec * 1000000 + tv.tv_usec;
  62. }
  63. /*
  64. * Function to put out a message on error exits.
  65. */
  66. void error(string str)
  67. {
  68. cerr << str << endl;
  69. exit(0);
  70. }
  71. // change the drawing coloursvoid changecolour(char colour[])
  72. void changecolour(XInfo & xinfo, char colour[])
  73. {
  74. XParseColor(xinfo.display, colours, colour, &xcolour);
  75. XAllocColor(xinfo.display, colours, &xcolour);
  76. XSetForeground(xinfo.display, xinfo.gc[0], xcolour.pixel);
  77. XFreeColormap(xinfo.display, colours);
  78. }
  79. /*
  80. * An abstract class representing displayable things.
  81. */
  82. class Displayable
  83. {
  84. public:
  85. virtual void paint(XInfo & xinfo) = 0;
  86. };
  87. class Fruit : public Displayable
  88. {
  89. public:
  90. virtual void paint(XInfo & xinfo)
  91. {
  92. changecolour(xinfo, orange);
  93. XFillArc(xinfo.display, xinfo.buffer, xinfo.gc[0], x, y, 25, 25, 0, 360 * 64);
  94. }
  95. Fruit()
  96. {
  97. newspot();
  98. }
  99. int getx()
  100. {
  101. return x;
  102. }
  103. int gety()
  104. {
  105. return y;
  106. }
  107. void newspot()
  108. {
  109. x = (rand() % 32) * 25;
  110. y = (rand() % 24) * 25;
  111. }
  112. // ** ADD YOUR LOGIC **
  113. /*
  114. * The fruit needs to be re-generated at new location every time a snake eats it. See the assignment webpage for more details.
  115. */
  116. private:
  117. int x;
  118. int y;
  119. };
  120. class Section: public Displayable
  121. {
  122. public:
  123. virtual void paint(XInfo & xinfo)
  124. {
  125. changecolour(xinfo, black);
  126. switch(direction)
  127. {
  128. case 1:
  129. XFillRectangle(xinfo.display, xinfo.buffer, xinfo.gc[0], x, y, blockSize, (len + 1) * blockSize);
  130. break;
  131. case 2:
  132. XFillRectangle(xinfo.display, xinfo.buffer, xinfo.gc[0], (x - len * blockSize), y, (1 + len) * blockSize, blockSize);
  133. break;
  134. case 3:
  135. XFillRectangle(xinfo.display, xinfo.buffer, xinfo.gc[0], x, (y - len * blockSize), blockSize, ((1 + len) * blockSize));
  136. break;
  137. case 4:
  138. XFillRectangle(xinfo.display, xinfo.buffer, xinfo.gc[0], x, y, (len + 1) * blockSize, blockSize);
  139. break;
  140. }
  141. }
  142. void increment(int x)
  143. {
  144. len += x;
  145. }
  146. void decrement()
  147. {
  148. --len;
  149. }
  150. bool empty()
  151. {
  152. return (!len);
  153. }
  154. void grow()
  155. {
  156. switch(direction)
  157. {
  158. case 1:
  159. y -= blockSize;
  160. break;
  161. case 2:
  162. x += blockSize;
  163. break;
  164. case 3:
  165. y += blockSize;
  166. break;
  167. case 4:
  168. x -= blockSize;
  169. break;
  170. }
  171. ++len;
  172. }
  173. int getx()
  174. {
  175. return x;
  176. }
  177. int gety()
  178. {
  179. return y;
  180. }
  181. int getdir()
  182. {
  183. return direction;
  184. }
  185. int getlen()
  186. {
  187. return len;
  188. }
  189. Section(int x, int y, int dir, int len = 1): x(x), y(y), direction(dir), len(len)
  190. {
  191. blockSize = 25;
  192. }
  193. private:
  194. int x;
  195. int y;
  196. int len;
  197. int blockSize;
  198. int direction;
  199. };
  200. class Snake : public Displayable
  201. {
  202. public:
  203. virtual void paint(XInfo & xinfo)
  204. {
  205. for (Section * each: sections)
  206. {
  207. each->paint(xinfo);
  208. }
  209. }
  210. void move(XInfo & xinfo)
  211. {
  212. if ((now() - lastmove < speed) || paused)
  213. {
  214. return;
  215. }
  216. // turn if a turn happens
  217. if (direction)
  218. {
  219. Section * newsec = new Section(sections.front()->getx(), sections.front()->gety(), direction, 0);
  220. direction = 0;
  221. sections.insert(sections.begin(), newsec);
  222. }
  223. // move 1 block forwards
  224. sections.front()->grow();
  225. // see if fruit was eaten
  226. if (sections.front()->getx() == fruity->getx() && sections.front()->gety() == fruity->gety())
  227. {
  228. fruit = 1;
  229. }
  230. // add fruit, remove 1 from end, delete if empty
  231. sections.back()->increment(fruit);
  232. if (fruit) fruity->newspot();
  233. fruit = 0;
  234. sections.back()->decrement();
  235. if (sections.back()->empty())
  236. {
  237. delete sections.back();
  238. sections.pop_back();
  239. }
  240. if (sections.front()->getx() > width || sections.front()->gety() < 0)
  241. {
  242. dead = true;
  243. paused = true;
  244. }
  245. if (sections.front()->getx() < 0 || sections.front()->gety() > height)
  246. {
  247. dead = true;
  248. paused = true;
  249. }
  250. if (sections.size() <= 3) goto SKIP;
  251. for (auto each = sections.begin() + 1; each != sections.end(); ++each)
  252. {
  253. int tempdir = (*each)->getdir();
  254. int frontx = sections.front()->getx();
  255. int fronty = sections.front()->gety();
  256. int eachx = (*each)->getx();
  257. int eachy = (*each)->gety();
  258. int eachlen = (*each)->getlen();
  259. switch(tempdir)
  260. {
  261. case 1:
  262. if (frontx == eachx && (fronty >= eachy && fronty <= (eachy + blockSize * eachlen)))
  263. {
  264. dead = true;
  265. paused = true;
  266. }
  267. break;
  268. case 2:
  269. if ((frontx >= (eachx - eachlen * blockSize) && frontx <= eachx) && fronty == eachy)
  270. {
  271. dead = true;
  272. paused = true;
  273. }
  274. break;
  275. case 3:
  276. if (frontx == eachx && (fronty <= eachy && fronty >= (eachy - blockSize * eachlen)))
  277. {
  278. dead = true;
  279. paused = true;
  280. }
  281. break;
  282. case 4:
  283. if ((frontx <= (eachx - eachlen * blockSize) && frontx >= eachx) && fronty == eachy)
  284. {
  285. dead = true;
  286. paused = true;
  287. }
  288. break;
  289. }
  290. }
  291. SKIP:
  292. lastmove = now();
  293. }
  294. void moveup()
  295. {
  296. if (direction == 4 || direction == 2 || direction == 0) direction = 1;
  297. }
  298. void moveright()
  299. {
  300. if (direction == 1 || direction == 3 || direction == 0) direction = 2;
  301. }
  302. void movedown()
  303. {
  304. if (direction == 4 || direction == 2 || direction == 0) direction = 3;
  305. }
  306. void moveleft()
  307. {
  308. if (direction == 1 || direction == 3 || direction == 0) direction = 4;
  309. }
  310. void changespeed(unsigned long x)
  311. {
  312. speed = 250000 / x;
  313. }
  314. void reset(int fast)
  315. {
  316. int size = sections.size();
  317. for (int i = 0; i < size; i++)
  318. {
  319. delete sections.at(i);
  320. sections.erase(sections.begin() + i);
  321. }
  322. Section * newsec = new Section(x, y, 2, 2);
  323. sections.insert(sections.begin(), newsec);
  324. direction = 0;
  325. blockSize = 25;
  326. speed = 250000 / fast;
  327. lastmove = 0;
  328. }
  329. Snake(int x, int y, Fruit * fruity, unsigned long fast): x(x), y(y), fruity(fruity)
  330. {
  331. Section * newsec = new Section(x, y, 2, 2);
  332. sections.insert(sections.begin(), newsec);
  333. direction = 0;
  334. blockSize = 25;
  335. speed = 250000 / fast;
  336. lastmove = 0;
  337. }
  338. ~Snake()
  339. {
  340. int size = sections.size();
  341. for (int i = 0; i < size; i++)
  342. {
  343. delete sections.at(i);
  344. sections.erase(sections.begin() + i);
  345. }
  346. }
  347. private:
  348. int x;
  349. int y;
  350. vector<Section *> sections;
  351. int blockSize;
  352. int direction;
  353. unsigned long speed;
  354. int fruit;
  355. Fruit * fruity;
  356. unsigned long lastmove;
  357. };
  358. list<Displayable *> dList; // list of Displayables
  359. Fruit fruity;
  360. Snake snake(100, 450, &fruity, velocity);
  361. /*
  362. * Initialize X and create a window
  363. */
  364. void initX(int argc, char * argv[], XInfo & xInfo)
  365. {
  366. XSizeHints hints;
  367. unsigned long white, black;
  368. /*
  369. * Display opening uses the DISPLAY environment variable.
  370. * It can go wrong if DISPLAY isn't set, or you don't have permission.
  371. */
  372. xInfo.display = XOpenDisplay("");
  373. if (!xInfo.display)
  374. {
  375. error("Can't open display.");
  376. }
  377. /*
  378. * Find out some things about the display you're using.
  379. */
  380. xInfo.screen = DefaultScreen(xInfo.display);
  381. colours = DefaultColormap(xInfo.display, xInfo.screen);
  382. white = XWhitePixel(xInfo.display, xInfo.screen);
  383. black = XBlackPixel(xInfo.display, xInfo.screen);
  384. hints.x = 100;
  385. hints.y = 100;
  386. hints.width = 800;
  387. hints.height = 600;
  388. hints.flags = PPosition | PSize;
  389. xInfo.window = XCreateSimpleWindow
  390. (
  391. xInfo.display, // display where window appears
  392. DefaultRootWindow( xInfo.display ), // window's parent in window tree
  393. hints.x, hints.y, // upper left corner location
  394. hints.width, hints.height, // size of the window
  395. Border, // width of window's border
  396. black, // window border colour
  397. white
  398. ); // window background colour
  399. XSetStandardProperties
  400. (
  401. xInfo.display, // display containing the window
  402. xInfo.window, // window whose properties are set
  403. "animation", // window's title
  404. "Animate", // icon's title
  405. None, // pixmap for the icon
  406. argv, argc, // applications command line args
  407. &hints
  408. ); // size hints for the window
  409. int depth = DefaultDepth(xInfo.display, DefaultScreen(xInfo.display));
  410. xInfo.buffer = XCreatePixmap(xInfo.display, xInfo.window, hints.width, hints.height, depth);
  411. /*
  412. * Create Graphics Contexts
  413. */
  414. int i = 0;
  415. xInfo.gc[i] = XCreateGC(xInfo.display, xInfo.window, 0, 0);
  416. XSetBackground(xInfo.display, xInfo.gc[0], WhitePixel(xInfo.display, xInfo.screen));
  417. XSetFillStyle(xInfo.display, xInfo.gc[i], FillSolid);
  418. XSetLineAttributes(xInfo.display, xInfo.gc[i],
  419. 1, LineSolid, CapButt, JoinRound);
  420. XSelectInput(xInfo.display, xInfo.window,
  421. ButtonPressMask | KeyPressMask |
  422. PointerMotionMask |
  423. EnterWindowMask | LeaveWindowMask); // for resize events
  424. /*
  425. * Put the window on the screen.
  426. */
  427. XMapRaised( xInfo.display, xInfo.window );
  428. XFlush(xInfo.display);
  429. }
  430. void splash(XInfo & xinfo)
  431. {
  432. changecolour(xinfo, white);
  433. XFillRectangle(xinfo.display, xinfo.buffer, xinfo.gc[0], 100, 100, 600, 400);
  434. changecolour(xinfo, black);
  435. XFontStruct * font = XLoadQueryFont(xinfo.display, "12x24");
  436. XSetFont(xinfo.display, xinfo.gc[0], font->fid);
  437. XDrawString(xinfo.display, xinfo.buffer, xinfo.gc[0], 350, 200, "Snake", 5);
  438. XDrawString(xinfo.display, xinfo.buffer, xinfo.gc[0], 340, 240, "tsdedhar", 8);
  439. XDrawString(xinfo.display, xinfo.buffer, xinfo.gc[0], 340, 280, "20621325", 8);
  440. XDrawString(xinfo.display, xinfo.buffer, xinfo.gc[0], 265, 320, "Use arrow keys to turn", 22);
  441. XDrawString(xinfo.display, xinfo.buffer, xinfo.gc[0], 200, 360, "Collect fruit to grow your snake", 32);
  442. }
  443. void endgame(XInfo & xinfo)
  444. {
  445. changecolour(xinfo, white);
  446. XFillRectangle(xinfo.display, xinfo.buffer, xinfo.gc[0], 100, 100, 600, 400);
  447. changecolour(xinfo, black);
  448. XFontStruct * font = XLoadQueryFont(xinfo.display, "12x24");
  449. XSetFont(xinfo.display, xinfo.gc[0], font->fid);
  450. XDrawString(xinfo.display, xinfo.buffer, xinfo.gc[0], 347, 200, "Game Over!", 10);
  451. XDrawString(xinfo.display, xinfo.buffer, xinfo.gc[0], 340, 240, "Press r to restart", 18);
  452. XDrawString(xinfo.display, xinfo.buffer, xinfo.gc[0], 342, 280, "Press q to quit", 15);
  453. }
  454. /*
  455. * Function to repaint a display list
  456. */
  457. void repaint(XInfo & xinfo)
  458. {
  459. list<Displayable *>::const_iterator begin = dList.begin();
  460. list<Displayable *>::const_iterator end = dList.end();
  461. // get height and width of window (might have changed since last repaint)
  462. XWindowAttributes windowInfo;
  463. XGetWindowAttributes(xinfo.display, xinfo.window, &windowInfo);
  464. unsigned int height = windowInfo.height;
  465. unsigned int width = windowInfo.width;
  466. changecolour(xinfo, lblue);
  467. XFillRectangle(xinfo.display, xinfo.buffer, xinfo.gc[0], 0, 0, width, height);
  468. changecolour(xinfo, black);
  469. // draw display list
  470. while (begin != end)
  471. {
  472. Displayable * d = *begin;
  473. d->paint(xinfo);
  474. begin++;
  475. }
  476. if (starting) splash(xinfo);
  477. if (dead)
  478. {
  479. endgame(xinfo);
  480. }
  481. }
  482. void handleKeyPress(XInfo & xinfo, XEvent & event)
  483. {
  484. KeySym key;
  485. char text[BufferSize];
  486. int i = XLookupString
  487. (
  488. (XKeyEvent *)&event, // the keyboard event
  489. text, // buffer when text will be written
  490. BufferSize, // size of the text buffer
  491. &key, // workstation-independent key symbol
  492. NULL
  493. );
  494. if (i == 1)
  495. {
  496. printf("Got key press -- %c\n", text[0]);
  497. switch(text[0])
  498. {
  499. case 'q':
  500. error("Terminating normally.");
  501. break;
  502. case ' ':
  503. paused = !paused;
  504. starting = false;
  505. break;
  506. case 'r':
  507. snake.reset(velocity);
  508. paused = true;
  509. starting = true;
  510. dead = false;
  511. break;
  512. }
  513. }
  514. if (key == XK_Up)
  515. {
  516. snake.moveup();
  517. }
  518. if (key == XK_Right)
  519. {
  520. snake.moveright();
  521. }
  522. if (key == XK_Down)
  523. {
  524. snake.movedown();
  525. }
  526. if (key == XK_Left)
  527. {
  528. snake.moveleft();
  529. }
  530. }
  531. void handleAnimation(XInfo & xinfo, int inside)
  532. {
  533. snake.move(xinfo);
  534. }
  535. void eventLoop(XInfo & xinfo)
  536. {
  537. // Add stuff to paint to the display list
  538. dList.push_front(&snake);
  539. dList.push_front(&fruity);
  540. XEvent event;
  541. unsigned long lastRepaint = 0;
  542. int inside = 0;
  543. XWindowAttributes w;
  544. XGetWindowAttributes(xinfo.display, xinfo.window, &w);
  545. while(1)
  546. {
  547. if (XPending(xinfo.display) > 0)
  548. {
  549. XNextEvent(xinfo.display, &event);
  550. switch(event.type)
  551. {
  552. case KeyPress:
  553. handleKeyPress(xinfo, event);
  554. break;
  555. case EnterNotify:
  556. inside = 1;
  557. break;
  558. case LeaveNotify:
  559. inside = 0;
  560. break;
  561. }
  562. }
  563. unsigned long end = now();
  564. if (end - lastRepaint > 1000000 / FPS)
  565. {
  566. handleAnimation(xinfo, inside);
  567. repaint(xinfo);
  568. XCopyArea(xinfo.display, xinfo.buffer, xinfo.window, xinfo.gc[0], 0, 0 , w.width, w.height, 0, 0);
  569. XFlush(xinfo.display);
  570. lastRepaint = now();
  571. }
  572. if (XPending(xinfo.display) == 0)
  573. {
  574. usleep(1000000 / FPS - (end - lastRepaint));
  575. }
  576. }
  577. }
  578. /*
  579. * Start executing here.
  580. * First initialize window.
  581. * Next loop responding to events.
  582. * Exit forcing window manager to clean up - cheesy, but easy.
  583. */
  584. int main (int argc, char * argv[])
  585. {
  586. if (argc == 1)
  587. {
  588. }
  589. else if (argc == 3)
  590. {
  591. FPS = atoi(argv[1]);
  592. velocity = atoi(argv[2]);
  593. if (atoi(argv[2]) < 1 || atoi(argv[2]) > 10) goto BADARGS;
  594. }
  595. else
  596. {
  597. BADARGS:
  598. printf("Incorrect arguments supplied\n");
  599. exit(0);
  600. }
  601. snake.changespeed(velocity);
  602. XInfo xInfo;
  603. initX(argc, argv, xInfo);
  604. eventLoop(xInfo);
  605. XCloseDisplay(xInfo.display);
  606. }