'Zundoko Kiyoshi' in the creative coding style.

'Zundoko Kiyoshi' in the creative coding style.


'Zundoko Kiyoshi' in the creative coding style.

'Zundoko Kiyoshi' is some of the training programs like 'Fizz Buzz'. Some of them in Japan wrote 'Zundoko Kiyoshi' program in various languages.

So I wrote one in the 'Processing' programming language in the creative coding way.

References : Zundoko Bushi by Kiyoshi Hikawa.







The 'Processing' example code.

// Zundoko Kiyoshi.
// Processing 3.2.1
// 2018.03.03
// Got "Zun, Zun Zun, Zun Doko" then say "Kiyoshi!"

/* ---------------------------------------------------------------------- */
class Kiyoshi {

  PShape psKiyosi;
  PShape psStar;
  boolean sayKiyoshi;
  int verseCnt, verseCntMax;

  Kiyoshi() {
    psStar = createStar();

    psKiyosi = createShape(GROUP);
    psKiyosi.addChild(psKI());
    psKiyosi.addChild(psYO());
    psKiyosi.addChild(psSI());
    psKiyosi.addChild(psDoubleNote());

    sayKiyoshi = false;
    verseCnt = 0;
    verseCntMax = 40; // how long KIYOSHI verse
  }

  void setKiyoshi() {
    sayKiyoshi = true;
  }

  void sing() {
    if (sayKiyoshi) {
      // 0 --- pause --- verseCntMax / 2 --- say Kiyoshi! --- verseCntMax * 2
      if (verseCnt < verseCntMax * 2) {
        ++verseCnt;
        if (verseCnt > verseCntMax / 2) {
          drawTile();
          drawSpangle();
          drawKiyoshi();
        }    
      } else {
        verseCnt = 0;
        sayKiyoshi = false;
      }
    }
  }
  
  void drawTile() {
    int count = 0;
    for (int y = -200; y <= 200; y += 20) {
      for (int x = -200; x <= 200; x += 20) {
        pushMatrix();
        rotateZ(radians((frameCount % 90) * 6.0));
        translate(x, y, -100);
        fill(
             (frameCount % 40) * 9,
             60,
             (20 + (count % 2) * 20) * sin(radians((frameCount * 16) % 180)),
             100
             );
        rect(0, 0, 18, 18);
        popMatrix();
        ++count;
      }
    }
  }

  void drawSpangle() {
    float r = random(72);
    for (int i = 0; i < 50; ++i) {
      float x = random(-200, 200);
      float y = random(-200, 200);
      pushMatrix();
      translate(
                x,
                y,
                -100 + random(0, 100)
                );
      rotateZ(radians(r));
      psStar.setFill(color(
                           random(360),
                           100,
                           map(sqrt(pow(x, 2) + pow(y, 2)), 0, 282, 0, 200),
                           100
                           ));
      shape(psStar);
      popMatrix();
    }
  }

  void drawKiyoshi() {
        pushMatrix();
        translate(
                  0,
                  0,
                  -550 + 50 * sin(radians((frameCount * 4) % 180))
                  );
        psKiyosi.setFill(color(80, 80, 100, 100));
        shape(psKiyosi);
        popMatrix();
  }    

}

/* ---------------------------------------------------------------------- */
class Zundoko {

  PShape psZun, psDoko, psWord;
  int cntWordMax;
  PShape[] wordsAry;
  Motion[] motionAry;

  Zundoko() {
    psZun = createShape(GROUP);
    psZun.addChild(psSU());
    psZun.addChild(psSonant());
    psZun.addChild(psN());
    psZun.scale(1.8, 2.0, 1);
    psZun.setFill(color(0,0,0,0));

    psDoko = createShape(GROUP);
    psDoko.addChild(psTO());
    psDoko.addChild(psSonant());
    psDoko.addChild(psKO());
    psDoko.scale(1.8, 2.0, 1);
    psDoko.setFill(color(0,0,0,0));

    cntWordMax = 5;
    wordsAry = new PShape[cntWordMax];
    motionAry = new Motion[cntWordMax];
    for (int i = 0; i < cntWordMax; ++i) {
      wordsAry[i] = createShape(GROUP); // Null shape
      motionAry[i] = new NoMotion(); // Null motion
    }
  }

