<table border id=t>
<script>

// Initialize 4 arrays of data and one game state:
// b: bombs
// v: viewed cells
// f: flags
// n: neighbours
// g: gameover (1 win, -1 lost)
// l: field is locked (boolean)
b = [v = []];
f = [l = g = 0];
n = [];

// Rendering function
// @param d: grid dimension (d * d-1)
function r(d){

  // While there are more cells to show and the field is not locked
  // Set lock flag to be equal to gameover. It should be made anytime after M assignment
  for(M = !l; M; l = g){
    
    // Loop on lines, reset HTML, assume there are no more cells to show
    for(a = M = H = ""; s > ++a;){
    
      // Loop on columns, generate HTML
      for
      (
        j = s,
        H += "<tr>";
        ~--j;
        H += "<th onclick=b[i=" + I + "]?g--:v[i]=1;r() oncontextmenu=for(f[" + I + "]^=g=1,a=0;a<s*s;)g&=b[a]^!f[a++];return!!r() " + (v[I] | g || "bgcolor=tan") + ">" + (f[I] ? "\u2691" : b[I] & g ? "\uD83D\uDCA3" : v[I] | g && n[I] || "\u2001\u2002")
      )
      {
        
        // Loop on neighbours
        for
        (
          
          // Compute cell index
          I = a * s + j,
          
          // At start, set bomb randomly in the cell
          T = d ? b[I] = .1 > Math.random() : 0,
          x = 2;
          ~x--;
        )
        {
          for
          (
            y = 2;
            ~y--;
             
            // If the neighbour is in bounds
            ~B && B < s ?
            
            // If there is a bomb in the cell, inrement neighbours' counters
            (n[C] = ~~n[C] + T) || v[I] | b[I] | !v[C] ? 0 : v[I] = M = 1 :
            0;
          )
          {

            // Compute coordinates and index of neighbour
            B = j + y,
            C = (a + x) * s + B;
            
            // Update HTML
            t.innerHTML = H;
          }
        }
      }
    }
  }
}

// Render the grid
r(s = 9);

</script>