resolveSynchroneMove(move) {
    let m1 = this.whiteMove;
    let m2 = move;
    const movingLikeCapture = (m) => {
      const shift = (m.vanish[0].c == 'w' ? -1 : 1);
      return (
        m.start.x + shift == m.end.x &&
        Math.abs(m.end.y - m.start.y) == 1
      );
    };
    const isPossible = (m, other) => {
      return (
        (
          m.vanish[0].p == V.KNIGHT &&
          (
            m.vanish.length == 1 ||
            m.vanish[1].c != m.vanish[0].c ||
            // Self-capture attempt
            (
              !other.end.illegal &&
              other.end.x == m.end.x &&
              other.end.y == m.end.y
            )
          )
        )
        ||
        (
          m.vanish[0].p == V.PAWN &&
          !other.end.illegal &&
          (
            (
              // Promotion attempt
              m.end.x == (m.vanish[0].c == "w" ? 0 : V.size.x - 1) &&
              other.vanish.length == 2 &&
              other.vanish[1].p == V.KNIGHT &&
              other.vanish[1].c == m.vanish[0].c
            )
            ||
            (
              // Moving attempt
              !movingLikeCapture(m) &&
              other.start.x == m.end.x &&
              other.start.y == m.end.y
            )
            ||
            (
              // Capture attempt
              movingLikeCapture(m) &&
              other.end.x == m.end.x &&
              other.end.y == m.end.y
            )
          )
        )
      );
    };
    if (!!m1.end.illegal && !isPossible(m1, m2)) {
      // Either an anticipated capture of something which didn't move
      // (or not to the right square), or a push through blocus.
      // ==> Just discard the move, and add a penalty point
      this.penaltyFlags[m1.vanish[0].c]++;
      m1.isNull = true;
    }
    if (!!m2.end.illegal && !isPossible(m2, m1)) {
      this.penaltyFlags[m2.vanish[0].c]++;
      m2.isNull = true;
    }
    if (!!m1.isNull) m1 = null;
    if (!!m2.isNull) m2 = null;
    // If one move is illegal, just execute the other
    if (!m1 && !!m2) return m2;
    if (!m2 && !!m1) return m1;
    // For PlayOnBoard (no need for start / end, irrelevant)
    let smove = {
      appear: [],
      vanish: []
    };
    if (!m1 && !m2) return smove;
    // Both moves are now legal or at least possible:
    smove.vanish.push(m1.vanish[0]);
    smove.vanish.push(m2.vanish[0]);
    if ((m1.end.x != m2.end.x) || (m1.end.y != m2.end.y)) {
      // Easy case: two independant moves
      smove.appear.push(m1.appear[0]);
      smove.appear.push(m2.appear[0]);
      // "Captured" pieces may have moved:
      if (
        m1.vanish.length == 2 &&
        (
          m1.vanish[1].x != m2.start.x ||
          m1.vanish[1].y != m2.start.y
        )
      ) {
        smove.vanish.push(m1.vanish[1]);
      }
      if (
        m2.vanish.length == 2 &&
        (
          m2.vanish[1].x != m1.start.x ||
          m2.vanish[1].y != m1.start.y
        )
      ) {
        smove.vanish.push(m2.vanish[1]);
      }
    } else {
      // Collision: priority to the anticipated capture, if any.
      // If ex-aequo: knight wins (higher risk), or both disappears.
      // Then, priority to the knight vs pawn: remains.
      // Finally: both disappears.
      let remain = null;
      const p1 = m1.vanish[0].p;
      const p2 = m2.vanish[0].p;
      if (!!m1.end.illegal && !m2.end.illegal) remain = { c: 'w', p: p1 };
      else if (!!m2.end.illegal && !m1.end.illegal) remain = { c: 'b', p: p2 };
      if (!remain) {
        // Either both are illegal or both are legal
        if (p1 == V.KNIGHT && p2 == V.PAWN) remain = { c: 'w', p: p1 };
        else if (p2 == V.KNIGHT && p1 == V.PAWN) remain = { c: 'b', p: p2 };
        // If remain is still null: same type same risk, both disappear
      }
      if (!!remain) {
        smove.appear.push({
          x: m1.end.x,
          y: m1.end.y,
          p: remain.p,
          c: remain.c
        });
      }
    }
    return smove;
  }