  void sayZun() {
    psWord = createShape(GROUP);
    psWord.addChild(psZun);
    psWord.setFill(color(frameCount % 360, 60, 90, 100));
    stackWord(psWord);
  }

  void sayDoko() {
    psWord = createShape(GROUP);
    psWord.addChild(psDoko);
    psWord.setFill(color(frameCount % 360, 60, 90, 100));
    stackWord(psWord);
  }

  void stackWord(PShape word) {
    for (int i = 0; i < cntWordMax - 1; ++i) {
      wordsAry[i] = wordsAry[i + 1];
      motionAry[i] = motionAry[i + 1];
    }
    wordsAry[cntWordMax - 1] = word;
    motionAry[cntWordMax - 1] = setMotion();
  }

  Motion setMotion() {
    switch (ceil(random(0.1, 4.9))) {
    case 1:
      return new MotionRotate(140, 0);
    case 2:
      return new MotionShake(140, 0);
    case 3:
      return new MotionPush(140, 0);
    case 4:
      return new MotionPull(140, 0);
    case 5:
      return new MotionSlant(140, 0);
    }
    return new NoMotion(); // fail safe
  }

  void sing() {
    for (int i = 0; i < cntWordMax; ++i) {
      pushMatrix();
      motionAry[i].doMotion();
      shape(wordsAry[i]);
      popMatrix();
    }
  }
  
}

/* Zun Doko motion class ---------------------------------------------------------------- */
interface Motion {
  abstract void doMotion();
}

class MotionRotate implements Motion {
  float sX, sY, sZ;
  float speed;
  float angleZ;

  MotionRotate(float x, float y) {
    sX = x;
    sY = y;
    sZ = -400;
    angleZ = 180;
    speed = 30;
  }
  
  void doMotion() {
    sX -= 1.0;
    translate(sX, sY, sZ);
    if (angleZ > 0) {
      rotateZ(radians(angleZ));
      angleZ -= speed;
      speed = 2 + speed / 1.2;
    } else {
      rotateZ(0);
    }
  }
}

class MotionShake implements Motion {
  float sX, sY, sZ;
  float rZ, divZ;
  int count;

  MotionShake(float x, float y) {
    sX = x;
    sY = y;
    sZ = -400;
    divZ = 60;
    rZ = sZ + divZ;
    count = 0;
  }

  void doMotion() {
    sX -= 1.0;
    if (count < 14) {
      rZ = sZ + map(random(1), 0, 1, -divZ, divZ);
      divZ = divZ / 1.2;
      ++count;
      translate(sX, sY, rZ);
    } else {
      translate(sX, sY, sZ);
    }
  }
}

class MotionPush implements Motion {
  float sX, sY, sZ;
  float speed;

  MotionPush(float x, float y) {
    sX = x;
    sY = y;
    sZ = -250.;
    speed = 3;
  }
  
  void doMotion() {
    sX -= 1.0;
    if (sZ > -400) {
      sZ -= speed;
      speed += 3;
    } else {
      sZ = -400;
    }
    translate(sX, sY, sZ);
  }
}

class MotionPull implements Motion {
  float sX, sY, sZ;
  float speed;

  MotionPull(float x, float y) {
    sX = x;
    sY = y;
    sZ = -600;
    speed = 3;
  }
  
  void doMotion() {
    sX -= 1.0;
    if (sZ < -400) {
      sZ += speed;
      speed += 3;
    } else {
      sZ = -400;
    }
    translate(sX, sY, sZ);
  }
}

class MotionSlant implements Motion {
  float sX, sY, sZ;
  float positionX, positionY;
  float speedX, speedY;
  float magX, magY;
  int count;

  MotionSlant(float x, float y) {
    sX = x;
    sY = y;
    sZ = -400;

    speedX = map(random(1), 0, 1, -3, 3);
    speedY = map(random(1), 0, 1, -3, 3);
    magX = 0.2;
    magY = 0.2;

    positionX = sX - 40 * speedX;
    positionY = sY - 40 * speedY;
    
    count = 0;
  }
  
