jocly/src/games/chessbase/leychessalpha-model.js

575 lines
17 KiB
JavaScript

/*
* Copyright(c) 2013-2017 - jocly.com
*
* You are allowed to use and modify this source code as long as it is exclusively for use in the Jocly API.
*
* Original authors: Jocly team
*
*/
(function() {
var geometry = Model.Game.cbBoardGeometryGrid(12,12);
Model.Game.cbDefine = function() {
var $this = this;
/*
* Movement/capture graph for the prince
*/
function PrinceGraph(side) {
var graph={};
for(var pos=0;pos<geometry.boardSize;pos++) {
graph[pos]=[];
var forward=[]; // hold the pos line in front of the piece
var pos1=geometry.Graph(pos,[0,side]);
if(pos1!=null) {
forward.push(pos1 | $this.cbConstants.FLAG_MOVE | $this.cbConstants.FLAG_CAPTURE); // capture and move allowed at first forward position
pos1=geometry.Graph(pos1,[0,side]);
if(pos1!=null)
forward.push(pos1 | $this.cbConstants.FLAG_MOVE); // move to second forward only, no capture
graph[pos].push($this.cbTypedArray(forward));
}
}
return $this.cbMergeGraphs(geometry,
$this.cbShortRangeGraph(geometry,[[-1,-1],[-1,1],[-1,0],[1,0],[1,-1],[1,1],[0,-side]]), // direction other than forward
graph // forward direction
);
}
/*
* Movement/capture graph for the eagle
*/
function EagleGraph() {
var flags = $this.cbConstants.FLAG_MOVE | $this.cbConstants.FLAG_CAPTURE;
var graph={};
for(var pos=0;pos<geometry.boardSize;pos++) {
graph[pos]=[];
[[-1,-1],[-1,1],[1,-1],[1,1]].forEach(function(delta) { // loop on all 4 diagonals
var pos1=geometry.Graph(pos,delta);
if(pos1!=null) {
for(var dir=0;dir<2;dir++) { // dir=0 for row, dir=1 for column
var away=[] // hold the sliding line
for(var n=1;n<11;n++) { // board is 12 cells long, so only consider max 11 cell displacements
var delta2=[];
delta2[dir]=delta[dir]*n;
delta2[1-dir]=0; // delta2 is now only about moving orthogonally, away from the piece
var pos2=geometry.Graph(pos1,delta2);
if(pos2!=null) {
if(n==1) // possible to slide at least 1 cell, make sure the diagonal cell is not occupied, but cannot move to this cell
away.push(pos1 | $this.cbConstants.FLAG_STOP);
away.push(pos2 | flags);
}
}
if(away.length>0)
graph[pos].push($this.cbTypedArray(away));
}
}
});
}
return $this.cbMergeGraphs(geometry,
$this.cbShortRangeGraph(geometry,[[-1,-1],[-1,1],[1,-1],[1,1]]),
graph
);
}
return {
geometry: geometry,
pieceTypes: {
0: {
name: 'pawn-w',
aspect: 'fr-pawn',
graph: this.cbPawnGraph(geometry,1),
value: 1,
abbrev: '',
fenAbbrev: 'P',
epCatch: false,
},
1: {
name: 'ipawn-w',
aspect: 'fr-pawn',
graph: this.cbInitialPawnGraph(geometry,1),
value: 1,
abbrev: '',
fenAbbrev: 'P',
initial: [{s:1,p:24},{s:1,p:25},{s:1,p:26},{s:1,p:27},{s:1,p:28},{s:1,p:29},{s:1,p:30},{s:1,p:31},{s:1,p:32},{s:1,p:33},{s:1,p:34},{s:1,p:35}],
epTarget: true,
epCatch: false,
},
2: {
name: 'pawn-b',
aspect: 'fr-pawn',
graph: this.cbPawnGraph(geometry,-1),
value: 1,
abbrev: '',
fenAbbrev: 'P',
epCatch: false,
},
3: {
name: 'ipawn-b',
aspect: 'fr-pawn',
graph: this.cbInitialPawnGraph(geometry,-1),
value: 1,
abbrev: '',
fenAbbrev: 'P',
initial: [{s:-1,p:108},{s:-1,p:109},{s:-1,p:110},{s:-1,p:111},{s:-1,p:112},{s:-1,p:113},{s:-1,p:114},{s:-1,p:115},{s:-1,p:116},{s:-1,p:117},{s:-1,p:118},{s:-1,p:119}],
epTarget: true,
epCatch: false,
},
4: {
name: 'knight',
aspect: 'fr-knight',
graph: this.cbKnightGraph(geometry),
value: 2.9,
abbrev: 'N',
initial: [{s:1,p:14},{s:1,p:21},{s:-1,p:122},{s:-1,p:129}],
},
5: {
name: 'bishop',
aspect: 'fr-bishop',
graph: this.cbBishopGraph(geometry),
value: 3.1,
abbrev: 'B',
initial: [{s:1,p:15},{s:1,p:20},{s:-1,p:123},{s:-1,p:128}],
},
6: {
name: 'rook',
aspect: 'fr-rook',
graph: this.cbRookGraph(geometry),
value: 5,
abbrev: 'R',
initial: [{s:1,p:13},{s:1,p:22},{s:-1,p:121},{s:-1,p:130}],
castle: true,
},
7: {
name: 'queen',
aspect: 'fr-queen',
graph: this.cbQueenGraph(geometry),
value: 9,
abbrev: 'Q',
initial: [{s:1,p:18},{s:-1,p:126}],
},
8: {
name: 'king',
aspect: 'fr-king',
isKing: true,
graph: this.cbKingGraph(geometry),
abbrev: 'K',
initial: [{s:1,p:17},{s:-1,p:125}],
},
9: {
name: 'cannon',
aspect: 'fr-cannon2',
graph: this.cbXQCannonGraph(geometry),
value: 3.5,
abbrev: 'C',
initial: [{s:1,p:0},{s:1,p:11},{s:-1,p:132},{s:-1,p:143}],
},
10: {
name: 'elephant',
aspect: 'fr-elephant',
graph: this.cbShortRangeGraph(geometry,[[-1,-1],[-1,1],[1,-1],[1,1],[-2,-2],[-2,2],[2,-2],[2,2]]),
value: 2.5,
abbrev: 'E',
initial: [{s:1,p:12},{s:1,p:23},{s:-1,p:120},{s:-1,p:131}],
},
11: {
name: 'prince-w',
aspect: 'fr-admiral',
graph: PrinceGraph(1),
value: 3,
epTarget: true,
abbrev: 'I',
initial: [{s:1,p:16},{s:1,p:19}],
},
12: {
name: 'prince-b',
aspect: 'fr-admiral',
graph: PrinceGraph(-1),
epTarget: true,
value: 3,
abbrev: 'I',
initial: [{s:-1,p:124},{s:-1,p:127}],
},
13: {
name: 'camel',
aspect: 'fr-camel',
graph: this.cbShortRangeGraph(geometry,[[-3,-1],[-3,1],[3,-1],[3,1],[1,3],[1,-3],[-1,3],[-1,-3]]),
value: 2,
abbrev: 'M',
initial: [{s:1,p:1},{s:1,p:10},{s:-1,p:133},{s:-1,p:142}],
},
14: {
name: 'lion',
aspect: 'fr-lion',
graph: this.cbShortRangeGraph(geometry,[
[-1,-1],[-1,1],[1,-1],[1,1],[1,0],[0,1],[-1,0],[0,-1],
[-2,0],[-2,-1],[-2,-2],[-1,-2],[0,-2],
[1,-2],[2,-2],[2,-1],[2,0],[2,1],
[2,2],[1,2],[0,2],[-1,2],[-2,2],[-2,1]]),
value: 7.5,
abbrev: 'L',
initial: [{s:1,p:5},{s:-1,p:137}],
},
15: {
name: 'eagle',
aspect: 'fr-eagle',
graph: EagleGraph(),
value: 8,
abbrev: 'A',
initial: [{s:1,p:6},{s:-1,p:138}],
},
16: {
name: 'unicorn',
aspect: 'fr-unicorn',
graph: this.cbMergeGraphs(geometry,
this.cbShortRangeGraph(geometry,[
[-2,-2],[-2,0],[-2,2],[0,2],[2,2],[2,0],[2,-2],[0,-2],
[-3,-3],[-3,0],[-3,3],[0,3],[3,3],[3,0],[3,-3],[0,-3]]),
this.cbKingGraph(geometry),
this.cbKnightGraph(geometry),
this.cbLongRangeGraph(geometry,[[2,-1],[2,1],[-2,-1],[-2,1],[-1,2],[-1,-2],[1,2],[1,-2]]),
this.cbShortRangeGraph(geometry,[[-3,-1],[-3,1],[3,-1],[3,1],[1,3],[1,-3],[-1,3],[-1,-3]])
),
value: 13,
abbrev: 'Pa',
initial: [{s:1,p:2},{s:-1,p:134}],
},
17: {
name: 'dragon',
aspect: 'fr-dragon',
graph: this.cbMergeGraphs(geometry,
this.cbQueenGraph(geometry),
EagleGraph()),
value: 14,
abbrev: 'D',
initial: [{s:1,p:4},{s:-1,p:136}],
},
18: {
name: 'lighthouse',
aspect: 'fr-lighthouse',
graph: this.cbMergeGraphs(geometry,
this.cbQueenGraph(geometry),
this.cbXQCannonGraph(geometry),
this.cbLongRangeGraph(geometry,[[1,-1],[-1,-1],[-1,1],[1,1]],null,this.cbConstants.FLAG_MOVE | this.cbConstants.FLAG_SCREEN_CAPTURE)),
value: 12,
abbrev: 'LH',
initial: [{s:1,p:3},{s:-1,p:135}],
},
19: {
name: 'emperor',
aspect: 'fr-crowned-rook',
graph: this.cbMergeGraphs(geometry,
this.cbQueenGraph(geometry),
this.cbXQCannonGraph(geometry),
this.cbLongRangeGraph(geometry,[[1,-1],[-1,-1],[-1,1],[1,1]],null,this.cbConstants.FLAG_MOVE | this.cbConstants.FLAG_SCREEN_CAPTURE),
EagleGraph(),
this.cbShortRangeGraph(geometry,[
[-2,-2],[-2,0],[-2,2],[0,2],[2,2],[2,0],[2,-2],[0,-2],
[-3,-3],[-3,0],[-3,3],[0,3],[3,3],[3,0],[3,-3],[0,-3]]),
this.cbKnightGraph(geometry),
this.cbLongRangeGraph(geometry,[[2,-1],[2,1],[-2,-1],[-2,1],[-1,2],[-1,-2],[1,2],[1,-2]]),
this.cbShortRangeGraph(geometry,[[-3,-1],[-3,1],[3,-1],[3,1],[1,3],[1,-3],[-1,3],[-1,-3]])),
value: 20,
abbrev: 'EM',
},
20: {
name: 'commander',
aspect: 'fr-amazon',
graph: this.cbMergeGraphs(geometry,
this.cbQueenGraph(geometry),
this.cbKnightGraph(geometry)),
value: 13,
abbrev: 'Co',
initial: [{s:1,p:7},{s:-1,p:139}],
},
21: {
name: 'cardinal',
aspect: 'fr-cardinal',
graph: this.cbMergeGraphs(geometry,
this.cbBishopGraph(geometry),
this.cbKnightGraph(geometry)),
value: 6,
abbrev: 'Ca',
initial: [{s:1,p:9},{s:-1,p:141}],
},
22: {
name: 'chancellor',
aspect: 'fr-marshall',
graph: this.cbMergeGraphs(geometry,
this.cbRookGraph(geometry),
this.cbKnightGraph(geometry)),
value: 9,
abbrev: 'Ch',
initial: [{s:1,p:8},{s:-1,p:140}],
castle: true,
},
},
promote: function(aGame,piece,move) {
if(piece.t==1 && geometry.R(move.t)==11)
return [14,15,20,16,17,18];
else if(piece.t==3 && geometry.R(move.t)==0)
return [14,15,20,16,17,18];
else if(piece.t==11 && geometry.R(move.t)==11)
return [19];
else if(piece.t==12 && geometry.R(move.t)==0)
return [19];
return [];
},
};
}
/*
* Model.Board.GenerateMoves:
* - handle setup phase
* - handle king special move: a kind of castle involving only the king
*/
var kingLongMoves={
"1": {
17: [ [15,16],[19,18],[41,29],[39,28],[43,30],[3,4,16],[27,16,28],[40,28,29],[42,29,30],[31,18,30],[7,18,6] ],
5: [ [3,4],[7,8],[29,17],[27,16],[31,18],[15,4,6],[28,16,17],[30,17,18],[19,6,18] ],
},
"-1": {
125: [ [127,126],[123,124],[101,113],[99,112],[103,114],[135,124,136],[111,112,124],[100,112,113],[102,113,114],[115,114,126],[139,126,138] ],
137: [ [139,138],[135,136],[113,125],[115,126],[111,124],[127,126,138],[114,125,126],[112,124,125],[123,124,136] ],
},
}
var SuperModelBoardGenerateMoves=Model.Board.GenerateMoves;
Model.Board.GenerateMoves = function(aGame) {
// first moves (white and black) are managed specifically to setup K,Q,E,L initial position
if(this.setupState===undefined) {
this.mMoves=[{}];
return;
}
if(this.setupState=="setup") {
this.mMoves=[];
for(var i=0;i<12;i++)
this.mMoves.push({setup:i});
return;
}
SuperModelBoardGenerateMoves.apply(this,arguments); // call regular GenerateMoves method
// now consider special 2 cases king moves
var kPiece=this.pieces[this.board[this.kings[this.mWho]]];
if(!kPiece.m && !this.check) {
var lMoves=kingLongMoves[this.mWho][kPiece.p];
for(var i=0;i<lMoves.length;i++) {
var lMove=lMoves[i];
if(this.board[lMove[0]]>=0)
continue;
var canMove=true;
var oppInCheck=false;
for(var j=0;j<lMove.length;j++) {
var pos=lMove[j];
var tmpOut=this.board[pos];
this.board[pos]=-1; // remove possible piece to prevent problems when quick-applying/unapplying
var undo=this.cbQuickApply(aGame,{
f: kPiece.p,
t: pos,
});
var inCheck=this.cbGetAttackers(aGame,pos,this.mWho,true).length>0;
if(!inCheck && j==0)
oppInCheck=this.cbGetAttackers(aGame,this.kings[-this.mWho],-this.mWho,true).length>0;
this.cbQuickUnapply(aGame,undo);
this.board[pos]=tmpOut;
this.cbIntegrity(aGame);
if(inCheck) {
canMove=false;
break;
}
}
if(canMove)
this.mMoves.push({
f: kPiece.p,
t: lMove[0],
c: null,
ck: oppInCheck,
a: 'K',
});
}
}
}
/*
* Model.Board.CopyFrom overriding to copy setupState property
*/
var SuperModelBoardCopyFrom = Model.Board.CopyFrom;
Model.Board.CopyFrom = function(aBoard) {
SuperModelBoardCopyFrom.apply(this,arguments);
this.setupState = aBoard.setupState;
}
/*
* Model.Board.Evaluate overriding: in setup phase, no evaluation
*/
var SuperModelBoardEvaluate = Model.Board.Evaluate;
Model.Board.Evaluate = function(aGame) {
if(this.setupState===undefined || this.setupState=="setup")
return;
SuperModelBoardEvaluate.apply(this,arguments);
}
/*
* Model.Board.ApplyMove overriding: setup phase and king special move
*/
var SuperModelBoardApplyMove=Model.Board.ApplyMove;
Model.Board.ApplyMove = function(aGame,move) {
if(this.setupState===undefined)
this.setupState="setup";
else if(this.setupState=="setup") {
var $this=this, piece;
// at this point, KQLE have arbitrary positions. remember those piece indexes so we can move them
var starting={
"1": { K: 17, Q: 18, L: 5, E: 6 },
"-1": { K: 125, Q: 126, L: 137, E: 138 },
}
var indexes={ "1": {}, "-1": {} };
["1","-1"].forEach(function(side) {
for(var p in starting[side])
indexes[side][p]=$this.board[starting[side][p]];
});
// remove KQLE from the board
[5,6,17,18,125,126,137,138].forEach(function(pos) {
var pIndex=$this.board[pos];
$this.board[pos]=-1;
piece = $this.pieces[pIndex];
piece.p=-1;
$this.zSign=aGame.zobrist.update($this.zSign,"board",piece.t,pos);
});
// setup KQLE positions according to the setup
var setup=move.setup;
var remaining={};
if(setup/6<1) {
this.board[17]=indexes[1].K;
piece = this.pieces[indexes[1].K];
piece.p=17;
$this.zSign=aGame.zobrist.update($this.zSign,"board",piece.t,17);
this.kings[1]=17;
remaining[1]=[18,5,6];
this.board[125]=indexes[-1].K;
piece = this.pieces[indexes[-1].K];
piece.p=125;
$this.zSign=aGame.zobrist.update($this.zSign,"board",piece.t,125);
this.kings[-1]=125;
remaining[-1]=[126,137,138];
} else {
this.board[5]=indexes[1].K;
piece = this.pieces[indexes[1].K];
piece.p=5;
$this.zSign=aGame.zobrist.update($this.zSign,"board",piece.t,5);
this.kings[1]=5;
remaining[1]=[17,18,6];
this.board[137]=indexes[-1].K;
piece = this.pieces[indexes[-1].K];
piece.p=137;
$this.zSign=aGame.zobrist.update($this.zSign,"board",piece.t,137);
this.kings[-1]=137;
remaining[-1]=[125,126,138];
}
setup%=6;
var queen=Math.floor(setup/2);
this.board[remaining[1][queen]]=indexes[1].Q;
piece = this.pieces[indexes[1].Q];
piece.p=remaining[1][queen];
$this.zSign=aGame.zobrist.update($this.zSign,"board",piece.t,remaining[1][queen]);
remaining[1].splice(queen,1);
this.board[remaining[-1][queen]]=indexes[-1].Q;
piece = this.pieces[indexes[-1].Q];
piece.p=remaining[-1][queen];
$this.zSign=aGame.zobrist.update($this.zSign,"board",piece.t,remaining[-1][queen]);
remaining[-1].splice(queen,1);
var eagle,lion;
setup%=2;
if(setup==0) {
eagle=0;
lion=1;
} else {
eagle=1;
lion=0;
}
this.board[remaining[1][eagle]]=indexes[1].E;
piece = this.pieces[indexes[1].E];
piece.p=remaining[1][eagle];
$this.zSign=aGame.zobrist.update($this.zSign,"board",piece.t,remaining[1][eagle]);
this.board[remaining[1][lion]]=indexes[1].L;
piece = this.pieces[indexes[1].L];
piece.p=remaining[1][lion];
$this.zSign=aGame.zobrist.update($this.zSign,"board",piece.t,remaining[1][lion]);
this.board[remaining[-1][eagle]]=indexes[-1].E;
piece = this.pieces[indexes[-1].E];
piece.p=remaining[-1][eagle];
$this.zSign=aGame.zobrist.update($this.zSign,"board",piece.t,remaining[-1][eagle]);
this.board[remaining[-1][lion]]=indexes[-1].L;
piece = this.pieces[indexes[-1].L];
piece.p=remaining[-1][lion];
$this.zSign=aGame.zobrist.update($this.zSign,"board",piece.t,remaining[1][lion]);
this.setupState="done";
} else
SuperModelBoardApplyMove.apply(this,arguments);
}
/*
* Model.Move.ToString overriding for setup notation
*/
var SuperModelMoveToString = Model.Move.ToString;
Model.Move.ToString = function() {
if(this.f===undefined) {
if(this.setup===undefined)
return "--";
else
return "#"+this.setup;
}
return SuperModelMoveToString.apply(this,arguments);
}
/*
* Model.Board.CompactMoveString overriding to help reading PJN game transcripts
*/
var SuperModelBoardCompactMoveString = Model.Board.CompactMoveString;
Model.Board.CompactMoveString = function(aGame,aMove,allMoves) {
if(typeof aMove.ToString!="function") // ensure proper move object, if necessary
aMove=aGame.CreateMove(aMove);
if(this.setupState===undefined || this.setupState=="setup")
return aMove.ToString();
return SuperModelBoardCompactMoveString.apply(this,arguments);
}
/*
* Model.Board.StaticGenerateMoves overriding to prevent using AI during the setup phase
*/
Model.Board.StaticGenerateMoves = function(aGame) {
if(this.setupState=="setup")
return [aGame.CreateMove({setup:Math.floor(Math.random()*12)})];
return null;
}
})();