snake.cc 15 KB

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