  void doMotion() {
    sX -= 1;
    if (count < 14) {
      if (pow(sX - positionX, 2) > 10) {
        positionX += speedX;
        speedX = speedX * (1 + magX);
      }
      if (pow(sY - positionY, 2) > 10) {
        positionY += speedY;
        speedY = speedY * (1 + magY);
      }
      translate(positionX, positionY, sZ);
      ++count;
    } else {
      translate(sX, sY, sZ);
    }
  }
}

class NoMotion implements Motion {
  NoMotion() {
  }
  void doMotion() {
    // Nothing to do
  }
}

/* draw laser X ---------------------------------------------------------------- */
class LaserX {

  float lineX, lineY, lineZ;
  float lineHue;
  float divX, divXSpeed;
  int pauseCnt, pauseCntMax;
  
  LaserX() {
    initialize();
  }

  void drawLaser() {
    ++pauseCnt;
    if (pauseCnt < pauseCntMax) {
      return;
    }
    
    if (abs(lineX) > 3000) {
      initialize();
    }

    divX += divX * divXSpeed;
    lineX += divX;
    translate(lineX, lineY, lineZ);
    stroke(lineHue, 30, 40, 100);
    line(-3000, 0, 0, 3000, 0, 0);
    for (int i = -30; i <= 30; ++i) {
      float normalize = map(abs(i), 0, 30, 0.0, 1.0);
      translate(1.0, 0.0, 0.0);
      fill(
           lineHue,
           100,
           5 - normalize * 3,
           100
           );
      ellipse(
              0.0,
              0.0,
              20.0 + normalize * 60,
              10.0 - normalize * 5
              );
    }
    translate(-lineX - 61, -lineY, -lineZ);
  }

  void initialize() {
    if (random(10) < 5) {
      divX = -1.0;
    } else {
      divX = 1.0;
    }
    
    lineX = -2000 * divX;
    lineY = random(150, 200) * divX;
    lineZ = random(-1000, -300);
    divXSpeed = random(0.03, 0.0001);
    lineHue = random(0.0, 360.0);

    pauseCnt = 0;
    pauseCntMax = ceil(random(300));
  }
}

/* draw laser Z ---------------------------------------------------------------- */

class LaserZ {

  float lineX, lineY, lineZ;
  float lineHue;
  float divZ, divZSpeed;
  int pauseCnt, pauseCntMax;
  
  LaserZ() {
    initialize();
  }

  void drawLaser() {
    ++pauseCnt;
    if (pauseCnt < pauseCntMax) {
      return;
    }

    if (abs(lineZ) > 3000) {
      initialize();
    }

    divZ += divZ * divZSpeed;
    lineZ += divZ;
    translate(lineX, lineY, lineZ);
    stroke(lineHue, 30, 20, 100);
    line(0, 0, -3000, 0, 0, 3000);
    for (int i = -50; i <= 50; ++i) {
      float normalize = map(abs(i), 0, 50, 0.0, 1.0);
      translate(0.0, 0.0, 2.0);
      fill(
           lineHue,
           100,
           10 - normalize * 5,
           100
           );
      ellipse(
              0.0,
              0.0,
              15.0 - normalize * 10,
              15.0 - normalize * 10
              );
    }
    translate(-lineX, -lineY, -lineZ - 202);
  }

  void initialize() {
    if (random(10) < 5) {
      divZ = -1.0;
    } else {
      divZ = 1.0;
    }
    lineX = random(-400, 400);
    lineY = random(-150, 150);
    lineZ = -random(2000, 3000) * divZ;
    divZSpeed = random(0.03, 0.0001);
    lineHue = random(0.0, 360.0);

    pauseCnt = 0;
    pauseCntMax = ceil(random(300));
  }
}

/* create PShape utility functions ---------------------------------------------------------------- */

PShape createStar() {
  float vertexRadius = 0;
  float maxRadius = 10;
  int vertexNum = 10;

  PShape psStar = createShape();
  psStar.beginShape();

  for (int i = 0; i <= vertexNum; ++i) {
    if (i%2 == 0) {
      vertexRadius = maxRadius;
    } else {
      vertexRadius = maxRadius / 2.5;
    }
    psStar.vertex(
                  vertexRadius * cos(radians(360 / vertexNum * i)),
                  vertexRadius * sin(radians(360 / vertexNum * i))
                  );
  }

  psStar.endShape(CLOSE);
  return psStar;
}

PShape createQuads(int p[][][], int parts_num) {
  int porder[] = {4,5,6,7, 0,1,5,4, 2,3,7,6, 0,3,7,4, 1,2,6,5}; // Front, Top, Bottom, Left, Right
  int porder_num = 20;
      
  PShape psquads = createShape();
  psquads.beginShape(QUADS);
  for (int j = 0; j < parts_num; ++j) {
    for (int i = 0; i < porder_num; ++i) {
      psquads.vertex(p[j][porder[i]][0], p[j][porder[i]][1], p[j][porder[i]][2]);
    }
  }

  psquads.endShape();
  return psquads;
}

PShape psSU() {

  int parts_num = 3; // 3 parts
  int[][][] p;
  p = new int[parts_num][8][3]; // parts, 8 points, xyz

  /* parts 1 */
  p[0][0][0] = -13; p[0][0][1] = -6;  p[0][0][2] = 0;
  p[0][1][0] = -5;  p[0][1][1] = -6;  p[0][1][2] = 0;
  p[0][2][0] = -6;  p[0][2][1] = -3;  p[0][2][2] = 0;
  p[0][3][0] = -12; p[0][3][1] = -3;  p[0][3][2] = 0;
  for (int i = 4; i < 8; ++i) {
    p[0][i][0] = p[0][i - 4][0];
    p[0][i][1] = p[0][i - 4][1];
    p[0][i][2] = 300;
  }
      
  /* parts 2 */
  p[1][0][0] = -9;  p[1][0][1] = -3; p[1][0][2] = 0;
  p[1][1][0] = -6;  p[1][1][1] = -3; p[1][1][2] = 0;
  p[1][2][0] = -10; p[1][2][1] = 6;  p[1][2][2] = 0;
  p[1][3][0] = -12; p[1][3][1] = 4;  p[1][3][2] = 0;
  for (int i = 4; i < 8; ++i) {
    p[1][i][0] = p[1][i - 4][0];
    p[1][i][1] = p[1][i - 4][1];
    p[1][i][2] = 300;
  }
      
  /* parts 3 */
  p[2][0][0] = -7; p[2][0][1] = -1; p[2][0][2] = 0;
  p[2][1][0] = -3; p[2][1][1] = 5;  p[2][1][2] = 0;
  p[2][2][0] = -5; p[2][2][1] = 6;  p[2][2][2] = 0;
  p[2][3][0] = -8; p[2][3][1] = 2;  p[2][3][2] = 0;
  for (int i = 4; i < 8; ++i) {
    p[2][i][0] = p[2][i - 4][0];
    p[2][i][1] = p[2][i - 4][1];
    p[2][i][2] = 300;
  }
      
  return createQuads(p, parts_num);

}

PShape psN() {

  int parts_num = 2; // 2 parts
  int[][][] p;
  p = new int[parts_num][8][3]; // parts, 8 points, xyz

  /* parts 1 */
  p[0][0][0] = 2;  p[0][0][1] = -4;  p[0][0][2] = 0;
  p[0][1][0] = 6;  p[0][1][1] = -6;  p[0][1][2] = 0;
  p[0][2][0] = 7;  p[0][2][1] = -3;  p[0][2][2] = 0;
  p[0][3][0] = 4;  p[0][3][1] = -1;  p[0][3][2] = 0;
  for (int i = 4; i < 8; ++i) {
    p[0][i][0] = p[0][i - 4][0];
    p[0][i][1] = p[0][i - 4][1];
    p[0][i][2] = 300;
  }
      
  /* parts 2 */
  p[1][0][0] = 1;  p[1][0][1] = 3;  p[1][0][2] = 0;
  p[1][1][0] = 9;  p[1][1][1] = -3; p[1][1][2] = 0;
  p[1][2][0] = 10; p[1][2][1] = 0;  p[1][2][2] = 0;
  p[1][3][0] = 2;  p[1][3][1] = 6;  p[1][3][2] = 0;
  for (int i = 4; i < 8; ++i) {
    p[1][i][0] = p[1][i - 4][0];
    p[1][i][1] = p[1][i - 4][1];
    p[1][i][2] = 300;
  }
  return createQuads(p, parts_num);

}