// White and black (partial) moves were played: merge
White and black (partial) moves were played: merge
White and black ( partial ) moves were played : merge
resolveSynchroneMove(move) {
    let m1 = this.whiteMove;
    let m2 = move;
    const movingLikeCapture = (m) => {
      const shift = (m.vanish[0].c == 'w' ? -1 : 1);
      return (
        m.start.x + shift == m.end.x &&
        Math.abs(m.end.y - m.start.y) == 1
      );
    };
    const isPossible = (m, other) => {
      return (
        (
          m.vanish[0].p == V.KNIGHT &&
          (
            m.vanish.length == 1 ||
            m.vanish[1].c != m.vanish[0].c ||
            (
              !other.end.illegal &&
              other.end.x == m.end.x &&
              other.end.y == m.end.y
            )
          )
        )
        ||
        (
          m.vanish[0].p == V.PAWN &&
          !other.end.illegal &&
          (
            (
              m.end.x == (m.vanish[0].c == "w" ? 0 : V.size.x - 1) &&
              other.vanish.length == 2 &&
              other.vanish[1].p == V.KNIGHT &&
              other.vanish[1].c == m.vanish[0].c
            )
            ||
            (
              !movingLikeCapture(m) &&
              other.start.x == m.end.x &&
              other.start.y == m.end.y
            )
            ||
            (
              movingLikeCapture(m) &&
              other.end.x == m.end.x &&
              other.end.y == m.end.y
            )
          )
        )
      );
    };
    if (!!m1.end.illegal && !isPossible(m1, m2)) {
      this.penaltyFlags[m1.vanish[0].c]++;
      m1.isNull = true;
    }
    if (!!m2.end.illegal && !isPossible(m2, m1)) {
      this.penaltyFlags[m2.vanish[0].c]++;
      m2.isNull = true;
    }
    if (!!m1.isNull) m1 = null;
    if (!!m2.isNull) m2 = null;
    if (!m1 && !!m2) return m2;
    if (!m2 && !!m1) return m1;
    let smove = {
      appear: [],
      vanish: []
    };
    if (!m1 && !m2) return smove;
    smove.vanish.push(m1.vanish[0]);
    smove.vanish.push(m2.vanish[0]);
    if ((m1.end.x != m2.end.x) || (m1.end.y != m2.end.y)) {
      smove.appear.push(m1.appear[0]);
      smove.appear.push(m2.appear[0]);
      if (
        m1.vanish.length == 2 &&
        (
          m1.vanish[1].x != m2.start.x ||
          m1.vanish[1].y != m2.start.y
        )
      ) {
        smove.vanish.push(m1.vanish[1]);
      }
      if (
        m2.vanish.length == 2 &&
        (
          m2.vanish[1].x != m1.start.x ||
          m2.vanish[1].y != m1.start.y
        )
      ) {
        smove.vanish.push(m2.vanish[1]);
      }
    } else {
      let remain = null;
      const p1 = m1.vanish[0].p;
      const p2 = m2.vanish[0].p;
      if (!!m1.end.illegal && !m2.end.illegal) remain = { c: 'w', p: p1 };
      else if (!!m2.end.illegal && !m1.end.illegal) remain = { c: 'b', p: p2 };
      if (!remain) {
        if (p1 == V.KNIGHT && p2 == V.PAWN) remain = { c: 'w', p: p1 };
        else if (p2 == V.KNIGHT && p1 == V.PAWN) remain = { c: 'b', p: p2 };
      }
      if (!!remain) {
        smove.appear.push({
          x: m1.end.x,
          y: m1.end.y,
          p: remain.p,
          c: remain.c
        });
      }
    }
    return smove;
  }
resolveSynchroneMove ( move ) { let m1 = this . whiteMove ; let m2 = move ; const movingLikeCapture = ( m ) => { const shift = ( m . vanish [ 0 ] . c == 'w' ? - 1 : 1 ) ; return ( m . start . x + shift == m . end . x && Math . abs ( m . end . y - m . start . y ) == 1 ) ; } ; const isPossible = ( m , other ) => { return ( ( m . vanish [ 0 ] . p == V . KNIGHT && ( m . vanish . length == 1 || m . vanish [ 1 ] . c != m . vanish [ 0 ] . c || ( ! other . end . illegal && other . end . x == m . end . x && other . end . y == m . end . y ) ) ) || ( m . vanish [ 0 ] . p == V . PAWN && ! other . end . illegal && ( ( m . end . x == ( m . vanish [ 0 ] . c == "w" ? 0 : V . size . x - 1 ) && other . vanish . length == 2 && other . vanish [ 1 ] . p == V . KNIGHT && other . vanish [ 1 ] . c == m . vanish [ 0 ] . c ) || ( ! movingLikeCapture ( m ) && other . start . x == m . end . x && other . start . y == m . end . y ) || ( movingLikeCapture ( m ) && other . end . x == m . end . x && other . end . y == m . end . y ) ) ) ) ; } ; if ( ! ! m1 . end . illegal && ! isPossible ( m1 , m2 ) ) { this . penaltyFlags [ m1 . vanish [ 0 ] . c ] ++ ; m1 . isNull = true ; } if ( ! ! m2 . end . illegal && ! isPossible ( m2 , m1 ) ) { this . penaltyFlags [ m2 . vanish [ 0 ] . c ] ++ ; m2 . isNull = true ; } if ( ! ! m1 . isNull ) m1 = null ; if ( ! ! m2 . isNull ) m2 = null ; if ( ! m1 && ! ! m2 ) return m2 ; if ( ! m2 && ! ! m1 ) return m1 ; let smove = { appear : [ ] , vanish : [ ] } ; if ( ! m1 && ! m2 ) return smove ; smove . vanish . push ( m1 . vanish [ 0 ] ) ; smove . vanish . push ( m2 . vanish [ 0 ] ) ; if ( ( m1 . end . x != m2 . end . x ) || ( m1 . end . y != m2 . end . y ) ) { smove . appear . push ( m1 . appear [ 0 ] ) ; smove . appear . push ( m2 . appear [ 0 ] ) ; if ( m1 . vanish . length == 2 && ( m1 . vanish [ 1 ] . x != m2 . start . x || m1 . vanish [ 1 ] . y != m2 . start . y ) ) { smove . vanish . push ( m1 . vanish [ 1 ] ) ; } if ( m2 . vanish . length == 2 && ( m2 . vanish [ 1 ] . x != m1 . start . x || m2 . vanish [ 1 ] . y != m1 . start . y ) ) { smove . vanish . push ( m2 . vanish [ 1 ] ) ; } } else { let remain = null ; const p1 = m1 . vanish [ 0 ] . p ; const p2 = m2 . vanish [ 0 ] . p ; if ( ! ! m1 . end . illegal && ! m2 . end . illegal ) remain = { c : 'w' , p : p1 } ; else if ( ! ! m2 . end . illegal && ! m1 . end . illegal ) remain = { c : 'b' , p : p2 } ; if ( ! remain ) { if ( p1 == V . KNIGHT && p2 == V . PAWN ) remain = { c : 'w' , p : p1 } ; else if ( p2 == V . KNIGHT && p1 == V . PAWN ) remain = { c : 'b' , p : p2 } ; } if ( ! ! remain ) { smove . appear . push ( { x : m1 . end . x , y : m1 . end . y , p : remain . p , c : remain . c } ) ; } } return smove ; }
White and black (partial) moves were played: merge
White and black ( partial ) moves were played : merge
// Self-capture attempt // Promotion attempt // Moving attempt // Capture attempt // Either an anticipated capture of something which didn't move // (or not to the right square), or a push through blocus. // ==> Just discard the move, and add a penalty point // If one move is illegal, just execute the other // For PlayOnBoard (no need for start / end, irrelevant) // Both moves are now legal or at least possible: // Easy case: two independant moves // "Captured" pieces may have moved: // Collision: priority to the anticipated capture, if any. // If ex-aequo: knight wins (higher risk), or both disappears. // Then, priority to the knight vs pawn: remains. // Finally: both disappears. // Either both are illegal or both are legal // If remain is still null: same type same risk, both disappear