Last active
April 13, 2024 12:59
-
-
Save keul/383a50f64a163a9c2f06d63eb0f08640 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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