PShape psTO() {

  int parts_num = 2; // 2 parts
  int[][][] p;
  p = new int[parts_num][8][3]; // parts, 8 points, xyz

  /* parts 1 */
  p[0][0][0] = -9;  p[0][0][1] = -6;  p[0][0][2] = 0;
  p[0][1][0] = -6;  p[0][1][1] = -6;  p[0][1][2] = 0;
  p[0][2][0] = -6;  p[0][2][1] = 6;   p[0][2][2] = 0;
  p[0][3][0] = -9;  p[0][3][1] = 6;  p[0][3][2] = 0;
  for (int i = 4; i < 8; ++i) {
    p[0][i][0] = p[0][i - 4][0];
    p[0][i][1] = p[0][i - 4][1];
    p[0][i][2] = 300;
  }
      
  /* parts 2 */
  p[1][0][0] = -6;  p[1][0][1] = -3; p[1][0][2] = 0;
  p[1][1][0] = -2;  p[1][1][1] = 1;  p[1][1][2] = 0;
  p[1][2][0] = -3;  p[1][2][1] = 3;  p[1][2][2] = 0;
  p[1][3][0] = -6;  p[1][3][1] = 1; p[1][3][2] = 0;
  for (int i = 4; i < 8; ++i) {
    p[1][i][0] = p[1][i - 4][0];
    p[1][i][1] = p[1][i - 4][1];
    p[1][i][2] = 300;
  }
  return createQuads(p, parts_num);

}

PShape psKO() {

  int parts_num = 3; // 3 parts
  int[][][] p;
  p = new int[parts_num][8][3]; // parts, 8 points, xyz

  /* parts 1 */
  p[0][0][0] = 1;  p[0][0][1] = -4;  p[0][0][2] = 0;
  p[0][1][0] = 9;  p[0][1][1] = -4;  p[0][1][2] = 0;
  p[0][2][0] = 9;  p[0][2][1] = -1;  p[0][2][2] = 0;
  p[0][3][0] = 1;   p[0][3][1] = -1;  p[0][3][2] = 0;
  for (int i = 4; i < 8; ++i) {
    p[0][i][0] = p[0][i - 4][0];
    p[0][i][1] = p[0][i - 4][1];
    p[0][i][2] = 300;
  }
      
  /* parts 2 */
  p[1][0][0] = 7;  p[1][0][1] = -1; p[1][0][2] = 0;
  p[1][1][0] = 9; p[1][1][1] = -1; p[1][1][2] = 0;
  p[1][2][0] = 9; p[1][2][1] = 3;  p[1][2][2] = 0;
  p[1][3][0] = 7;  p[1][3][1] = 3;  p[1][3][2] = 0;
  for (int i = 4; i < 8; ++i) {
    p[1][i][0] = p[1][i - 4][0];
    p[1][i][1] = p[1][i - 4][1];
    p[1][i][2] = 300;
  }
      
  /* parts 3 */
  p[2][0][0] = 1;  p[2][0][1] = 3; p[2][0][2] = 0;
  p[2][1][0] = 9;  p[2][1][1] = 3;  p[2][1][2] = 0;
  p[2][2][0] = 9;  p[2][2][1] = 6;  p[2][2][2] = 0;
  p[2][3][0] = 1;  p[2][3][1] = 6;  p[2][3][2] = 0;
  for (int i = 4; i < 8; ++i) {
    p[2][i][0] = p[2][i - 4][0];
    p[2][i][1] = p[2][i - 4][1];
    p[2][i][2] = 300;
  }
      
  return createQuads(p, parts_num);

}

