Skip to content

Instantly share code, notes, and snippets.

@keul
Last active April 13, 2024 12:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save keul/383a50f64a163a9c2f06d63eb0f08640 to your computer and use it in GitHub Desktop.
Save keul/383a50f64a163a9c2f06d63eb0f08640 to your computer and use it in GitHub Desktop.
// Copy/paste this in the console while on your GitHub profile page
// See https://blog.keul.it/whats-stopping-you-from-coding-like-this/
(function () {
const RIGHT = 'right';
const LEFT = 'left';
const DOWN = 'down';
const UP = 'up';
let gameRunning = false;
class Nibble {
constructor() {
this.body = [
{
y: 3,
x: 1,
},
{
y: 3,
x: 2,
},
{
y: 3,
x: 3,
},
{
y: 3,
x: 4,
},
];
this.directionsStack = [RIGHT];
this.eatCounter = 0;
}
get direction() {
return this.directionsStack[0];
}
get lastDirection() {
return this.directionsStack[this.directionsStack.length - 1];
}
step() {
this.body.forEach((section, index) => {
if (index === this.body.length - 1) {
return;
}
section.y = this.body[index + 1].y;
section.x = this.body[index + 1].x;
});
const head = this.body[this.body.length - 1];
switch (this.direction) {
case UP:
head.y--;
break;
case RIGHT:
head.x++;
break;
case DOWN:
head.y++;
break;
case LEFT:
head.x--;
break;
}
if (this.directionsStack.length > 1) {
this.directionsStack.shift();
}
this.repaint();
}
grow() {
this.body = [{ ...this.body[0] }, ...this.body];
this.eatCounter++;
qs('h2').textContent = `${this.body.length} contributions in the last year`;
}
repaint() {
for (let index = 0; index < this.body.length; index++) {
const section = this.body[index];
const isHead = index === this.body.length - 1;
const { y, x } = section;
const cell = gameGrid[y] && gameGrid[y][x];
if (isHead) {
if (!cell || window.getComputedStyle(cell).fill === colors.snake) {
gameRunning = false;
qs('h2').textContent = 'Loser! You are not a 10x engineer!';
break;
}
if (window.getComputedStyle(cell).fill === colors.apple) {
this.grow();
apple.active = false;
}
}
if (cell) {
cell.style.fill = isHead ? colors.head : colors.snake;
}
}
}
}
let apple;
function placeApple() {
const x = Math.floor(Math.random() * 53);
const y = Math.floor(Math.random() * 7);
if (!gameGrid[y] || !gameGrid[y][x] || window.getComputedStyle(gameGrid[y][x]).fill !== colors.blank) {
return placeApple();
}
console.debug('apple at', y, x);
apple.x = x;
apple.y = y;
apple.active = true;
}
function clean() {
grid.forEach((cell) => {
cell.style.fill = colors.blank;
});
}
function tick() {
if (!gameRunning) {
return;
}
clean();
if (!apple.active) {
placeApple();
}
apple.paint();
nibble.step();
}
const container = document.querySelector('.js-yearly-contributions');
const qs = container.querySelector.bind(container);
const qsa = container.querySelectorAll.bind(container);
const colors = {
blank: window.getComputedStyle(qsa('.float-right .ContributionCalendar-day')[0]).fill,
head: window.getComputedStyle(qsa('.float-right .ContributionCalendar-day')[4]).fill,
snake: window.getComputedStyle(qsa('.float-right .ContributionCalendar-day')[3]).fill,
apple: window.getComputedStyle(qsa('.float-right .ContributionCalendar-day')[1]).fill,
};
const grid = qsa('.js-calendar-graph-svg > g > g > rect');
qs('.js-calendar-graph-svg').setAttribute('width', '100%');
const gameGrid = Array(7)
.fill()
.map(() => Array(53).fill());
for (let y = 0; y < gameGrid.length; y++) {
for (let x = 0; x < gameGrid[y].length; x++) {
const cell = grid[x * 7 + y];
if (cell) {
gameGrid[y][x] = cell;
}
}
}
document.addEventListener('keydown', (event) => {
if (event.code === 'ArrowRight' && nibble.lastDirection !== LEFT) {
nibble.directionsStack.push(RIGHT);
event.preventDefault();
} else if (event.code === 'ArrowDown' && nibble.lastDirection !== UP) {
nibble.directionsStack.push(DOWN);
event.preventDefault();
} else if (event.code === 'ArrowLeft' && nibble.lastDirection !== RIGHT) {
nibble.directionsStack.push(LEFT);
event.preventDefault();
} else if (event.code === 'ArrowUp' && nibble.lastDirection !== DOWN) {
nibble.directionsStack.push(UP);
event.preventDefault();
} else if (!gameRunning && event.code === 'Enter') {
event.preventDefault();
startAnimating();
}
});
let nibble = new Nibble();
function gameInit() {
nibble = new Nibble();
apple = {
active: false,
x: null,
y: null,
paint: function () {
gameGrid[this.y][this.x].style.fill = colors.apple;
},
};
}
function startAnimating() {
gameInit();
gameRunning = true;
qs('h2').textContent = `${nibble.body.length} contributions in the last year`;
let then = Date.now();
let fps;
function animate() {
if (nibble.eatCounter <= 5) {
fps = 4;
} else if (nibble.eatCounter <= 10) {
fps = 5;
} else if (nibble.eatCounter <= 20) {
fps = 7;
} else if (nibble.eatCounter <= 30) {
fps = 10;
} else if (nibble.eatCounter <= 50) {
fps = 14;
}
let fpsInterval = 1000 / fps;
if (!gameRunning) {
return;
}
requestAnimationFrame(animate);
const now = Date.now();
const elapsed = now - then;
if (elapsed > fpsInterval) {
then = now - (elapsed % fpsInterval);
tick();
}
}
animate();
}
qs('h2').textContent = "What's stopping you from coding like this?";
clean();
nibble.repaint();
console.log('Press enter on the page to start the game!');
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment