Check out Grant Acedrex, our featured variant for April, 2024.


[ Help | Earliest Comments | Latest Comments ]
[ List All Subjects of Discussion | Create New Subject of Discussion ]
[ List Earliest Comments Only For Pages | Games | Rated Pages | Rated Games | Subjects of Discussion ]

Single Comment

Interactive diagrams. Diagrams that interactively show piece moves.[All Comments] [Add Comment or Rating]
💡📝H. G. Muller wrote on Sun, Feb 5, 2023 11:37 AM UTC:

An Interactive Diagram using the new scripting interface is now available in the alternative script betzaNew.js. (Which, after sufficient testing, will replace the current betza.js). As a test case I used it to create the Ultima Diagram in the previous posting.

Ultima required a fair amount of scripting. Only the Long Leaper and the Withdrawer can be done purely with XBetza. The Immobilizer can be done with the aid of the new trackPieces=N and curse=freeze parameters. Pinching is a kind of burning, but since it is dependent on the burn victim being sandwiched, this has to be tested in a script before a 'selective burn' promotion can be issued. Coordinator capture needs to be performed entirely by the custom script (using the coordinates of the tracked King), by adding locust squares to Coordinator moves.

The Chameleon is of course a disaster; it needs to be able to do what all other pieces do, but in a type-selective way. Only the replacement capture can be implemented in XBetza, as kK, because the King happens to be royal. The other captures that can be described with XBetza need to be vetted for whether they capture the correct victim. I only did that for Long Leapers, and add capture of a Withdrawer as an extra locust square 'by hand'. The standard script now supplies the routine AddVictim(move, file, rank, mask, target) to facilitate that; the mask and target arguments are optional, and when omitted the square (file, rank) is only added if it contains an enemy piece. By setting mask = 0x4FF you can test for a specific colored piece type (the test is (board[rank][file] & mask) == target), and other pieces would not be affected.