PShape psSonant() {

  int parts_num = 2; // 2 parts
  int[][][] p;
  p = new int[parts_num][8][3]; // parts, 8 points, xyz

  /* parts 1 */
  p[0][0][0] = -5;  p[0][0][1] = -5;  p[0][0][2] = 0;
  p[0][1][0] = -4;  p[0][1][1] = -5;  p[0][1][2] = 0;
  p[0][2][0] = -4;  p[0][2][1] = -3;  p[0][2][2] = 0;
  p[0][3][0] = -5;  p[0][3][1] = -3;  p[0][3][2] = 0;
  for (int i = 4; i < 8; ++i) {
    p[0][i][0] = p[0][i - 4][0];
    p[0][i][1] = p[0][i - 4][1];
    p[0][i][2] = 300;
  }
      
  /* parts 2 */
  p[1][0][0] = -3;  p[1][0][1] = -5;  p[1][0][2] = 0;
  p[1][1][0] = -2;  p[1][1][1] = -5;  p[1][1][2] = 0;
  p[1][2][0] = -2;  p[1][2][1] = -3;  p[1][2][2] = 0;
  p[1][3][0] = -3;  p[1][3][1] = -3;  p[1][3][2] = 0;
  for (int i = 4; i < 8; ++i) {
    p[1][i][0] = p[1][i - 4][0];
    p[1][i][1] = p[1][i - 4][1];
    p[1][i][2] = 300;
  }
  return createQuads(p, parts_num);

}

PShape psKI() {

  int parts_num = 3; // 3 parts
  int[][][] p;
  p = new int[parts_num][8][3]; // parts, 8 points, xyz

  /* parts 1 */
  p[0][0][0] = -90; p[0][0][1] = -12;  p[0][0][2] = 0;
  p[0][1][0] = -56; p[0][1][1] = -22;  p[0][1][2] = 0;
  p[0][2][0] = -52; p[0][2][1] = -12;  p[0][2][2] = 0;
  p[0][3][0] = -85; p[0][3][1] = -2;   p[0][3][2] = 0;
  for (int i = 4; i < 8; ++i) {
    p[0][i][0] = p[0][i - 4][0];
    p[0][i][1] = p[0][i - 4][1];
    p[0][i][2] = 500;
  }
      
  /* parts 2 */
  p[1][0][0] = -85; p[1][0][1] = 6;  p[1][0][2] = 0;
  p[1][1][0] = -51; p[1][1][1] = -4; p[1][1][2] = 0;
  p[1][2][0] = -49; p[1][2][1] = 6;  p[1][2][2] = 0;
  p[1][3][0] = -80; p[1][3][1] = 16; p[1][3][2] = 0;
  for (int i = 4; i < 8; ++i) {
    p[1][i][0] = p[1][i - 4][0];
    p[1][i][1] = p[1][i - 4][1];
    p[1][i][2] = 500;
  }
      
  /* parts 3 */
  p[2][0][0] = -78; p[2][0][1] = -27; p[2][0][2] = 0;
  p[2][1][0] = -68; p[2][1][1] = -30; p[2][1][2] = 0;
  p[2][2][0] = -52; p[2][2][1] = 28;  p[2][2][2] = 0;
  p[2][3][0] = -60; p[2][3][1] = 30;  p[2][3][2] = 0;
  for (int i = 4; i < 8; ++i) {
    p[2][i][0] = p[2][i - 4][0];
    p[2][i][1] = p[2][i - 4][1];
    p[2][i][2] = 500;
  }
      
  return createQuads(p, parts_num);

}

