return this.getColor(x1, y1) !== this.getColor(x2, y2);
   }
 
-  // TODO: currently unused, but makes sense?
+  // Teleport & Recycle. Assumption: color(x1,y1) == color(x2,y2)
   canSelfTake([x1, y1], [x2, y2]) {
-    return true;
+    return !this.isKing(x2, y2);
   }
 
   canStepOver(i, j, p) {
   }
 
   // All possible moves from selected square
+  // TODO: generalize usage if arg "color" (e.g. Checkered)
   getPotentialMovesFrom([x, y], color) {
     if (this.subTurnTeleport == 2)
       return [];
           segments: this.options["cylinder"],
           stepSpec: stepSpec
         },
-        ([i1, j1], [i2, j2]) =>
-          this.getColor(i2, j2) == color && !this.isKing(i2, j2)
+        ([i1, j1], [i2, j2]) => {
+          return (
+            this.getColor(i2, j2) == color &&
+            this.canSelfTake([i1, j1], [i2, j2])
+          );
+        }
       );
       Array.prototype.push.apply(squares, selfCaptures);
     }
     return res;
   }
 
+  // cb: callback returning a boolean (false if king missing)
+  trackKingWrap(move, kingPos, cb) {
+    let newKingPP = null,
+        sqIdx = 0,
+        res = true; //a priori valid
+    const oldKingPP =
+      move.vanish.find(v => this.isKing(0, 0, v.p) && v.c == color);
+    if (oldKingPP) {
+      // Search king in appear array:
+      newKingPP =
+        move.appear.find(a => this.isKing(0, 0, a.p) && a.c == color);
+      if (newKingPP) {
+        sqIdx = kingPos.findIndex(kp =>
+          kp[0] == oldKingPP.x && kp[1] == oldKingPP.y);
+        kingPos[sqIdx] = [newKingPP.x, newKingPP.y];
+      }
+      else
+        res = false; //king vanished
+    }
+    res &&= cb(kingPos);
+    if (oldKingPP && newKingPP)
+      kingPos[sqIdx] = [oldKingPP.x, oldKingPP.y];
+    return res;
+  }
+
   // 'color' arg because some variants (e.g. Refusal) check opponent moves
   filterValid(moves, color) {
     if (!color)
     let kingPos = this.searchKingPos(color);
     return moves.filter(m => {
       this.playOnBoard(m);
-      let newKingPP = null,
-          sqIdx = 0,
-          res = true; //a priori valid
-      const oldKingPP =
-        m.vanish.find(v => this.isKing(0, 0, v.p) && v.c == color);
-      if (oldKingPP) {
-        // Search king in appear array:
-        newKingPP =
-          m.appear.find(a => this.isKing(0, 0, a.p) && a.c == color);
-        if (newKingPP) {
-          sqIdx = kingPos.findIndex(kp =>
-            kp[0] == oldKingPP.x && kp[1] == oldKingPP.y);
-          kingPos[sqIdx] = [newKingPP.x, newKingPP.y];
-        }
-        else
-          res = false; //king vanished
-      }
-      res &&= !this.underCheck(kingPos, oppCols);
-      if (oldKingPP && newKingPP)
-        kingPos[sqIdx] = [oldKingPP.x, oldKingPP.y];
+      const res = this.trackKingWrap(m, kingPos, (kp) => {
+        return !this.underCheck(kp, oppCols);
+      });
       this.undoOnBoard(m);
       return res;
     });
         if (this.board[i][j] != "" && this.getColor(i, j) == color) {
           // NOTE: in fact searching for all potential moves from i,j.
           //       I don't believe this is an issue, for now at least.
-          const moves = this.getPotentialMovesFrom([i, j]);
+          const moves = this.getPotentialMovesFrom([i, j], color);
           if (moves.some(m => this.filterValid([m]).length >= 1))
             return true;
         }
 
       }
     },
     "node_modules/anymatch": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
-      "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+      "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
       "dev": true,
       "dependencies": {
         "normalize-path": "^3.0.0",
   },
   "dependencies": {
     "anymatch": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
-      "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+      "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
       "dev": true,
       "requires": {
         "normalize-path": "^3.0.0",
 
   {name: 'Capture', desc: 'Mandatory captures'},
   {name: 'Chakart', desc: 'Capture the princess'},
   {name: 'Checkered', desc: 'Shared pieces'},
-//  {name: 'Checkless', desc: 'No-check mode'},
+  {name: 'Checkless', desc: 'No-check mode'},
   {name: 'Chess960', disp: "Chess 960", desc: "Standard rules"},
 //  {name: 'Circular', desc: 'Run forward'},
 //  {name: 'Clorange', desc: 'A Clockwork Orange', disp: 'Clockwork Orange'},
 
--- /dev/null
+import ChessRules from "/base_rules.js";
+
+export default class ChecklessRules extends ChessRules {
+
+  // Cannot use super.atLeastOneMove: lead to infinite recursion
+  atLeastOneMove_aux(kingPos, oppKingPos, color, oppCol) {
+    for (let i = 0; i < this.size.x; i++) {
+      for (let j = 0; j < this.size.y; j++) {
+        if (this.getColor(i, j) == color) {
+          const moves = this.getPotentialMovesFrom([i, j]);
+          for (let m of moves) {
+            this.playOnBoard(m);
+            const res = this.trackKingWrap(m, kingPos, (kp) => {
+              return !this.underCheck(kp, [oppCol]);
+            });
+            this.undoOnBoard(m);
+            if (res)
+              return true;
+          }
+        }
+      }
+    }
+    return false;
+  }
+
+  filterValid(moves) {
+    fmoves = super.filterValid(moves);
+    // Filter out moves giving check but not checkmate
+    const color = this.turn;
+    const oppCol = C.GetOppTurn(color);
+    let kingPos = this.searchKingPos(color),
+        oppKingPos = this.searchKingPos(oppCol);
+    return fmoves.filter(m => {
+      this.playOnBoard(m);
+      const res = this.trackKingWrap(m, oppKingPos, (oppKp) => {
+        return (
+          !this.underCheck(oppKp, [color]) ||
+          this.atLeastOneMove_aux(oppKp, kingPos, oppCol, color)
+        );
+      });
+      this.undoOnBoard(m);
+      return res;
+    });
+  }
+
+};