The curse=freeze option would only freeze enemy neighbors of an Immobilizer, not the Immoblizer itself (of course). But if a Chameleon is frozen, it reciprocates the favor. So the custom script has to test the board for adjacent enemy Chameleons, and mark the Immobilizer square as freezing too when any are found. Al in all this gave me the following custom script:

  var myNodes = 1e8;
  function ultimaTinker(m) {
    var s, p, v, k, x, y, xx, yy, type = m[-6] & 511, col = m[-6] & 1024; // mover and its color
    if(nodes != myNodes) { // new node; update burn map first
      myNodes = nodes;
      var q = loc[col+6], x = q & 7, y = q >> 7;       // friendly immobilizer?
      if(q >= 0 && (board[y][x] & 0x4FF) == col + 6) { // yes!
        var l = (x ? x-1 : 0), r = (x == 7 ? 7 : x+1); // left and right boundary of surrounding
        for(var i=l; i<=r; i++) { // detect enemy chameleons next to it
          if((board[y][i] & 0x4FF) == 1029 - col) neighbor[q] = nodes;            // on same rank
          if(y && (board[y-1][i] & 0x4FF) == 1029 - col) neighbor[q] = nodes;     // on next-lower rank
          if(y < 7 && (board[y+1][i] & 0x4FF) == 1029 - col) neighbor[q] = nodes; // on next-higher rank
        }
      }
      if(neighbor[q] == nodes && type == 6) { freeze = 100; return 1; } // the current move should have been frozen
   }
   if(type == 1) {        // pincher moved
     var p = 0, x = m[2], y = m[3], xcol = 1024 - col; // destination and enemy color
     if(x > 1 && (board[y][x-1] - 1 & 0xC00) == xcol && (board[y][x-2] - 1 & 0xC00) == col) p |= 0x40; // W
     if(x < 6 && (board[y][x+1] - 1 & 0xC00) == xcol && (board[y][x+2] - 1 & 0xC00) == col) p |= 0x04; // E
     if(y > 1 && (board[y-1][x] - 1 & 0xC00) == xcol && (board[y-2][x] - 1 & 0xC00) == col) p |= 0x10; // S
     if(y < 6 && (board[y+1][x] - 1 & 0xC00) == xcol && (board[y+2][x] - 1 & 0xC00) == col) p |= 0x01; // N
     if(p) m[-1] = p | 512; // request a selective burn through the promotion code
   } else if(type == 5) { // chameleon moved
     // long-leaper victims
     for(k=2; k<m[-2]; k++) { // at this stage all locust squares are long-leap captures
       v = board[m[2*k+1]][m[2*k]];
       if((v & 0x4FF) != 1027 - col) return 1; // victim not long leaper; reject this move
     }
     // withdrawer victims
     x = m[0] - m[2]; y = m[1] - m[3];         // calculate unit step (should really be table lookup...)
     k = (x ? x : y); if(k < 0) k = -k;
     xx = x/k + m[0]; yy = y/k + m[1];
     if(!((xx | yy) & 8)) AddVictim(m, xx, yy, 0x4FF, 1026 - col); // add withdrawer victim if on board
     // pincher victims
     if(!(x & y)) { // only on orthogonal moves
       p = 0, x = m[2], y = m[3], xcol = 1024 - col;
       if(x > 1 && (board[y][x-1] - 1 & 0x4FF) == xcol && (board[y][x-2] - 1 & 0xC00) == col) p |= 0x40; // W
       if(x < 6 && (board[y][x+1] - 1 & 0x4FF) == xcol && (board[y][x+2] - 1 & 0xC00) == col) p |= 0x04; // E
       if(y > 1 && (board[y-1][x] - 1 & 0x4FF) == xcol && (board[y-2][x] - 1 & 0xC00) == col) p |= 0x10; // S
       if(y < 6 && (board[y+1][x] - 1 & 0x4FF) == xcol && (board[y+2][x] - 1 & 0xC00) == col) p |= 0x01; // N
       if(p) m[-1] = p | 512; // request a selective burn through the promotion code
     }
     // coordinator victims
     k = loc[col + 7];  // king location (128*rank + file)
     AddVictim(m, k & 7, m[3], 0x4FF, 1028-col); // add locust square for coordinated coordinator
     AddVictim(m, m[2], k >> 7, 0x4FF, 1028-col);
   } else if(type == 4) { // coordinator moved
     k = loc[col + 7];  // king location
     AddVictim(m, k & 7, m[3]);  // add locust squares for coordinated enemies
     AddVictim(m, m[2], k >> 7);
   }
   return 0;
  }

Issues in the interface that could still be improved:

  • The standard script tests for freezing / burning before the custom script is consulted. So when the custom script adds a freezing or burning square, like it does here when the freezing is 'reflected' by a Chameleon, the move that consulted the script would have already passed the test if it happened to be the first move generated. The Ultima script above has to explicitly test for that.
  • The standard script now automatically marks the 'blast zone' around the tracked piece (controlled through trackPieces=N). But it now always uses K steps for that. It might be useful to make this user-configurable, through an option blastZone, so that the default burning could take place only on W or only on F squares, or perhaps even on N squares.
  • It could be useful to define the meaning of the bits in the promotion code for a selective burn (and the marking of the blast zone) relative to the player. That would allow asymmetric burning (e.g. only forward) to work the same for black and white.
  • Perhaps it should be possible to specify 'shooters' expicitly. Entering the Withdrawer and Coordinator moves in the Ultima Diagram below feels a bit queer, as you have to specify the victim first. This will always happen with locust victims added by the script; you will have to click those in the reverse order from which they were added. The Long Leaper obviously is a 'trampler', so it is less strange there (and the locust squares were generated from the XBetza move). But since all these capture are all implicit side effects, it is silly they would have to be clicked at all. Perhaps there should be a third class of pieces ('burners'), that can only be defined by the user, which would then autocomplete after origin and destination are clicked.