GitHub Contributions Snake

turn github contribs to snake game

目前为 2024-08-19 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name GitHub Contributions Snake
  3. // @namespace http://tampermonkey.net/
  4. // @version 2024-07-19
  5. // @description turn github contribs to snake game
  6. // @author GV3Dev
  7. // @match https://github.com/*
  8. // @icon https://www.google.com/s2/favicons?sz=64&domain=github.com
  9. // @grant none
  10. // @license MIT
  11. // ==/UserScript==
  12.  
  13. class SnakeElem {
  14. constructor() {
  15. this.body = [{x: 0, y: 0}];
  16. this.length = 1;
  17. this.foodEaten = 0;
  18. this.direction = "right";
  19. }
  20.  
  21. createSnake(cells, columns) {
  22. for (let i = 0; i < this.length; i++) {
  23. const pos = this.body[i];
  24. makeGreen(cells[pos.y * columns + pos.x], 4);
  25. }
  26. }
  27.  
  28. move(direction, cells, columns, rows, food) {
  29. const head = { ...this.body[0] };
  30.  
  31. switch (direction) {
  32. case "left":
  33. if (this.direction !== "right") this.direction = "left";
  34. break;
  35. case "right":
  36. if (this.direction !== "left") this.direction = "right";
  37. break;
  38. case "down":
  39. if (this.direction !== "up") this.direction = "down";
  40. break;
  41. case "up":
  42. if (this.direction !== "down") this.direction = "up";
  43. break;
  44. }
  45.  
  46. switch (this.direction) {
  47. case "left":
  48. head.x = (head.x - 1 + columns) % columns;
  49. break;
  50. case "right":
  51. head.x = (head.x + 1) % columns;
  52. break;
  53. case "down":
  54. head.y = (head.y + 1) % rows;
  55. break;
  56. case "up":
  57. head.y = (head.y - 1 + rows) % rows;
  58. break;
  59. }
  60.  
  61. this.body.unshift(head);
  62.  
  63. if (head.x === food.position.x && head.y === food.position.y) {
  64. this.foodEaten++;
  65. this.length += 2;
  66. food.getEaten(cells, this, columns, rows);
  67. } else {
  68. const tail = this.body.pop();
  69. cells[tail.y * columns + tail.x].setAttribute("data-level", "0");
  70. }
  71.  
  72. this.createSnake(cells, columns);
  73. }
  74. }
  75.  
  76. class FoodElem {
  77. constructor() {
  78. this.position = null;
  79. }
  80.  
  81. getEaten(cells, snake, columns, rows) {
  82. cells[this.position.y * columns + this.position.x].style.backgroundColor = "#161b22";
  83. this.createFood(cells, columns, rows, snake);
  84. }
  85.  
  86. createFood(cells, columns, rows, snake) {
  87. let randCell;
  88. let x, y;
  89. do {
  90. randCell = Math.floor(Math.random() * cells.length);
  91. y = Math.floor(randCell / columns);
  92. x = randCell % columns;
  93. } while (cells[randCell].getAttribute("data-level") !== "0" || snake.body.some(part => part.x === x && part.y === y));
  94.  
  95. cells[y * columns + x].style.backgroundColor = "red";
  96. this.position = {x, y};
  97. }
  98. }
  99.  
  100. const main = () => {
  101. const keepCheck = setInterval(() => {
  102. const contribBoard = document.querySelector(".js-yearly-contributions");
  103. if (contribBoard !== null) {
  104. clearInterval(keepCheck);
  105. let cells = contribBoard.querySelectorAll(`.ContributionCalendar-day`);
  106. let columns = contribBoard.querySelector(`.js-calendar-graph-table tbody`).firstElementChild.children.length;
  107. let rows = Math.floor(cells.length / columns+1);
  108. cleanGrid();
  109. const snake = new SnakeElem();
  110. const food = new FoodElem();
  111. food.createFood(cells, columns, rows, snake);
  112. snake.createSnake(cells, columns);
  113. appendControls(snake, cells, columns, rows, food);
  114. }
  115. }, 100);
  116. }
  117.  
  118. const appendControls = (snake, cells, columns, rows, food) => {
  119. window.addEventListener("keydown", (evt) => {
  120. if (["ArrowUp", "ArrowRight", "ArrowLeft", "ArrowDown"].includes(evt.code)) {
  121. evt.preventDefault();
  122. }
  123. if (evt.code === "ArrowUp") {
  124. snake.move("up", cells, columns, rows, food);
  125. } else if (evt.code === "ArrowRight") {
  126. snake.move("right", cells, columns, rows, food);
  127. } else if (evt.code === "ArrowLeft") {
  128. snake.move("left", cells, columns, rows, food);
  129. } else if (evt.code === "ArrowDown") {
  130. snake.move("down", cells, columns, rows, food);
  131. }
  132. });
  133. }
  134.  
  135. function makeGreen(elem, level) {
  136. elem.setAttribute("data-level", `${level}`);
  137. }
  138.  
  139. function cleanGrid() {
  140. const contribBoard = document.querySelector(".js-yearly-contributions");
  141. let cells = contribBoard.querySelectorAll(`.ContributionCalendar-day`);
  142. cells.forEach((cell) => { cell.setAttribute("data-level", "0"); });
  143. }
  144.  
  145. main();