canvas实现自定义ai数量俄罗斯方块对战游戏代码

代码语言:html

所属分类:游戏

代码描述:canvas实现自定义ai数量俄罗斯方块对战游戏代码,可设置ai的速度和数量,还可以设置自己的方块速速,还有排行榜,看看你能战胜ai吗?

代码标签: canvas 自定义 ai 数量 俄罗斯 方块 对战 游戏 代码

下面为部分代码预览,完整代码请点击下载或在bfwstudio webide中打开

<!DOCTYPE html>
<html lang="en" >

<head>
  <meta charset="UTF-8">
  

    
  
<style>
/* latin-ext */
@font-face {
  font-family: 'Courier Prime';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(https://fonts.gstatic.com/s/courierprime/v9/u-450q2lgwslOqpF_6gQ8kELaw9pWt_-.woff2) format('woff2');
  unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'Courier Prime';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(https://fonts.gstatic.com/s/courierprime/v9/u-450q2lgwslOqpF_6gQ8kELawFpWg.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
body,html{
  background: #000;
  margin: 0;
  height: 100vh;
  overflow: hidden;
  font-family: Courier Prime;
}
#c:focus{
  outline: none;
}
#c{
  background:#000;
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
}
</style>



  
  
</head>

<body translate="no">
  <canvas id="c" tabindex=0></canvas>
  
      <script >

c = document.querySelector('#c');
c.width = 1920;
c.height = 1080;
x = c.getContext('2d');
C = Math.cos;
S = Math.sin;
t = 0;
T = Math.tan;


rsz = window.onresize = () => {
  setTimeout(() => {
    if (document.body.clientWidth > document.body.clientHeight * 1.77777778) {
      c.style.height = '100vh';
      setTimeout(() => c.style.width = c.clientHeight * 1.77777778 + 'px', 0);
    } else {
      c.style.width = '100vw';
      setTimeout(() => c.style.height = c.clientWidth / 1.77777778 + 'px', 0);
    }
  }, 0);
};
rsz();

async function Draw() {
  oX = oY = oZ = 0;
  if (!t) {

    reflect = (a, n) => {
      let d1 = Math.hypot(...a) + .0001;
      let d2 = Math.hypot(...n) + .0001;
      a[0] /= d1;
      a[1] /= d1;
      a[2] /= d1;
      n[0] /= d2;
      n[1] /= d2;
      n[2] /= d2;
      let dot = -a[0] * n[0] + -a[1] * n[1] + -a[2] * n[2];
      let rx = -a[0] - 2 * n[0] * dot;
      let ry = -a[1] - 2 * n[1] * dot;
      let rz = -a[2] - 2 * n[2] * dot;
      return [-rx * d1, -ry * d1, -rz * d1];
    };

    spawnTunnel = (
    tx, ty, tz,
    rw, cl, sp = 1, rad = .5,
    theta1 = 0, theta2 = 0,
    theta1ModFreq = 0,
    theta1ModMag = 0,
    theta2ModFreq = 0,
    theta2ModMag = 0,
    theta1Offset = 0,
    theta2Offset = 0,
    radModFreq = 0,
    radModMag = 0,
    radModOffset = 0,
    showLine = false) =>
    {
      let X_ = X = tx;
      let Y_ = Y = ty;
      let Z_ = Z = tz;
      let ret = [];
      let p2a, p2, p2a1, ls;
      if (showLine) x.beginPath();
      for (let i = cl + 1; i--;) {
        let p1 = theta1 + C(Math.PI * 2 / cl * i * theta1ModFreq + theta1Offset) * theta1ModMag;
        let p2 = theta2 + C(Math.PI * 2 / cl * i * theta2ModFreq + theta2Offset) * theta2ModMag;
        let p2a1 = theta2 + C(Math.PI * 2 / cl * (i + 1) * theta2ModFreq + theta2Offset) * theta2ModMag;
        let lsa = rad + C(Math.PI * 2 / cl * i * radModFreq + radModOffset) * rad / 2 * radModMag;
        let lsb = rad + C(Math.PI * 2 / cl * (i + 1) * radModFreq + radModOffset) * rad / 2 * radModMag;
        if (i == cl) {
          p2a = p2;
          ls = lsa;
        } else if (i == 0) {
          p2a = p2a1;
          ls = lsb;
        } else {
          p2a = (p2 + p2a1) / 2;
          ls = (lsa + lsb) / 2;
        }
        let a = [];
        for (let j = rw + 1; j--;) {
          p = Math.PI * 2 / rw * j + Math.PI / rw;
          X = S(p) * ls;
          Y = 0;
          Z = C(p) * ls;
          R(-p2a + Math.PI / 2, 0, 0);
          R(0, 0, -p1);
          a = [...a, [X + X_, Y + Y_, Z + Z_]];
        }

        ret = [...ret, a];

        if (showLine) {
          X = X_;
          Y = Y_;
          Z = Z_;
          R(Rl, Pt, Yw, 1);
          if (Z > 0) x.lineTo(...Q());
        }

        vx = C(p1) * C(p2) * sp;
        vy = S(p2) * sp;
        vz = S(p1) * C(p2) * sp;
        X_ += vx;
        Y_ += vy;
        Z_ += vz;
      }
      if (showLine) stroke('#f00', '', 2, false);
      a = [];
      ret.map((v, i) => {
        if (i) {
          let s1 = ret[i];
          let s2 = ret[i - 1];
          for (let j = rw; j--;) {
            b = [];
            let l1_ = (j + 0) % rw;
            let l2_ = (j + 1) % rw;
            X = s1[l1_][0];
            Y = s1[l1_][1];
            Z = s1[l1_][2];
            b = [...b, [X, Y, Z]];
            X = s1[l2_][0];
            Y = s1[l2_][1];
            Z = s1[l2_][2];
            b = [...b, [X, Y, Z]];
            X = s2[l2_][0];
            Y = s2[l2_][1];
            Z = s2[l2_][2];
            b = [...b, [X, Y, Z]];
            X = s2[l1_][0];
            Y = s2[l1_][1];
            Z = s2[l1_][2];
            b = [...b, [X, Y, Z]];
            a = [...a, b];
          }
        }
      });
      return a;
    };

    HSVFromRGB = (R, G, B) => {
      let R_ = R / 256;
      let G_ = G / 256;
      let B_ = B / 256;
      let Cmin = Math.min(R_, G_, B_);
      let Cmax = Math.max(R_, G_, B_);
      let val = Cmax; //(Cmax+Cmin) / 2
      let delta = Cmax - Cmin;
      let sat = Cmax ? delta / Cmax : 0;
      let min = Math.min(R, G, B);
      let max = Math.max(R, G, B);
      let hue = 0;
      if (delta) {
        if (R >= G && R >= B) hue = (G - B) / (max - min);
        if (G >= R && G >= B) hue = 2 + (B - R) / (max - min);
        if (B >= G && B >= R) hue = 4 + (R - G) / (max - min);
      }
      hue *= 60;
      while (hue < 0) hue += 360;
      while (hue >= 360) hue -= 360;
      return [hue, sat, val];
    };

    RGBFromHSV = (H, S, V) => {
      while (H < 0) H += 360;
      while (H >= 360) H -= 360;
      let C = V * S;
      let X = C * (1 - Math.abs(H / 60 % 2 - 1));
      let m = V - C;
      let R_, G_, B_;
      if (H >= 0 && H < 60) R_ = C, G_ = X, B_ = 0;
      if (H >= 60 && H < 120) R_ = X, G_ = C, B_ = 0;
      if (H >= 120 && H < 180) R_ = 0, G_ = C, B_ = X;
      if (H >= 180 && H < 240) R_ = 0, G_ = X, B_ = C;
      if (H >= 240 && H < 300) R_ = X, G_ = 0, B_ = C;
      if (H >= 300 && H < 360) R_ = C, G_ = 0, B_ = X;
      let R = (R_ + m) * 256;
      let G = (G_ + m) * 256;
      let B = (B_ + m) * 256;
      return [R, G, B];
    };

    R = R2 = (Rl, Pt, Yw, m) => {
      M = Math;
      A = M.atan2;
      H = M.hypot;
      X = S(p = A(X, Z) + Yw) * (d = H(X, Z));
      Z = C(p) * d;
      Y = S(p = A(Y, Z) + Pt) * (d = H(Y, Z));
      Z = C(p) * d;
      X = S(p = A(X, Y) + Rl) * (d = H(X, Y));
      Y = C(p) * d;
      if (m) {
        X += oX;
        Y += oY;
        Z += oZ;
      }
    };
    Q = () => [c.width / 2 + X / Z * 700, c.height / 2 + Y / Z * 700];
    I = (A, B, M, D, E, F, G, H) => (K = ((G - E) * (B - F) - (H - F) * (A - E)) / (J = (H - F) * (M - A) - (G - E) * (D - B))) >= 0 && K <= 1 && (L = ((M - A) * (B - F) - (D - B) * (A - E)) / J) >= 0 && L <= 1 ? [A + K * (M - A), B + K * (D - B)] : 0;

    Rn = Math.random;
    async function loadOBJ(url, scale, tx, ty, tz, rl, pt, yw, recenter = true) {
      let res;
      await fetch(url, res => res).then(data => data.text()).then(data => {
        a = [];
        data.split("\nv ").map(v => {
          a = [...a, v.split("\n")[0]];
        });
        a = a.filter((v, i) => i).map(v => [...v.split(' ').map(n => +n.replace("\n", ''))]);
        ax = ay = az = 0;
        a.map(v => {
          v[1] *= -1;
          if (recenter) {
            ax += v[0];
            ay += v[1];
            az += v[2];
          }
        });
        ax /= a.length;
        ay /= a.length;
        az /= a.length;
        a.map(v => {
          X = (v[0] - ax) * scale;
          Y = (v[1] - ay) * scale;
          Z = (v[2] - az) * scale;
          R2(rl, pt, yw, 0);
          v[0] = X;
          v[1] = Y;
          v[2] = Z;
        });
        maxY = -6e6;
        a.map(v => {
          if (v[1] > maxY) maxY = v[1];
        });
        a.map(v => {
          v[1] -= maxY - oY;
          v[0] += tx;
          v[1] += ty;
          v[2] += tz;
        });

        b = [];
        data.split("\nf ").map(v => {
          b = [...b, v.split("\n")[0]];
        });
        b.shift();
        b = b.map(v => v.split(' '));
        b = b.map(v => {
          v = v.map(q => {
            return +q.split('/')[0];
          });
          v = v.filter(q => q);
          return v;
        });

        res = [];
        b.map(v => {
          e = [];
          v.map(q => {
            e = [...e, a[q - 1]];
          });
          e = e.filter(q => q);
          res = [...res, e];
        });
      });
      return res;
    }

    function loadAnimation(name, size, X, Y, Z, rl, pt, yw, speed = 1) {

      let rootURL = 'https://srmcgann.github.io/animations';
      if (typeof animations == 'undefined') animations = [];

      let animation = {
        name,
        speed,
        frameCt: 0,
        fileList: '',
        curFrame: 0,
        loopRangeStart: 0,
        loopRangeEnd: 0,
        hasLoop: false,
        looping: false,
        frameData: [],
        loaded: false,
        active: true };


      fetch(`${rootURL}/${name}/fileList.json`).then(v => v.json()).then(data => {
        animation.fileList = data.fileList;
        if (animation.fileList.hasLoop) {
          animation.hasLoop = true;
          animation.looping = true;
          animation.loopRangeStart = animation.fileList.loopRangeStart;
          animation.loopRangeEnd = animation.fileList.loopRangeEnd;
        }
        for (let i = 0; i < +animation.fileList.fileCount; i++) {
          let file = `${rootURL}/${name}/${animation.fileList.fileName}${i}.${animation.fileList.suffix}`;
          loadOBJ(file, size, X, Y, Z, rl, pt, yw, false).then(el => {
            animation.frameData[i] = el;
            animation.frameCt++;
            if (animation.frameCt == +animation.fileList.fileCount) {
              console.log(`loaded animation: ${name}`);
              animation.loaded = true;
              animations = [...animations, animation];
            }
          });
        }
      });
      return name;
    }

    drawAnimation = (animation_name, scol = '#8888', fcol = '', lineWidth = 2, glowing = true, overrideGlobalAlpha = 1) => {
      let animation = animations.filter(el => animation_name == el.name);
      if (animation.length) {
        animation = animation[0];
        animation.curFrame += animation.speed;
        if (animation.hasLoop && animation.looping) {
          animation.curFrame %= Math.min(animation.loopRangeEnd, animation.frameCt);
          if (animation.curFrame < 1) animation.curFrame = Math.max(0, animation.loopRangeStart);
        } else {
          animation.curFrame %= animation.frameCt;
        }
        animation.frameData[animation.curFrame | 0].map((v, i) => {
          x.beginPath();
          v.map(q => {
            X = q[0];
            Y = q[1];
            Z = q[2];
            R(Rl, Pt, Yw, 1);
            if (Z > 0) x.lineTo(...Q());
          });
          stroke(scol, fcol, lineWidth, glowing, overrideGlobalAlpha);
        });
      }
    };

    geoSphere = (mx, my, mz, iBc, size) => {
      let collapse = 0;
      let B = Array(iBc).fill().map(v => {
        X = Rn() - .5;
        Y = Rn() - .5;
        Z = Rn() - .5;
        return [X, Y, Z];
      });
      for (let m = 200; m--;) {
        B.map((v, i) => {
          X = v[0];
          Y = v[1];
          Z = v[2];
          B.map((q, j) => {
            if (j != i) {
              X2 = q[0];
              Y2 = q[1];
              Z2 = q[2];
              d = 1 + (Math.hypot(X - X2, Y - Y2, Z - Z2) * (3 + iBc / 40) * 3) ** 4;
              X += (X - X2) * 99 / d;
              Y += (Y - Y2) * 99 / d;
              Z += (Z - Z2) * 99 / d;
            }
          });
          d = Math.hypot(X, Y, Z);
          v[0] = X / d;
          v[1] = Y / d;
          v[2] = Z / d;
          if (collapse) {
            d = 25 + Math.hypot(X, Y, Z);
            v[0] = (X - X / d) / 1.1;
            v[1] = (Y - Y / d) / 1.1;
            v[2] = (Z - Z / d) / 1.1;
          }
        });
      }
      mind = 6e6;
      B.map((v, i) => {
        X1 = v[0];
        Y1 = v[1];
        Z1 = v[2];
        B.map((q, j) => {
          X2 = q[0];
          Y2 = q[1];
          Z2 = q[2];
          if (i != j) {
            d = Math.hypot(a = X1 - X2, b = Y1 - Y2, e = Z1 - Z2);
            if (d < mind) mind = d;
          }
        });
      });
      a = [];
      B.map((v, i) => {
        X1 = v[0];
        Y1 = v[1];
        Z1 = v[2];
        B.map((q, j) => {
          X2 = q[0];
          Y2 = q[1];
          Z2 = q[2];
          if (i != j) {
            d = Math.hypot(X1 - X2, Y1 - Y2, Z1 - Z2);
            if (d < mind * 2) {
              if (!a.filter(q => q[0] == X2 && q[1] == Y2 && q[2] == Z2 && q[3] == X1 && q[4] == Y1 && q[5] == Z1).length) a = [...a, [X1 * size, Y1 * size, Z1 * size, X2 * size, Y2 * size, Z2 * size]];
            }
          }
        });
      });
      B.map(v => {
        v[0] *= size;
        v[1] *= size;
        v[2] *= size;
        v[0] += mx;
        v[1] += my;
        v[2] += mz;
      });
      return [mx, my, mz, size, B, a];
    };

    lineFaceI = (X1, Y1, Z1, X2, Y2, Z2, facet, autoFlipNormals = false, showNormals = false) => {
      let X_, Y_, Z_, d, m, l_, K, J, L, p;
      let I_ = (A, B, M, D, E, F, G, H) => (K = ((G - E) * (B - F) - (H - F) * (A - E)) / (J = (H - F) * (M - A) - (G - E) * (D - B))) >= 0 && K <= 1 && (L = ((M - A) * (B - F) - (D - B) * (A - E)) / J) >= 0 && L <= 1 ? [A + K * (M - A), B + K * (D - B)] : 0;
      let Q_ = () => [c.width / 2 + X_ / Z_ * 600, c.height / 2 + Y_ / Z_ * 600];
      let R_ = (Rl, Pt, Yw, m) => {
        let M = Math,A = M.atan2,H = M.hypot;
        X_ = S(p = A(X_, Y_) + Rl) * (d = H(X_, Y_)), Y_ = C(p) * d, X_ = S(p = A(X_, Z_) + Yw) * (d = H(X_, Z_)), Z_ = C(p) * d, Y_ = S(p = A(Y_, Z_) + Pt) * (d = H(Y_, Z_)), Z_ = C(p) * d;
        if (m) {X_ += oX, Y_ += oY, Z_ += oZ;}
      };
      let rotSwitch = m => {
        switch (m) {
          case 0:R_(0, 0, Math.PI / 2);break;
          case 1:R_(0, Math.PI / 2, 0);break;
          case 2:R_(Math.PI / 2, 0, Math.PI / 2);break;}

      };
      let ax = 0,ay = 0,az = 0;
      facet.map(q_ => {ax += q_[0], ay += q_[1], az += q_[2];});
      ax /= facet.length, ay /= facet.length, az /= facet.length;
      let b1 = facet[2][0] - facet[1][0],b2 = facet[2][1] - facet[1][1],b3 = facet[2][2] - facet[1][2];
      let c1 = facet[1][0] - facet[0][0],c2 = facet[1][1] - facet[0][1],c3 = facet[1][2] - facet[0][2];
      let crs = [b2 * c3 - b3 * c2, b3 * c1 - b1 * c3, b1 * c2 - b2 * c1];
      d = Math.hypot(...crs) + .001;
      let nls = 1; //normal line length
      crs = crs.map(q => q / d * nls);
      let X1_ = ax,Y1_ = ay,Z1_ = az;
      let flip = 1;
      if (autoFlipNormals) {
        let d1_ = Math.hypot(X1_ - X1, Y1_ - Y1, Z1_ - Z1);
        let d2_ = Math.hypot(X1 - (ax + crs[0] / 99), Y1 - (ay + crs[1] / 99), Z1 - (az + crs[2] / 99));
        flip = d2_ > d1_ ? -1 : 1;
      }
      let X2_ = ax + (crs[0] *= flip),Y2_ = ay + (crs[1] *= flip),Z2_ = az + (crs[2] *= flip);
      if (showNormals) {
        x.beginPath();
        X_ = X1_, Y_ = Y1_, Z_ = Z1_;
        R_(Rl, Pt, Yw, 1);
        if (Z_ > 0) x.lineTo(...Q_());
        X_ = X2_, Y_ = Y2_, Z_ = Z2_;
        R_(Rl, Pt, Yw, 1);
        if (Z_ > 0) x.lineTo(...Q_());
        x.lineWidth = 5;
        x.strokeStyle = '#f004';
        x.stroke();
      }

      let p1_ = Math.atan2(X2_ - X1_, Z2_ - Z1_);
      let p2_ = -(Math.acos((Y2_ - Y1_) / (Math.hypot(X2_ - X1_, Y2_ - Y1_, Z2_ - Z1_) + .001)) + Math.PI / 2);
      let isc = false,iscs = [false, false, false];
      X_ = X1, Y_ = Y1, Z_ = Z1;
      R_(0, -p2_, -p1_);
      let rx_ = X_,ry_ = Y_,rz_ = Z_;
      for (let m = 3; m--;) {
        if (isc === false) {
          X_ = rx_, Y_ = ry_, Z_ = rz_;
          rotSwitch(m);
          X1_ = X_, Y1_ = Y_, Z1_ = Z_ = 5, X_ = X2, Y_ = Y2, Z_ = Z2;
          R_(0, -p2_, -p1_);
          rotSwitch(m);
          X2_ = X_, Y2_ = Y_, Z2_ = Z_;
          facet.map((q_, j_) => {
            if (isc === false) {
              let l = j_;
              X_ = facet[l][0], Y_ = facet[l][1], Z_ = facet[l][2];
              R_(0, -p2_, -p1_);
              rotSwitch(m);
              let X3_ = X_,Y3_ = Y_,Z3_ = Z_;
              l = (j_ + 1) % facet.length;
              X_ = facet[l][0], Y_ = facet[l][1], Z_ = facet[l][2];
              R_(0, -p2_, -p1_);
              rotSwitch(m);
              let X4_ = X_,Y4_ = Y_,Z4_ = Z_;
              if (l_ = I_(X1_, Y1_, X2_, Y2_, X3_, Y3_, X4_, Y4_)) iscs[m] = l_;
            }
          });
        }
      }
      if (iscs.filter(v => v !== false).length == 3) {
        let iscx = iscs[1][0],iscy = iscs[0][1],iscz = iscs[0][0];
        let pointInPoly = true;
        ax = 0, ay = 0, az = 0;
        facet.map((q_, j_) => {ax += q_[0], ay += q_[1], az += q_[2];});
        ax /= facet.length, ay /= facet.length, az /= facet.length;
        X_ = ax, Y_ = ay, Z_ = az;
        R_(0, -p2_, -p1_);
        X1_ = X_, Y1_ = Y_, Z1_ = Z_;
        X2_ = iscx, Y2_ = iscy, Z2_ = iscz;
        facet.map((q_, j_) => {
          if (pointInPoly) {
            let l = j_;
            X_ = facet[l][0], Y_ = facet[l][1], Z_ = facet[l][2];
            R_(0, -p2_, -p1_);
            let X3_ = X_,Y3_ = Y_,Z3_ = Z_;
            l = (j_ + 1) % facet.length;
            X_ = facet[l][0], Y_ = facet[l][1], Z_ = facet[l][2];
            R_(0, -p2_, -p1_);
            let X4_ = X_,Y4_ = Y_,Z4_ = Z_;
            if (I_(X1_, Y1_, X2_, Y2_, X3_, Y3_, X4_, Y4_)) pointInPoly = false;
          }
        });
        if (pointInPoly) {
          X_ = iscx, Y_ = iscy, Z_ = iscz;
          R_(0, p2_, 0);
          R_(0, 0, p1_);
          isc = [[X_, Y_, Z_], [crs[0], crs[1], crs[2]]];
        }
      }
      return isc;
    };

    TruncatedOctahedron = ls => {
      let shp = [],a = [];
      mind = 6e6;
      for (let i = 6; i--;) {
        X = S(p = Math.PI * 2 / 6 * i + Math.PI / 6) * ls;
        Y = C(p) * ls;
        Z = 0;
        if (Y < mind) mind = Y;
        a = [...a, [X, Y, Z]];
      }
      let theta = .6154797086703867;
      a.map(v => {
        X = v[0];
        Y = v[1] - mind;
        Z = v[2];
        R(0, theta, 0);
        v[0] = X;
        v[1] = Y;
        v[2] = Z + 1.5;
      });
      b = JSON.parse(JSON.stringify(a)).map(v => {
        v[1] *= -1;
        return v;
      });
      shp = [...shp, a, b];
      e = JSON.parse(JSON.stringify(shp)).map(v => {
        v.map(q => {
          X = q[0];
          Y = q[1];
          Z = q[2];
          R(0, 0, Math.PI);
          q[0] = X;
          q[1] = Y;
          q[2] = Z;
        });
        return v;
      });
      shp = [...shp, ...e];
      e = JSON.parse(JSON.stringify(shp)).map(v => {
        v.map(q => {
          X = q[0];
          Y = q[1];
          Z = q[2];
          R(0, 0, Math.PI / 2);
          q[0] = X;
          q[1] = Y;
          q[2] = Z;
        });
        return v;
      });
      shp = [...shp, ...e];

      coords = [
      [[3, 1], [4, 3], [4, 4], [3, 2]],
      [[3, 4], [3, 3], [2, 4], [6, 2]],
      [[1, 4], [0, 3], [0, 4], [4, 2]],
      [[1, 1], [1, 2], [6, 4], [7, 3]],
      [[3, 5], [7, 5], [1, 5], [3, 0]],
      [[2, 5], [6, 5], [0, 5], [4, 5]]];

      a = [];
      coords.map(v => {
        b = [];
        v.map(q => {
          X = shp[q[0]][q[1]][0];
          Y = shp[q[0]][q[1]][1];
          Z = shp[q[0]][q[1]][2];
          b = [...b, [X, Y, Z]];
        });
        a = [...a, b];
      });
      shp = [...shp, ...a];
      return shp.map(v => {
        v.map(q => {
          q[0] /= 3;
          q[1] /= 3;
          q[2] /= 3;
          q[0] *= ls;
          q[1] *= ls;
          q[2] *= ls;
        });
        return v;
      });
    };

    Cylinder = (rw, cl, ls1, ls2) => {
      let a = [];
      for (let i = rw; i--;) {
        let b = [];
        for (let j = cl; j--;) {
          X = S(p = Math.PI * 2 / cl * j) * ls1;
          Y = (1 / rw * i - .5) * ls2;
          Z = C(p) * ls1;
          b = [...b, [X, Y, Z]];
        }
        //a = [...a, b]
        for (let j = cl; j--;) {
          b = [];
          X = S(p = Math.PI * 2 / cl * j) * ls1;
          Y = (1 / rw * i - .5) * ls2;
          Z = C(p) * ls1;
          b = [...b, [X, Y, Z]];
          X = S(p = Math.PI * 2 / cl * (j + 1)) * ls1;
          Y = (1 / rw * i - .5) * ls2;
          Z = C(p) * ls1;
          b = [...b, [X, Y, Z]];
          X = S(p = Math.PI * 2 / cl * (j + 1)) * ls1;
          Y = (1 / rw * (i + 1) - .5) * ls2;
          Z = C(p) * ls1;
          b = [...b, [X, Y, Z]];
          X = S(p = Math.PI * 2 / cl * j) * ls1;
          Y = (1 / rw * (i + 1) - .5) * ls2;
          Z = C(p) * ls1;
          b = [...b, [X, Y, Z]];
          a = [...a, b];
        }
      }
      b = [];
      for (let j = cl; j--;) {
        X = S(p = Math.PI * 2 / cl * j) * ls1;
        Y = ls2 / 2;
        Z = C(p) * ls1;
        b = [...b, [X, Y, Z]];
      }
      //a = [...a, b]
      return a;
    };

    Tetrahedron = size => {
      ret = [];
      a = [];
      let h = size / 1.4142 / 1.25;
      for (i = 3; i--;) {
        X = S(p = Math.PI * 2 / 3 * i) * size / 1.25;
        Y = C(p) * size / 1.25;
        Z = h;
        a = [...a, [X, Y, Z]];
      }
      ret = [...ret, a];
      for (j = 3; j--;) {
        a = [];
        X = 0;
        Y = 0;
        Z = -h;
        a = [...a, [X, Y, Z]];
        X = S(p = Math.PI * 2 / 3 * j) * size / 1.25;
        Y = C(p) * size / 1.25;
        Z = h;
        a = [...a, [X, Y, Z]];
        X = S(p = Math.PI * 2 / 3 * (j + 1)) * size / 1.25;
        Y = C(p) * size / 1.25;
        Z = h;
        a = [...a, [X, Y, Z]];
        ret = [...ret, a];
      }
      ax = ay = az = ct = 0;
      ret.map(v => {
        v.map(q => {
          ax += q[0];
          ay += q[1];
          az += q[2];
          ct++;
        });
      });
      ax /= ct;
      ay /= ct;
      az /= ct;
      ret.map(v => {
        v.map(q => {
          q[0] -= ax;
          q[1] -= ay;
          q[2] -= az;
        });
      });
      return ret;
    };

    Cube = size => {
      for (CB = [], j = 6; j--; CB = [...CB, b]) for (b = [], i = 4; i--;) b = [...b, [(a = [S(p = Math.PI * 2 / 4 * i + Math.PI / 4), C(p), 2 ** .5 / 2])[j % 3] * (l = j < 3 ? size / 1.5 : -size / 1.5), a[(j + 1) % 3] * l, a[(j + 2) % 3] * l]];
      return CB;
    };

    Octahedron = size => {
      ret = [];
      let h = size / 1.25;
      for (j = 8; j--;) {
        a = [];
        X = 0;
        Y = 0;
        Z = h * (j < 4 ? -1 : 1);
        a = [...a, [X, Y, Z]];
        X = S(p = Math.PI * 2 / 4 * j) * size / 1.25;
        Y = C(p) * size / 1.25;
        Z = 0;
        a = [...a, [X, Y, Z]];
        X = S(p = Math.PI * 2 / 4 * (j + 1)) * size / 1.25;
        Y = C(p) * size / 1.25;
        Z = 0;
        a = [...a, [X, Y, Z]];
        ret = [...ret, a];
      }
      return ret;
    };

    Dodecahedron = size => {
      ret = [];
      a = [];
      mind = -6e6;
      for (i = 5; i--;) {
        X = S(p = Math.PI * 2 / 5 * i + Math.PI / 5);
        Y = C(p);
        Z = 0;
        if (Y > mind) mind = Y;
        a = [...a, [X, Y, Z]];
      }
      a.map(v => {
        X = v[0];
        Y = v[1] -= mind;
        Z = v[2];
        R(0, .553573, 0);
        v[0] = X;
        v[1] = Y;
        v[2] = Z;
      });
      b = JSON.parse(JSON.stringify(a));
      b.map(v => {
        v[1] *= -1;
      });
      ret = [...ret, a, b];
      mind = -6e6;
      ret.map(v => {
        v.map(q => {
          X = q[0];
          Y = q[1];
          Z = q[2];
          if (Z > mind) mind = Z;
        });
      });
      d1 = Math.hypot(ret[0][0][0] - ret[0][1][0], ret[0][0][1] - ret[0][1][1], ret[0][0][2] - ret[0][1][2]);
      ret.map(v => {
        v.map(q => {
          q[2] -= mind + d1 / 2;
        });
      });
      b = JSON.parse(JSON.stringify(ret));
      b.map(v => {
        v.map(q => {
          q[2] *= -1;
        });
      });
      ret = [...ret, ...b];
      b = JSON.parse(JSON.stringify(ret));
      b.map(v => {
        v.map(q => {
          X = q[0];
          Y = q[1];
          Z = q[2];
          R(0, 0, Math.PI / 2);
          R(0, Math.PI / 2, 0);
          q[0] = X;
          q[1] = Y;
          q[2] = Z;
        });
      });
      e = JSON.parse(JSON.stringify(ret));
      e.map(v => {
        v.map(q => {
          X = q[0];
          Y = q[1];
          Z = q[2];
          R(0, 0, Math.PI / 2);
          R(Math.PI / 2, 0, 0);
          q[0] = X;
          q[1] = Y;
          q[2] = Z;
        });
      });
      ret = [...ret, ...b, ...e];
      ret.map(v => {
        v.map(q => {
          q[0] *= size / 2;
          q[1] *= size / 2;
          q[2] *= size / 2;
        });
      });
      return ret;
    };

    Icosahedron = size => {
      ret = [];
      let B = [
      [[0, 3], [1, 0], [2, 2]],
      [[0, 3], [1, 0], [1, 3]],
      [[0, 3], [2, 3], [1, 3]],
      [[0, 2], [2, 1], [1, 0]],
      [[0, 2], [1, 3], [1, 0]],
      [[0, 2], [1, 3], [2, 0]],
      [[0, 3], [2, 2], [0, 0]],
      [[1, 0], [2, 2], [2, 1]],
      [[1, 1], [2, 2], [2, 1]],
      [[1, 1], [2, 2], [0, 0]],
      [[1, 1], [2, 1], [0, 1]],
      [[0, 2], [2, 1], [0, 1]],
      [[2, 0], [1, 2], [2, 3]],
      [[0, 0], [0, 3], [2, 3]],
      [[1, 3], [2, 0], [2, 3]],
      [[2, 3], [0, 0], [1, 2]],
      [[1, 2], [2, 0], [0, 1]],
      [[0, 0], [1, 2], [1, 1]],
      [[0, 1], [1, 2], [1, 1]],
      [[0, 2], [2, 0], [0, 1]]];

      for (p = [1, 1], i = 38; i--;) p = [...p, p[l = p.length - 1] + p[l - 1]];
      phi = p[l] / p[l - 1];
      a = [
      [-phi, -1, 0],
      [phi, -1, 0],
      [phi, 1, 0],
      [-phi, 1, 0]];

      for (j = 3; j--; ret = [...ret, b]) for (b = [], i = 4; i--;) b = [...b, [a[i][j], a[i][(j + 1) % 3], a[i][(j + 2) % 3]]];
      ret.map(v => {
        v.map(q => {
          q[0] *= size / 2.25;
          q[1] *= size / 2.25;
          q[2] *= size / 2.25;
        });
      });
      cp = JSON.parse(JSON.stringify(ret));
      out = [];
      a = [];
      B.map(v => {
        idx1a = v[0][0];
        idx2a = v[1][0];
        idx3a = v[2][0];
        idx1b = v[0][1];
        idx2b = v[1][1];
        idx3b = v[2][1];
        a = [...a, [cp[idx1a][idx1b], cp[idx2a][idx2b], cp[idx3a][idx3b]]];
      });
      out = [...out, ...a];
      return out;
    };

    stroke = (scol, fcol, lwo = 1, od = true, oga = 1, overrideClosePath = false) => {
      if (scol) {
        if (!overrideClosePath) x.closePath();
        if (od) x.globalAlpha = .2 * oga;
        x.strokeStyle = scol;
        x.lineWidth = Math.min(1000, 100 * lwo / Z);
        if (od) x.stroke();
        x.lineWidth /= 4;
        x.globalAlpha = 1 * oga;
        x.stroke();
      }
      if (fcol) {
        x.globalAlpha = 1 * oga;
        x.fillStyle = fcol;
        x.fill();
      }
      x.globalAlpha = 1;
    };

    bezTo = (X1, Y1, X2, Y2, col1, col2, lw = 1, dual = true, oga = 1, horizontal = true) => {
      let Xa, Ya, Xb, Yb, X, Y, l1, l2, l3;
      if (horizontal) {
        Xa = X1 + (X2 - X1) / 3 * 2;
        Ya = Y1;
        Xb = X1 + (X2 - X1) / 3 * 1;
        Yb = Y2;
      } else {
        Xa = X1;
        Ya = Y1 + (Y2 - Y1) / 3 * 2;
        Xb = X2;
        Yb = Y1 + (Y2 - Y1) / 3 * 1;
      }
      x.beginPath();
      X = X1;
      Y = Y1;
      x.moveTo(X, Y);
      x.bezierCurveTo(Xa, Ya, Xb, Yb, X2, Y2);
      Z = 5;
      stroke(col1, col2, lw, dual, oga, true);
    };

    subbed = (subs, size, sphereize, shape) => {
      for (let m = subs; m--;) {
        base = shape;
        shape = [];
        base.map(v => {
          l = 0;
          X1 = v[l][0];
          Y1 = v[l][1];
          Z1 = v[l][2];
          l = 1;
          X2 = v[l][0];
          Y2 = v[l][1];
          Z2 = v[l][2];
          l = 2;
          X3 = v[l][0];
          Y3 = v[l][1];
          Z3 = v[l][2];
          if (v.length > 3) {
            l = 3;
            X4 = v[l][0];
            Y4 = v[l][1];
            Z4 = v[l][2];
            if (v.length > 4) {
              l = 4;
              X5 = v[l][0];
              Y5 = v[l][1];
              Z5 = v[l][2];
            }
          }
          mx1 = (X1 + X2) / 2;
          my1 = (Y1 + Y2) / 2;
          mz1 = (Z1 + Z2) / 2;
          mx2 = (X2 + X3) / 2;
          my2 = (Y2 + Y3) / 2;
          mz2 = (Z2 + Z3) / 2;
          a = [];
          switch (v.length) {
            case 3:
              mx3 = (X3 + X1) / 2;
              my3 = (Y3 + Y1) / 2;
              mz3 = (Z3 + Z1) / 2;
              X = X1, Y = Y1, Z = Z1, a = [...a, [X, Y, Z]];
              X = mx1, Y = my1, Z = mz1, a = [...a, [X, Y, Z]];
              X = mx3, Y = my3, Z = mz3, a = [...a, [X, Y, Z]];
              shape = [...shape, a];
              a = [];
              X = mx1, Y = my1, Z = mz1, a = [...a, [X, Y, Z]];
              X = X2, Y = Y2, Z = Z2, a = [...a, [X, Y, Z]];
              X = mx2, Y = my2, Z = mz2, a = [...a, [X, Y, Z]];
              shape = [...shape, a];
              a = [];
              X = mx3, Y = my3, Z = mz3, a = [...a, [X, Y, Z]];
              X = mx2, Y = my2, Z = mz2, a = [...a, [X, Y, Z]];
              X = X3, Y = Y3, Z = Z3, a = [...a, [X, Y, Z]];
              shape = [...shape, a];
              a = [];
              X = mx1, Y = my1, Z = mz1, a = [...a, [X, Y, Z]];
              X = mx2, Y = my2, Z = mz2, a = [...a, [X, Y, Z]];
              X = mx3, Y = my3, Z = mz3, a = [...a, [X, Y, Z]];
              shape = [...shape, a];
              break;
            case 4:
              mx3 = (X3 + X4) / 2;
              my3 = (Y3 + Y4) / 2;
              mz3 = (Z3 + Z4) / 2;
              mx4 = (X4 + X1) / 2;
              my4 = (Y4 + Y1) / 2;
              mz4 = (Z4 + Z1) / 2;
              cx = (X1 + X2 + X3 + X4) / 4;
              cy = (Y1 + Y2 + Y3 + Y4) / 4;
              cz = (Z1 + Z2 + Z3 + Z4) / 4;
              X = X1, Y = Y1, Z = Z1, a = [...a, [X, Y, Z]];
              X = mx1, Y = my1, Z = mz1, a = [...a, [X, Y, Z]];
              X = cx, Y = cy, Z = cz, a = [...a, [X, Y, Z]];
              X = mx4, Y = my4, Z = mz4, a = [...a, [X, Y, Z]];
              shape = [...shape, a];
              a = [];
              X = mx1, Y = my1, Z = mz1, a = [...a, [X, Y, Z]];
              X = X2, Y = Y2, Z = Z2, a = [...a, [X, Y, Z]];
              X = mx2, Y = my2, Z = mz2, a = [...a, [X, Y, Z]];
              X = cx, Y = cy, Z = cz, a = [...a, [X, Y, Z]];
              shape = [...shape, a];
              a = [];
              X = cx, Y = cy, Z = cz, a = [...a, [X, Y, Z]];
              X = mx2, Y = my2, Z = mz2, a = [...a, [X, Y, Z]];
              X = X3, Y = Y3, Z = Z3, a = [...a, [X, Y, Z]];
              X = mx3, Y = my3, Z = mz3, a = [...a, [X, Y, Z]];
              shape = [...shape, a];
              a = [];
              X = mx4, Y = my4, Z = mz4, a = [...a, [X, Y, Z]];
              X = cx, Y = cy, Z = cz, a = [...a, [X, Y, Z]];
              X = mx3, Y = my3, Z = mz3, a = [...a, [X, Y, Z]];
              X = X4, Y = Y4, Z = Z4, a = [...a, [X, Y, Z]];
              shape = [...shape, a];
              break;
            case 5:
              cx = (X1 + X2 + X3 + X4 + X5) / 5;
              cy = (Y1 + Y2 + Y3 + Y4 + Y5) / 5;
              cz = (Z1 + Z2 + Z3 + Z4 + Z5) / 5;
              mx3 = (X3 + X4) / 2;
              my3 = (Y3 + Y4) / 2;
              mz3 = (Z3 + Z4) / 2;
              mx4 = (X4 + X5) / 2;
              my4 = (Y4 + Y5) / 2;
              mz4 = (Z4 + Z5) / 2;
              mx5 = (X5 + X1) / 2;
              my5 = (Y5 + Y1) / 2;
              mz5 = (Z5 + Z1) / 2;
              X = X1, Y = Y1, Z = Z1, a = [...a, [X, Y, Z]];
              X = X2, Y = Y2, Z = Z2, a = [...a, [X, Y, Z]];
              X = cx, Y = cy, Z = cz, a = [...a, [X, Y, Z]];
              shape = [...shape, a];
              a = [];
              X = X2, Y = Y2, Z = Z2, a = [...a, [X, Y, Z]];
              X = X3, Y = Y3, Z = Z3, a = [...a, [X, Y, Z]];
              X = cx, Y = cy, Z = cz, a = [...a, [X, Y, Z]];
              shape = [...shape, a];
              a = [];
              X = X3, Y = Y3, Z = Z3, a = [...a, [X, Y, Z]];
              X = X4, Y = Y4, Z = Z4, a = [...a, [X, Y, Z]];
              X = cx, Y = cy, Z = cz, a = [...a, [X, Y, Z]];
              shape = [...shape, a];
              a = [];
              X = X4, Y = Y4, Z = Z4, a = [...a, [X, Y, Z]];
              X = X5, Y = Y5, Z = Z5, a = [...a, [X, Y, Z]];
              X = cx, Y = cy, Z = cz, a = [...a, [X, Y, Z]];
              shape = [...shape, a];
              a = [];
              X = X5, Y = Y5, Z = Z5, a = [...a, [X, Y, Z]];
              X = X1, Y = Y1, Z = Z1, a = [...a, [X, Y, Z]];
              X = cx, Y = cy, Z = cz, a = [...a, [X, Y, Z]];
              shape = [...shape, a];
              a = [];
              break;}

        });
      }
      if (sphereize) {
        ip1 = sphereize;
        ip2 = 1 - sphereize;
        shape = shape.map(v => {
          v = v.map(q => {
            X = q[0];
            Y = q[1];
            Z = q[2];
            d = Math.hypot(X, Y, Z);
            X /= d;
            Y /= d;
            Z /= d;
            X *= size * .75 * ip1 + d * ip2;
            Y *= size * .75 * ip1 + d * ip2;
            Z *= size * .75 * ip1 + d * ip2;
            return [X, Y, Z];
          });
          return v;
        });
      }
      return shape;
    };

    subDividedIcosahedron = (size, subs, sphereize = 0) => subbed(subs, size, sphereize, Icosahedron(size));
    subDividedTetrahedron = (size, subs, sphereize = 0) => subbed(subs, size, sphereize, Tetrahedron(size));
    subDividedOctahedron = (size, subs, sphereize = 0) => subbed(subs, size, sphereize, Octahedron(size));
    subDividedCube = (size, subs, sphereize = 0) => subbed(subs, size, sphereize, Cube(size));
    subDividedDodecahedron = (size, subs, sphereize = 0) => subbed(subs, size, sphereize, Dodecahedron(size));

    Rn = Math.random;

    LsystemRecurse = (size, splits, p1, p2, stem, theta, LsystemReduction, twistFactor) => {
      if (size < .25) return;
      let X1 = stem[0];
      let Y1 = stem[1];
      let Z1 = stem[2];
      let X2 = stem[3];
      let Y2 = stem[4];
      let Z2 = stem[5];
      let p1a = Math.atan2(X2 - X1, Z2 - Z1);
      let p2a = -Math.acos((Y2 - Y1) / (Math.hypot(X2 - X1, Y2 - Y1, Z2 - Z1) + .0001)) + Math.PI;
      size /= LsystemReduction;
      for (let i = splits; i--;) {
        X = 0;
        Y = -size;
        Z = 0;
        R(0, theta, 0);
        R(0, 0, Math.PI * 2 / splits * i + twistFactor);
        R(0, p2a, 0);
        R(0, 0, p1a + twistFactor);
        X += X2;
        Y += Y2;
        Z += Z2;
        let newStem = [X2, Y2, Z2, X, Y, Z];
        Lshp = [...Lshp, newStem];
        LsystemRecurse(size, splits, p1 + Math.PI * 2 / splits * i + twistFactor, p2 + theta, newStem, theta, LsystemReduction, twistFactor);
      }
    };
    DrawLsystem = shp => {
      shp.map(v => {
        x.beginPath();
        X = v[0];
        Y = v[1];
        Z = v[2];
        R(Rl, Pt, Yw, 1);
        if (Z > 0) x.lineTo(...Q());
        X = v[3];
        Y = v[4];
        Z = v[5];
        R(Rl, Pt, Yw, 1);
        if (Z > 0) x.lineTo(...Q());
        lwo = Math.hypot(v[0] - v[3], v[1] - v[4], v[2] - v[5]) * 4;
        stroke('#0f82', '', lwo);
      });

    };
    Lsystem = (size, splits, theta, LsystemReduction, twistFactor) => {
      Lshp = [];
      stem = [0, 0, 0, 0, -size, 0];
      Lshp = [...Lshp, stem];
      LsystemRecurse(size, splits, 0, 0, stem, theta, LsystemReduction, twistFactor);
      Lshp.map(v => {
        v[1] += size * 1.5;
        v[4] += size * 1.5;
      });
      return Lshp;
    };

    Sphere = (ls, rw, cl) => {
      a = [];
      ls /= 1.25;
      for (j = rw; j--;) {
        for (i = cl; i--;) {
          b = [];
          X = S(p = Math.PI * 2 / cl * i) * S(q = Math.PI / rw * j) * ls;
          Y = C(q) * ls;
          Z = C(p) * S(q) * ls;
          b = [...b, [X, Y, Z]];
          X = S(p = Math.PI * 2 / cl * (i + 1)) * S(q = Math.PI / rw * j) * ls;
          Y = C(q) * ls;
          Z = C(p) * S(q) * ls;
          b = [...b, [X, Y, Z]];
          X = S(p = Math.PI * 2 / cl * (i + 1)) * S(q = Math.PI / rw * (j + 1)) * ls;
          Y = C(q) * ls;
          Z = C(p) * S(q) * ls;
          b = [...b, [X, Y, Z]];
          X = S(p = Math.PI * 2 / cl * i) * S(q = Math.PI / rw * (j + 1)) * ls;
          Y = C(q) * ls;
          Z = C(p) * S(q) * ls;
          b = [...b, [X, Y, Z]];
          a = [...a, b];
        }
      }
      return a;
    };

    Torus = (rw, cl, ls1, ls2, parts = 1, twists = 0, part_spacing = 1.5) => {
      let ret = [],tx = 0,ty = 0,tz = 0,prl1 = 0,p2a = 0;
      let tx1 = 0,ty1 = 0,tz1 = 0,prl2 = 0,p2b = 0,tx2 = 0,ty2 = 0,tz2 = 0;
      for (let m = parts; m--;) {
        avgs = Array(rw).fill().map(v => [0, 0, 0]);
        for (j = rw; j--;) for (let i = cl; i--;) {
          if (parts > 1) {
            ls3 = ls1 * part_spacing;
            X = S(p = Math.PI * 2 / parts * m) * ls3;
            Y = C(p) * ls3;
            Z = 0;
            R(prl1 = Math.PI * 2 / rw * (j - 1) * twists, 0, 0);
            tx1 = X;
            ty1 = Y;
            tz1 = Z;
            R(0, 0, Math.PI * 2 / rw * (j - 1));
            ax1 = X;
            ay1 = Y;
            az1 = Z;
            X = S(p = Math.PI * 2 / parts * m) * ls3;
            Y = C(p) * ls3;
            Z = 0;
            R(prl2 = Math.PI * 2 / rw * j * twists, 0, 0);
            tx2 = X;
            ty2 = Y;
            tz2 = Z;
            R(0, 0, Math.PI * 2 / rw * j);
            ax2 = X;
            ay2 = Y;
            az2 = Z;
            p1a = Math.atan2(ax2 - ax1, az2 - az1);
            p2a = Math.PI / 2 + Math.acos((ay2 - ay1) / (Math.hypot(ax2 - ax1, ay2 - ay1, az2 - az1) + .001));

            X = S(p = Math.PI * 2 / parts * m) * ls3;
            Y = C(p) * ls3;
            Z = 0;
            R(Math.PI * 2 / rw * j * twists, 0, 0);
            tx1b = X;
            ty1b = Y;
            tz1b = Z;
            R(0, 0, Math.PI * 2 / rw * j);
            ax1b = X;
            ay1b = Y;
            az1b = Z;
            X = S(p = Math.PI * 2 / parts * m) * ls3;
            Y = C(p) * ls3;
            Z = 0;
            R(Math.PI * 2 / rw * (j + 1) * twists, 0, 0);
            tx2b = X;
            ty2b = Y;
            tz2b = Z;
            R(0, 0, Math.PI * 2 / rw * (j + 1));
            ax2b = X;
            ay2b = Y;
            az2b = Z;
            p1b = Math.atan2(ax2b - ax1b, az2b - az1b);
            p2b = Math.PI / 2 + Math.acos((ay2b - ay1b) / (Math.hypot(ax2b - ax1b, ay2b - ay1b, az2b - az1b) + .001));
          }
          a = [];
          X = S(p = Math.PI * 2 / cl * i) * ls1;
          Y = C(p) * ls1;
          Z = 0;
          //R(0,0,-p1a)
          R(prl1, p2a, 0);
          X += ls2 + tx1, Y += ty1, Z += tz1;
          R(0, 0, Math.PI * 2 / rw * j);
          a = [...a, [X, Y, Z]];
          X = S(p = Math.PI * 2 / cl * (i + 1)) * ls1;
          Y = C(p) * ls1;
          Z = 0;
          //R(0,0,-p1a)
          R(prl1, p2a, 0);
          X += ls2 + tx1, Y += ty1, Z += tz1;
          R(0, 0, Math.PI * 2 / rw * j);
          a = [...a, [X, Y, Z]];
          X = S(p = Math.PI * 2 / cl * (i + 1)) * ls1;
          Y = C(p) * ls1;
          Z = 0;
          //R(0,0,-p1b)
          R(prl2, p2b, 0);
          X += ls2 + tx2, Y += ty2, Z += tz2;
          R(0, 0, Math.PI * 2 / rw * (j + 1));
          a = [...a, [X, Y, Z]];
          X = S(p = Math.PI * 2 / cl * i) * ls1;
          Y = C(p) * ls1;
          Z = 0;
          //R(0,0,-p1b)
          R(prl2, p2b, 0);
          X += ls2 + tx2, Y += ty2, Z += tz2;
          R(0, 0, Math.PI * 2 / rw * (j + 1));
          a = [...a, [X, Y, Z]];
          ret = [...ret, a];
        }
      }
      return ret;
    };

    G_ = 100000, iSTc = 1e4;
    ST = Array(iSTc).fill().map(v => {
      X = (Rn() - .5) * G_;
      Y = (Rn() - .5) * G_;
      Z = (Rn() - .5) * G_;
      return [X, Y, Z];
    });

    burst = new Image();
    burst.src = "https://srmcgann.github.io/temp/burst.png";

    document.body.onload = () => {
      c.focus();
    };

    starsLoaded = false, starImgs = [{ loaded: false }];
    starImgs = Array(9).fill().map((v, i) => {
      let a = { img: new Image(), loaded: false };
      a.img.onload = () => {
        a.loaded = true;
        setTimeout(() => {
          if (starImgs.filter(v => v.loaded).length == 9) starsLoaded = true;
        }, 0);
      };
      a.img.src = `//repo.bfw.wiki/bfwrepo/images/game/fangkuai/star${i + 1}.png`;
      return a;
    });

    showstars = true;

    tboard = new Image();
    //tboard.src = 'tboard.png'
    tboard.src = '//repo.bfw.wiki/bfwrepo/images/game/fangkuai/tXnnqzu.png';
    tboard_inactive = new Image();
    //tboard_inactive.src = 'tboard_inactive.jpg'
    tboard_inactive.src = '//repo.bfw.wiki/bfwrepo/images/game/fangkuai/FhBEuaY.jpeg';
    gridoverlay = new Image();
    //gridoverlay.src = 'gridoverlay.png'
    gridoverlay.src = '//repo.bfw.wiki/bfwrepo/images/game/fangkuai/QzF5K9b.png';
    border = new Image();
    //border.src = 'border.png'
    border.src = '//repo.bfw.wiki/bfwrepo/images/game/fangkuai/yzLTMwN.png';

    manualBoards = false;

    spawnPiece = (uid, rndPc = true, pc = 0) => {
      let ret = [];
      if (rndPc && uid != -1 && typeof players != 'undefined' && players.length) {
        let player = players.filter(player => player.id == uid);
        let np;
        if (player.length && typeof player[0]?.nextPieces != 'undefined' && player[0]?.nextPieces.length) {
          np = player[0]?.nextPieces[player[0]?.nextPieces.length - 1][1];

          //force non-duplicate next-piece

          do {
            pieceSel = Rn() * 7 | 0;
          } while (np == pieceSel);

          // use NES Tetris® rule: re-draw only once on duplicate
          //pieceSel = Rn()*7|0
        } else {
          pieceSel = Rn() * 7 | 0;
        }
      } else {
        pieceSel = rndPc ? Rn() * 7 | 0 : pc;
      }
      switch (pieceSel) {
        case 0: // longboi
          ret = [
          [0, 0, 0],
          [0, 1, 0],
          [0, -2, 0],
          [0, -1, 0]];

          break;
        case 1: // square
          ret = [
          [0, 0, 0],
          [-1, -1, 0],
          [0, -1, 0],
          [-1, 0, 0]];

          break;
        case 2: // zig
          ret = [
          [0, -1, 0],
          [-1, -2, 0],
          [-1, -1, 0],
          [0, 0, 0]];

          break;
        case 3: // zag
          ret = [
          [0, -1, 0],
          [0, -2, 0],
          [-1, -1, 0],
          [-1, 0, 0]];

          break;
        case 4: // L
          ret = [
          [-1, -1, 0],
          [-1, 0, 0],
          [0, 0, 0],
          [-1, -2, 0]];

          break;
        case 5: // 7
          ret = [
          [0, -1, 0],
          [0, 0, 0],
          [0, -2, 0],
          [-1, 0, 0]];

          break;
        case 6: // T
          ret = [
          [0, 0, 0],
          [-1, 0, 0],
          [1, 0, 0],
          [0, -1, 0]];

          break;}

      ret = ret.map(v => {
        v[1] -= 3;
        X = v[6] = v[3] = v[0];
        Y = v[7] = v[4] = v[1];
        Z = v[8] = v[5] = v[2];
        if (typeof players != 'undefined' && players.length && players.length - 1 > uid) {
          let idx = Y * 10 + X + 95;
          l = players.filter(player => player.id == uid)[0];
          if (l.alive && l.box[idx] != -1) {
            l.alive = false;
            l.respawns++;
          }
        }
        return v;
      });
      return [ret, pieceSel];
    };

    spawnPlayer = (uid, max = 0, speed = 10, tetrises = 0, respawns = 0) => {
      let ret = {
        keys: Array(128).fill().map(v => false),
        keyTimers: Array(128).fill(0),
        box: Array(10 * 30).fill(-1),
        alive: true,
        name: uid ? 'AI p. ' + uid : 'human',
        deathGrow: 0,
        pieceSwapped: false,
        moveDecided: false,
        speed: speed,
        respawns: respawns,
        tetrises: tetrises,
        rowsCompleted: 0,
        maxRowsCompleted: max,
        swapPiece: [],
        keyQueue: [0, 0],
        curPiece: spawnPiece(uid),
        id: uid,
        settledTimer: -1,
        nextPieces: Array(10).fill().map(piece => spawnPiece(uid)) };


      return ret;
    };

    masterInit = (setAI = false) => {

      if (1 || !setAI) {
        rw = 20;
        cl = 10;
        localsp = sp = 2.125;
        spawnBoard = (tx, ty, tz,
        sp, active = false,
        name = 'unknown player') => {
          let board = {};
          if (name.length > 7) {
            name = name.substring(0, 7) + '…';
          }
          board.grid = Array(rw * cl).fill().map((v, i) => {
            X = (i % cl - cl / 2 + .5) * sp + tx;
            Y = ((i / cl | 0) - rw / 2 + .5) * sp + ty;
            Z = tz;
            return [X, Y, Z];
          });
          board.sp = sp;
          board.active = active;
          return board;
        };
        boards = [];
        boards = [...boards, spawnBoard(-sp * 12.5, 3, 0, sp)];
        cbcl = 5;
        cbrw = 3;
        remotesp = sp = .5;
        for (let i = 0; i < cbcl * cbrw; i++) {
          let X = (i % cbcl - cbcl / 2 + .5) * sp * 12 + sp * 40 + 4;
          let Y = ((i / cbcl | 0) - cbrw / 2 + .5) * sp * 27 - 4.5;
          let Z = 0;
          boards = [...boards, spawnBoard(X, Y, Z, sp, !i)];
        }
      }

      players = [];
      iPlayerc = 4;
      if (setAI) {
        do {
          iPlayerc = +prompt('how many AI players? [0-14]');
        } while (!(iPlayerc !== null && iPlayerc >= 0 && iPlayerc <= 14));
      }
      showSideBars = true;
      sidebarPieces = setAI ? sidebarPieces : Array(7).fill().map((v, i) => spawnPiece(-1, false, i));
      settleInterval = .15;
      showInfo = false;
      mousedown = false;
      instadropAI = false;
      buttons = setAI ? buttons : [];
      buttonsLoaded = setAI ? buttonsLoaded : false;
      dropTimer = 50;
      flashes = [];
      lerpFactor = 5;
      sortCriteria = 'maxRowsCompleted';
      paused = false;
      sliders = setAI ? sliders : [];
      grav = .005;
      showLeaderBoard = true;
      AISpeed = setAI ? AISpeed : 25;
      gameInPlay = true;
      keyTimerInterval = .14;
      tplayer = spawnPlayer(0);
      players = [tplayer, tplayer];
      Array(iPlayerc).fill().map((player, idx) => {
        players = [...players, spawnPlayer(idx + 1, 0, AISpeed)];
      });
      players.map((player, idx) => {
        boards[idx].active = true;
      });
    };
    masterInit();


    togglePause = () => {
      paused = !paused;
    };

    c.onkeydown = e => {
      e.preventDefault();
      e.stopPropagation();
      let player = players[0];
      player.keys[e.keyCode] = true;
      if (e.keyCode == 32 && player.deathGrow > 200) resetGame(player, 0);
    };

    c.onkeyup = e => {
      e.preventDefault();
      e.stopPropagation();
      let player = players[0];
      player.keys[e.keyCode] = false;
      player.keyTimers[e.keyCode] = 0;
    };

    c.onmousedown = e => {
      let rect = c.getBoundingClientRect();
      mx = (e.pageX - rect.left) / c.clientWidth * c.width;
      my = (e.pageY - rect.top) / c.clientHeight * c.height;
      if (sliders.length) {
        sliders.map(slider => {
          X = slider.posX - slider.width / 2 + slider.width / (slider.max - slider.min) * eval(slider.valVariable);
    .........完整代码请登录后点击上方下载按钮下载查看

网友评论0