PShape psYO() {

  int parts_num = 4; // 3 parts
  int[][][] p;
  p = new int[parts_num][8][3]; // parts, 8 points, xyz

  /* parts 1 */
  p[0][0][0] = -35; p[0][0][1] = -26;  p[0][0][2] = 0;
  p[0][1][0] = -15; p[0][1][1] = -27;  p[0][1][2] = 0;
  p[0][2][0] = -15; p[0][2][1] = -13;  p[0][2][2] = 0;
  p[0][3][0] = -34; p[0][3][1] = -10;  p[0][3][2] = 0;
  for (int i = 4; i < 8; ++i) {
    p[0][i][0] = p[0][i - 4][0];
    p[0][i][1] = p[0][i - 4][1];
    p[0][i][2] = 500;
  }
      
  /* parts 2 */
  p[1][0][0] = -32; p[1][0][1] = -5; p[1][0][2] = 0;
  p[1][1][0] = -15; p[1][1][1] = -5; p[1][1][2] = 0;
  p[1][2][0] = -15; p[1][2][1] = 5;  p[1][2][2] = 0;
  p[1][3][0] = -33; p[1][3][1] = 3;  p[1][3][2] = 0;
  for (int i = 4; i < 8; ++i) {
    p[1][i][0] = p[1][i - 4][0];
    p[1][i][1] = p[1][i - 4][1];
    p[1][i][2] = 500;
  }
      
  /* parts 3 */
  p[2][0][0] = -33; p[2][0][1] = 12; p[2][0][2] = 0;
  p[2][1][0] = -15; p[2][1][1] = 12; p[2][1][2] = 0;
  p[2][2][0] = -15; p[2][2][1] = 22; p[2][2][2] = 0;
  p[2][3][0] = -33; p[2][3][1] = 25; p[2][3][2] = 0;
  for (int i = 4; i < 8; ++i) {
    p[2][i][0] = p[2][i - 4][0];
    p[2][i][1] = p[2][i - 4][1];
    p[2][i][2] = 500;
  }
      
  /* parts 4 */
  p[3][0][0] = -15; p[3][0][1] = -27; p[3][0][2] = 0;
  p[3][1][0] = -4;  p[3][1][1] = -25; p[3][1][2] = 0;
  p[3][2][0] = -6;  p[3][2][1] = 23;  p[3][2][2] = 0;
  p[3][3][0] = -15; p[3][3][1] = 23;  p[3][3][2] = 0;
  for (int i = 4; i < 8; ++i) {
    p[3][i][0] = p[3][i - 4][0];
    p[3][i][1] = p[3][i - 4][1];
    p[3][i][2] = 500;
  }
      
  return createQuads(p, parts_num);

}

PShape psSI() {

  int parts_num = 3; // 3 parts
  int[][][] p;
  p = new int[parts_num][8][3]; // parts, 8 points, xyz

  /* parts 1 */
  p[0][0][0] = 5;  p[0][0][1] = -2; p[0][0][2] = 0;
  p[0][1][0] = 10; p[0][1][1] = -8; p[0][1][2] = 0;
  p[0][2][0] = 18; p[0][2][1] = 0;  p[0][2][2] = 0;
  p[0][3][0] = 15; p[0][3][1] = 5;  p[0][3][2] = 0;
  for (int i = 4; i < 8; ++i) {
    p[0][i][0] = p[0][i - 4][0];
    p[0][i][1] = p[0][i - 4][1];
    p[0][i][2] = 500;
  }
      
  /* parts 2 */
  p[1][0][0] = 12; p[1][0][1] = -16; p[1][0][2] = 0;
  p[1][1][0] = 20; p[1][1][1] = -20; p[1][1][2] = 0;
  p[1][2][0] = 28; p[1][2][1] = -14; p[1][2][2] = 0;
  p[1][3][0] = 20; p[1][3][1] = -10; p[1][3][2] = 0;
  for (int i = 4; i < 8; ++i) {
    p[1][i][0] = p[1][i - 4][0];
    p[1][i][1] = p[1][i - 4][1];
    p[1][i][2] = 500;
  }
      
  /* parts 3 */
  p[2][0][0] = 5;  p[2][0][1] = 18;  p[2][0][2] = 0;
  p[2][1][0] = 53; p[2][1][1] = -10; p[2][1][2] = 0;
  p[2][2][0] = 55; p[2][2][1] = -5;  p[2][2][2] = 0;
  p[2][3][0] = 10; p[2][3][1] = 28;  p[2][3][2] = 0;
  for (int i = 4; i < 8; ++i) {
    p[2][i][0] = p[2][i - 4][0];
    p[2][i][1] = p[2][i - 4][1];
    p[2][i][2] = 500;
  }
      
  return createQuads(p, parts_num);

}

