A kilobyte of fun: Contributions Snake
The snippet can be accessed without any authentication.
Authored by
Lorenz van Herwaarden
Paste the snippet in your console on a GitLab profile page.
Contributions Snake let's you play snake with the sum of your contributions of the last year! The goal is to get food which is worth 100 contributions. If you get all your contributions, you win!
0-minified.js 1019 B
let e,t,l,n,r,a=document,i=e=>a.createElement(e),c=e=>a.querySelectorAll(e),d=c(".user-calendar")[0],o=e=>d.append(e),s=i("div"),y=(e,l)=>{t.fillStyle=e,t.fillRect(l.x,l.y,13,13)},x=e=>~~(Math.random()*e),f=(e,t=1)=>{s.innerText=e,t&&(clearInterval(n),setTimeout(v,5e3))},u=()=>{l>0?f(l+" contributions left",0):f("Win!")},m=(e,t)=>e&&e.x==t.x&&e.y==t.y,v=()=>{let a,d=4,s=[],v=140,h=70,p=()=>{a={x:14*x(56),y:14*x(15)}};p(),r={x:14,y:0};l=[...c("rect")].map(e=>+e.getAttribute("title").slice(0,2)).reduce((e,t)=>t?e+t:e,0),u(),e&&e.remove(),e=i("canvas"),e.width=784,e.height=210,t=e.getContext("2d"),o(e),n=setInterval(()=>{t.reset(),v+=r.x,h+=r.y,v=v<0?770:v<784?v:0,h=h<0?196:h<210?h:0,s=[{x:v,y:h},...s].slice(0,d),y("#fc6d26",a),s.some((e,t)=>{y(["#d2dcff","#7992f5","#3f51ae","#2a2b59"][x(3)],e),m(e,a)&&(d++,p(),l-=100,u()),t>0&&m(s[0],e)&&f("Loss!")})},50)};a.addEventListener("keydown",e=>{e.preventDefault();let t=(r.x?r.y?r:{38:[-14,0],40:[14,0]}:{37:[0,-14],39:[0,14]})[e.which];t&&([r.y,r.x]=t)}),o(s),v()
1-contributions-snake.js 2.04 KiB
2-contributions-snake-eipi-minification-mod.js 3.26 KiB
-
The version below minifes to 1086 bytes.
let g = 14; let width = 784; let height = 210; let d = document; let cE = (tag) => d.createElement(tag) let qA = (tag) => d.querySelectorAll(tag) let setText = (text) => l.innerText = text; let c = qA(".user-calendar")[0]; let append = (element) => c.append(element) let l = cE("div"); let left; let can; let ctx; let ival; let s; let a; let fillSquare = (col,coordinates) => { ctx.fillStyle = col; ctx.fillRect(coordinates.x,coordinates.y,g-1,g-1) }; let r = (max) => ~~(Math.random() * (max)); let placeFood = () => { a = { x: r(56) * g,y: r(15) * g} }; let reset = text => { setText(`You ${text}!`); clearInterval(ival); setTimeout(init, 5000); }; let check = () => { if (left > 0) { setText(`${left} contributions left`); } else { reset("win"); } }; let ranCol = () => { let colors = ["#d2dcff", "#7992f5", "#3f51ae", "#2a2b59"]; return colors[r(3)]; }; let sameCell = (a,b) => a && a.x == b.x && a.y == b.y let loop = () => { // Reset the canvas ctx.reset(); // Advance the snake's head s.x += s.dx; s.y += s.dy; // Wrap the snake's head when out of bounds s.x = s.x < 0 ? width - g : s.x < width ? s.x : 0; s.y = s.y < 0 ? height - g : s.y < height ? s.y : 0; // prepend the snake's head and truncate to snake's length s.c = [{ x: s.x, y: s.y },...s.c].slice(0,s.m); // paint food fillSquare("#fc6d26", a); // `some` is shorter than `forEach` // Paint the snake s.c.some((cell, idx) => { fillSquare(ranCol(), cell); // Nom-nom-nom if (sameCell(cell,a)) { s.m++; placeFood() left -=100; check(); } // Ouch // We do not return because we want to paint the whole Snake! if (idx > 0 && sameCell(s.c[0], cell)) { reset("lose"); } }); }; let init = () => { // reset state placeFood() s = {x: 140,y : 70,c: [],m: 30,dx:g,dy:0} // count contributions left = [...qA("rect")] .map(rect => +rect.getAttribute("title").slice(0,2)) .reduce((acc, t) => t ? acc + t: acc, 0); // have we won yet? check(); if (can) can.remove(); can = cE("canvas"); can.width = width; can.height = height; ctx = can.getContext("2d"); append(can); ival = setInterval(loop, 50); } d.addEventListener("keydown", (e) => { e.preventDefault(); let r; if (s.dx == 0) { r = { 37: [0, -g], 39: [0, g] }[e.which]; } else if (s.dy == 0) { r = { 38: [-g, 0], 40: [g, 0] }[e.which]; } if (r) { [s.dy, s.dx] = r; } }); append(l); init();
- Edit: Realized that
min
in our random function was always0
. Some more bytes gone! - Edit2: Without the
trim()
the map is smaller!
Edited by Lukas Eipert - Edit: Realized that
-
Here is the compress to less than 1024B version (1019 to be exact)
let blockSize = 14; let canvasWidth = 784; let canvasHeight = 210; /** * Elements and stuff */ let doc = document; let createElement = (tag) => doc.createElement(tag); let querySelectorAll = (tag) => doc.querySelectorAll(tag); let calendarContainer = querySelectorAll(".user-calendar")[0]; let append = (element) => calendarContainer.append(element); let textContainer = createElement("div"); let canvas; let context; /** * Global state */ // How many contributions to play down let contributions; // Current loop interval let interval; // Direction the snake head is gonna go let direction; let fillSquare = (col, coordinates) => { context.fillStyle = col; context.fillRect(coordinates.x, coordinates.y, blockSize - 1, blockSize - 1); }; let r = (max) => ~~(Math.random() * max); let setText = (text, done = 1) => { textContainer.innerText = text; if (done) { clearInterval(interval); setTimeout(init, 5000); } }; let check = () => { if (contributions > 0) { setText(`${contributions} contributions left`, 0); } else { setText("Win!"); } }; let randomColor = () => { let colors = ["#d2dcff", "#7992f5", "#3f51ae", "#2a2b59"]; return colors[r(3)]; }; let sameCell = (a, b) => a && a.x == b.x && a.y == b.y; let init = () => { // reset state let snakeLength = 4; let coordinates = []; let x = 140; let y = 70; let food; let placeFood = () => { food = { x: r(56) * blockSize, y: r(15) * blockSize }; }; placeFood(); direction = { x: blockSize, y: 0 }; let loop = () => { // Reset the canvas context.reset(); // Advance the snake's head x += direction.x; y += direction.y; // Wrap the snake's head when out of bounds x = x < 0 ? canvasWidth - blockSize : x < canvasWidth ? x : 0; y = y < 0 ? canvasHeight - blockSize : y < canvasHeight ? y : 0; // prepend the snake's head and truncate to snake's length coordinates = [{ x, y }, ...coordinates].slice(0, snakeLength); // paint food fillSquare("#fc6d26", food); // `some` is shorter than `forEach` // Paint the snake coordinates.some((cell, index) => { fillSquare(randomColor(), cell); // Nom-nom-nom if (sameCell(cell, food)) { snakeLength++; placeFood(); contributions -= 100; check(); } // Ouch // We do not return because we want to paint the whole Snake! if (index > 0 && sameCell(coordinates[0], cell)) { setText("Loss!"); } }); }; // count contributions contributions = [...querySelectorAll("rect")] .map((rect) => +rect.getAttribute("title").slice(0, 2)) .reduce((acc, t) => (t ? acc + t : acc), 0); // have we won yet? check(); if (canvas) canvas.remove(); canvas = createElement("canvas"); canvas.width = canvasWidth; canvas.height = canvasHeight; context = canvas.getContext("2d"); append(canvas); interval = setInterval(loop, 50); }; doc.addEventListener("keydown", (e) => { e.preventDefault(); let r = ( !direction.x ? { 37: [0, -blockSize], 39: [0, blockSize] } : !direction.y ? { 38: [-blockSize, 0], 40: [blockSize, 0], } : direction )[e.which]; if (r) { [direction.y, direction.x] = r; } }); append(textContainer); init();
Biggest jump was the move of some state to the
init
function rather than having it in a global state object. -
Thanks @leipert for turning this into a valid submission!
I'll update the description to reflect this one is below 1024 Bytes!
Please register or sign in to comment