1 import { ChessRules
} from "@/base_rules";
2 import { randInt
} from "@/utils/alea";
4 // TODO: Two moves, both promoting the same pawn, but to a different type of piece, count as two different moves.
5 // ==> need to accept temporarily pawn promotions even if on forbidden square, and check afterward if promoted type changed (info in lastMove)
7 export class RefusalRules
extends ChessRules
{
9 static IsGoodFen(fen
) {
10 if (!ChessRules
.IsGoodFen(fen
)) return false;
11 if (!V
.ParseFen(fen
).lastMove
) return false;
15 static ParseFen(fen
) {
17 { lastMove: fen
.split(" ")[5] },
18 ChessRules
.ParseFen(fen
)
23 const L
= this.lastMove
.length
;
24 const lm
= this.lastMove
[L
-1];
25 return super.getFen() + " " + JSON
.stringify(lm
);
28 // NOTE: with this variant's special rule,
29 // some extra repetitions could be detected... TODO (...)
31 static GenRandInitFen(randomness
) {
32 return ChessRules
.GenRandInitFen(randomness
) + " null";
35 setOtherVariables(fen
) {
36 super.setOtherVariables(fen
);
37 this.lastMove
= [JSON
.parse(V
.ParseFen(fen
).lastMove
)]; //may be null
40 canIplay(side
, [x
, y
]) {
41 if (super.canIplay(side
, [x
, y
])) return true;
42 if (this.turn
!= side
) return false;
43 // Check if playing last move, reversed:
44 const L
= this.lastMove
.length
;
45 const lm
= this.lastMove
[L
-1];
46 return (!!lm
&& !lm
.noRef
&& x
== lm
.end
.x
&& y
== lm
.end
.y
);
49 getPotentialMovesFrom([x
, y
]) {
50 if (this.getColor(x
, y
) != this.turn
) {
51 const L
= this.lastMove
.length
;
52 const lm
= this.lastMove
[L
-1];
53 if (!!lm
&& !lm
.noRef
&& x
== lm
.end
.x
&& y
== lm
.end
.y
) {
54 let revLm
= JSON
.parse(JSON
.stringify(lm
));
55 let tmp
= revLm
.appear
;
56 revLm
.appear
= revLm
.vanish
;
59 revLm
.start
= revLm
.end
;
65 return super.getPotentialMovesFrom([x
, y
]);
68 // NOTE: do not take refusal move into account here (two own moves)
71 const color
= this.turn
;
72 for (let i
= 0; i
< V
.size
.x
; i
++) {
73 for (let j
= 0; j
< V
.size
.y
; j
++) {
74 if (this.board
[i
][j
] != V
.EMPTY
&& this.getColor(i
, j
) == color
) {
75 const moves
= this.getPotentialMovesFrom([i
, j
]);
76 for (let m
of moves
) {
77 if (m
.vanish
[0].c
== color
&& this.filterValid([m
]).length
> 0) {
79 if (movesCounter
>= 2) return true;
89 if (moves
.length
== 0) return [];
90 const color
= this.turn
;
91 const L
= this.lastMove
.length
;
92 const lm
= this.lastMove
[L
-1];
93 return moves
.filter(m
=> {
95 !!lm
&& !!lm
.refusal
&&
96 m
.start
.x
== lm
.end
.x
&& m
.start
.y
== lm
.end
.y
&&
97 m
.end
.x
== lm
.start
.x
&& m
.end
.y
== lm
.start
.y
101 // NOTE: not using this.play()/undo() ==> infinite loop
102 V
.PlayOnBoard(this.board
, m
);
103 if (m
.appear
[0].p
== V
.KING
)
104 this.kingPos
[m
.appear
[0].c
] = [m
.appear
[0].x
, m
.appear
[0].y
];
105 const res
= !this.underCheck(color
);
106 V
.UndoOnBoard(this.board
, m
);
107 if (m
.vanish
[0].p
== V
.KING
)
108 this.kingPos
[m
.vanish
[0].c
] = [m
.vanish
[0].x
, m
.vanish
[0].y
];
114 const L
= this.lastMove
.length
;
115 const lm
= this.lastMove
[L
-1];
117 // My previous move was already refused?
118 (!!lm
&& this.getColor(lm
.end
.x
, lm
.end
.y
) == this.turn
) ||
119 // I've only one move available?
120 !this.atLeastTwoMoves()
124 // NOTE: refusal could be recomputed, but, it's easier like this
125 if (move.vanish
[0].c
!= this.turn
) move.refusal
= true;
129 if (!move.refusal
) return super.getEpSquare(move);
130 return null; //move refusal
134 if (!move.refusal
) super.postPlay(move);
136 const L
= this.lastMove
.length
;
137 const lm
= this.lastMove
[L
-1];
138 this.disaggregateFlags(JSON
.parse(lm
.flags
));
140 // NOTE: explicitely give fields, because some are assigned in BaseGame
148 if (!!move.noRef
) mvInLm
.noRef
= true;
149 if (!!move.refusal
) mvInLm
.refusal
= true;
150 this.lastMove
.push(mvInLm
);
154 if (!move.refusal
) super.postUndo(move);
158 getAllPotentialMoves() {
159 const color
= this.turn
;
160 const L
= this.lastMove
.length
;
161 const lm
= this.lastMove
[L
-1];
162 let potentialMoves
= [];
163 for (let i
= 0; i
< V
.size
.x
; i
++) {
164 for (let j
= 0; j
< V
.size
.y
; j
++) {
166 this.board
[i
][j
] != V
.EMPTY
&&
168 this.getColor(i
, j
) == color
||
170 (!!lm
&& lm
.end
.x
== i
&& lm
.end
.y
== j
)
173 Array
.prototype.push
.apply(
175 this.getPotentialMovesFrom([i
, j
])
180 return potentialMoves
;
184 // Just play at random for now... (TODO?)
185 // Refuse last move with odds 1/3.
186 const moves
= this.getAllValidMoves();
187 const refusal
= moves
.find(m
=> m
.vanish
[0].c
!= this.turn
);
189 if (Math
.random() <= 0.33) return refusal
;
190 const others
= moves
.filter(m
=> m
.vanish
[0].c
== this.turn
);
191 return others
[randInt(others
.length
)];
193 else return moves
[randInt(moves
.length
)];
197 if (move.vanish
[0].c
!= this.turn
) return "Refuse";
198 return super.getNotation(move);