PShape psDoubleNote() {

  int parts_num = 4; // 3 parts
  int[][][] p;
  p = new int[parts_num][8][3]; // parts, 8 points, xyz

  /* parts 1 */
  p[0][0][0] = 65; p[0][0][1] = -18; p[0][0][2] = 0;
  p[0][1][0] = 78; p[0][1][1] = -15; p[0][1][2] = 0;
  p[0][2][0] = 69; p[0][2][1] = 18;  p[0][2][2] = 0;
  p[0][3][0] = 62; p[0][3][1] = 13;  p[0][3][2] = 0;
  for (int i = 4; i < 8; ++i) {
    p[0][i][0] = p[0][i - 4][0];
    p[0][i][1] = p[0][i - 4][1];
    p[0][i][2] = 500;
  }
      
  /* parts 2 */
  p[1][0][0] = 63; p[1][0][1] = 21; p[1][0][2] = 0;
  p[1][1][0] = 68; p[1][1][1] = 24; p[1][1][2] = 0;
  p[1][2][0] = 66; p[1][2][1] = 29; p[1][2][2] = 0;
  p[1][3][0] = 62; p[1][3][1] = 27; p[1][3][2] = 0;
  for (int i = 4; i < 8; ++i) {
    p[1][i][0] = p[1][i - 4][0];
    p[1][i][1] = p[1][i - 4][1];
    p[1][i][2] = 500;
  }
      
  /* parts 3 */
  p[2][0][0] = 82; p[2][0][1] = -18; p[2][0][2] = 0;
  p[2][1][0] = 90; p[2][1][1] = -15; p[2][1][2] = 0;
  p[2][2][0] = 86; p[2][2][1] = 12;  p[2][2][2] = 0;
  p[2][3][0] = 75; p[2][3][1] = 12;  p[2][3][2] = 0;
  for (int i = 4; i < 8; ++i) {
    p[2][i][0] = p[2][i - 4][0];
    p[2][i][1] = p[2][i - 4][1];
    p[2][i][2] = 500;
  }
      
  /* parts 4 */
  p[3][0][0] = 74; p[3][0][1] = 20; p[3][0][2] = 0;
  p[3][1][0] = 84; p[3][1][1] = 20; p[3][1][2] = 0;
  p[3][2][0] = 84; p[3][2][1] = 29; p[3][2][2] = 0;
  p[3][3][0] = 72; p[3][3][1] = 28; p[3][3][2] = 0;
  for (int i = 4; i < 8; ++i) {
    p[3][i][0] = p[3][i - 4][0];
    p[3][i][1] = p[3][i - 4][1];
    p[3][i][2] = 500;
  }
      
  return createQuads(p, parts_num);

}

/* ---------------------------------------------------------------------- */
Kiyoshi ky;
Zundoko zd;
LaserX lx[];
LaserZ lz[];
int cntZun;

void setup() {
  size(900, 556, P3D);
  colorMode(HSB, 360, 100, 100, 100);
  hint(DISABLE_DEPTH_TEST);
  smooth();

  ky = new Kiyoshi();
  zd = new Zundoko();

  lx = new LaserX[6];
  lz = new LaserZ[6];
  for (int i = 0; i < 6; ++i) {
    lx[i] = new LaserX();
    lz[i] = new LaserZ();
  }

  cntZun = 0;
}

void draw() {
  background(0, 0, 0, 100);

  lightFalloff(
               1.0,
               0.00005,
               0.000002
               );
  pointLight(
             0, 0, 100,
             0, 0, 100
             );
  camera(
         0, 0, 80,
         0, 0, 0,
         0, 1, 0
         );

  // zundoko
  blendMode(LIGHTEST);
  noStroke();
  if (frameCount % (ky.verseCntMax + 10) == 1) {
    if (isDoko()) {
      zd.sayDoko();
      if (cntZun >= 4) {
        ky.setKiyoshi(); // Kiyoshi check success
      }
      cntZun = 0; // Kiyoshi check reset
    } else {
      zd.sayZun();
      ++cntZun;
    }
  }

  zd.sing();
  ky.sing();

  // background
  strokeWeight(0.5);
  for (int i = 0; i < 6; ++i) {
    lx[i].drawLaser();
    lz[i].drawLaser();
  }
  
  // saveFrame("frames/####.png");
  if (frameCount > 30 * 30) {
    exit ();
  }
}

boolean isDoko() {
  if (random(10.0) < 3.0) {
    return true;
  }
  return false;
}

/*
Copyright (C) 2018- deconbatch

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>
*/