Skip to content
Snippets Groups Projects

A kilobyte of fun: Contributions Snake

  • Clone with SSH
  • Clone with HTTPS
  • Embed
  • Share
    The snippet can be accessed without any authentication.
    Authored by Lorenz van Herwaarden

    contributions-snake

    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!

    :raised_hands: Thanks to @leipert's minimization efforts, the terser minified version is 1019 bytes < 1024 bytes < 1 KB

    contributions-snake

    Edited
    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 always 0. Some more bytes gone!
      • Edit2: Without the trim() the map is smaller!
      Edited by Lukas Eipert
    • 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.

    • Lorenz van Herwaarden @lorenzvanherwaarden ·

      Thanks @leipert for turning this into a valid submission! :pray: I'll update the description to reflect this one is below 1024 Bytes!

    0% Loading or .
    You are about to add 0 people to the discussion. Proceed with caution.
    Please register or to comment