import Point from './Point' ; import Piece from './Piece' ; import Move from './Move' ; import BoardBase from './BoardBase' ; import { PLAYER , PIECE , SIZE , STATUS } from './define' ; export default class Rule { static isOutsideBoard ( pos ) { return pos . x < 0 || pos . x >= SIZE . files || pos . y < 0 || pos . y >= SIZE . ranks ; } static isInCastle ( pos , owner , board ) { if ( pos . x < 3 || pos . x > 5 ) return false ; if ( owner === board . upSidePlayer ) { return pos . y <= 2 ; } else { return pos . y >= 7 ; } } static isCrossRiver ( pos , owner , board ) { if ( owner === board . upSidePlayer ) { return pos . y >= 5 ; } else { return pos . y <= 4 ; } } static quickTestStatus ( board ) { let own = null ; let opponent = null ; let pos = new Point ( ) ; for ( let x of [ 3 , 4 , 5 ] ) { for ( let y of [ 0 , 1 , 2 , 7 , 8 , 9 ] ) { pos . moveTo ( x , y ) ; let piece = board . at ( pos ) ; if ( piece == null ) continue ; if ( piece . type === PIECE . King ) { if ( piece . isOwnedBy ( board . currentMover ) ) own = pos . clone ( ) ; else opponent = pos . clone ( ) ; } } } if ( own == null && opponent == null ) { return STATUS . Impossbile ; } else if ( own == null ) { return STATUS . Lost ; } else if ( opponent == null ) { return STATUS . Win ; } if ( own . x === opponent . x ) { let up = ( board . currentMover == board . upSidePlayer ? own . y : opponent . y ) ; let blew = ( board . currentMover == board . blewSidePlayer ? own . y : opponent . y ) ; let intervening = 0 ; pos . moveTo ( own . x , up + 1 ) ; while ( pos . y < blew ) { if ( ! board . empty ( pos ) ) { ++ intervening ; break ; } pos . y += 1 ; } if ( intervening === 0 ) return STATUS . FlyCheck ; } if ( board . moveHistory . length > 60 ) { if ( board . moveHistory . slice ( - 60 ) . every ( ( m ) => m . target == null ) ) return STATUS . Draw ; } return STATUS . NotEnded ; } static isLegalMove ( move , board ) { let from = move . from , to = move . to ; if ( Rule . isOutsideBoard ( from ) || Rule . isOutsideBoard ( to ) ) return false ; let piece = board . at ( from ) ; if ( piece == null ) throw Error ( "Piece Not Found!" ) ; if ( ! piece . isOwnedBy ( board . currentMover ) ) return false ; let target = board . at ( to ) ; if ( target != null ) { if ( piece . isSameOwnerWith ( target ) ) return false ; } switch ( piece . type ) { case PIECE . King : return Rule . isLegalKingMove ( move , piece . owner , board ) ; case PIECE . Guard : return Rule . isLegalGuardMove ( move , piece . owner , board ) ; case PIECE . Bishop : return Rule . isLegalBishopMove ( move , piece . owner , board ) ; case PIECE . Knight : return Rule . isLegalKnightMove ( move , board ) ; case PIECE . Rook : return Rule . isLegalRookMove ( move , board ) ; case PIECE . Cannon : return Rule . isLegalCannonMove ( move , board ) ; case PIECE . Pawn : return Rule . isLegalPawnMove ( move , piece . owner , board ) ; default : throw new Error ( "Unkown Piece Type!" ) ; } } private static isLegalCannonMove ( move , board ) { let from = move . from , to = move . to ; let dx = to . x - from . x , dy = to . y - from . y ; if ( dx != 0 && dy != 0 ) return false ; let steps = Math . abs ( dx ) || Math . abs ( dy ) ; let unitX = dx == 0 ? 0 : ( dx > 0 ? 1 : - 1 ) , unitY = dy == 0 ? 0 : ( dy > 0 ? 1 : - 1 ) ; let pos = from . clone ( ) ; let intervening = 0 ; for ( let i = 1 ; i < steps ; ++ i ) { pos . moveBy ( unitX , unitY ) ; if ( ! board . empty ( pos ) ) ++ intervening ; if ( intervening > 1 ) return false ; } return board . empty ( to ) ? intervening === 0 : intervening === 1 ; } private static isLegalRookMove ( move , board ) { let from = move . from , to = move . to ; let dx = to . x - from . x , dy = to . y - from . y ; if ( dx != 0 && dy != 0 ) return false ; let steps = Math . abs ( dx ) || Math . abs ( dy ) ; let unitX = dx == 0 ? 0 : ( dx > 0 ? 1 : - 1 ) , unitY = dy == 0 ? 0 : ( dy > 0 ? 1 : - 1 ) ; let pos = from . clone ( ) ; for ( let i = 1 ; i < steps ; ++ i ) { pos . moveBy ( unitX , unitY ) ; if ( ! board . empty ( pos ) ) return false ; } return true ; } private static isLegalBishopMove ( move , owner , board ) { let from = move . from , to = move . to ; if ( Rule . isCrossRiver ( from , owner , board ) || Rule . isCrossRiver ( to , owner , board ) ) return false ; let dx = to . x - from . x , dy = to . y - from . y ; if ( Math . abs ( dx ) != 2 || Math . abs ( dy ) != 2 ) return false ; let pos = new Point ( from . x + dx / 2 , from . y + dy / 2 ) ; return board . empty ( pos ) ; } private static isLegalKingMove ( move , owner , board ) { let from = move . from , to = move . to ; if ( ! Rule . isInCastle ( from , owner , board ) || ! Rule . isInCastle ( to , owner , board ) ) return false ; let dx = to . x - from . x , dy = to . y - from . y ; if ( Math . abs ( dx ) + Math . abs ( dy ) != 1 ) return false ; return true ; } private static isLegalGuardMove ( move , owner , board ) { let from = move . from , to = move . to ; if ( ! Rule . isInCastle ( from , owner , board ) || ! Rule . isInCastle ( to , owner , board ) ) return false ; let dx = to . x - from . x , dy = to . y - from . y ; if ( Math . abs ( dx ) != 1 || Math . abs ( dy ) != 1 ) return false ; return true ; } private static isLegalKnightMove ( move , board ) { let from = move . from , to = move . to ; let dx = to . x - from . x , dy = to . y - from . y ; if ( dx == 0 || dy == 0 || Math . abs ( dx ) + Math . abs ( dy ) != 3 ) return false ; let pos = from . clone ( ) ; if ( Math . abs ( dx ) == 2 ) pos . x += dx / 2 ; else pos . y += dy / 2 ; return board . empty ( pos ) ; } private static isLegalPawnMove ( move , owner , board ) { let from = move . from , to = move . to ; let dx = to . x - from . x , dy = to . y - from . y ; if ( ! Rule . isCrossRiver ( from , owner , board ) && dx != 0 ) return false ; if ( owner === board . upSidePlayer && dy < 0 ) return false ; else if ( owner === board . blewSidePlayer && dy > 0 ) return false ; if ( Math . abs ( dx ) + Math . abs ( dy ) != 1 ) return false ; return true ; } static getPossibleMoves ( board , currentMoverOnly = true ) { let allMoves = [ ] , pos = new Point ( 0 , 0 ) ; for ( let i = 0 ; i < SIZE . files ; i ++ ) { for ( let j = 0 ; j < SIZE . ranks ; j ++ ) { pos . moveTo ( i , j ) ; let piece = board . at ( pos ) ; if ( piece == null ) continue ; if ( currentMoverOnly && piece . owner != board . currentMover ) continue ; let moves = [ ] ; switch ( piece . type ) { case PIECE . King : moves = Rule . getKingPossibleMoves ( piece , pos , board ) ; break ; case PIECE . Guard : moves = Rule . getGuardPossibleMoves ( piece , pos , board ) ; break ; case PIECE . Bishop : moves = Rule . getBishopPossibleMoves ( piece , pos , board ) ; break ; case PIECE . Knight : moves = Rule . getKnightPossibleMoves ( piece , pos , board ) ; break ; case PIECE . Rook : moves = Rule . getRookPossibleMoves ( piece , pos , board ) ; break ; case PIECE . Cannon : moves = Rule . getCannonPossibleMoves ( piece , pos , board ) ; break ; case PIECE . Pawn : moves = Rule . getPawnPossibleMoves ( piece , pos , board ) ; break ; default : throw new Error ( "Unkown Piece Type!" ) ; } allMoves = allMoves . concat ( moves ) ; } } if ( currentMoverOnly ) return allMoves . filter ( ( move ) => move . protege == null ) ; return allMoves ; } private static possibleMove ( piece , from , to , relate ) { let move = new Move ( from . clone ( ) , to . clone ( ) ) ; if ( relate != null ) { if ( piece . isSameOwnerWith ( relate ) ) move . guard ( relate . type ) ; else move . attack ( relate . type ) ; } return move ; } private static getKingPossibleMoves ( piece , pos , board ) { let moves = [ ] ; let from = pos . clone ( ) ; [ [ - 1 , 0 ] , [ 0 , - 1 ] , [ 1 , 0 ] , [ 0 , 1 ] ] . forEach ( ( d ) => { pos . moveTo ( from . x + d [ 0 ] , from . y + d [ 1 ] ) ; if ( ! Rule . isOutsideBoard ( pos ) ) { if ( Rule . isInCastle ( pos , piece . owner , board ) ) { moves . push ( Rule . possibleMove ( piece , from , pos , board . at ( pos ) ) ) ; } } } ) ; return moves ; } private static getGuardPossibleMoves ( piece , pos , board ) { let moves = [ ] ; let from = pos . clone ( ) ; [ [ - 1 , 1 ] , [ 1 , - 1 ] , [ 1 , 1 ] , [ - 1 , - 1 ] ] . forEach ( ( d ) => { pos . moveTo ( from . x + d [ 0 ] , from . y + d [ 1 ] ) ; if ( ! Rule . isOutsideBoard ( pos ) ) { if ( Rule . isInCastle ( pos , piece . owner , board ) ) { moves . push ( Rule . possibleMove ( piece , from , pos , board . at ( pos ) ) ) ; } } } ) ; return moves ; } private static getBishopPossibleMoves ( piece , pos , board ) { let moves = [ ] ; let from = pos . clone ( ) ; [ [ - 2 , 2 ] , [ 2 , - 2 ] , [ 2 , 2 ] , [ - 2 , - 2 ] ] . forEach ( ( d ) => { pos . moveTo ( from . x + d [ 0 ] , from . y + d [ 1 ] ) ; if ( ! Rule . isOutsideBoard ( pos ) ) { if ( ! this . isCrossRiver ( pos , piece . owner , board ) ) { if ( board . empty ( new Point ( from . x + d [ 0 ] / 2 , from . y + d [ 1 ] / 2 ) ) ) { moves . push ( Rule . possibleMove ( piece , from , pos , board . at ( pos ) ) ) ; } } } } ) ; return moves ; } private static getKnightPossibleMoves ( piece , pos , board ) { let moves = [ ] ; let from = pos . clone ( ) ; [ [ 1 , - 2 ] , [ 2 , - 1 ] , [ 2 , 1 ] , [ 1 , 2 ] , [ - 1 , 2 ] , [ - 2 , 1 ] , [ - 2 , - 1 ] , [ - 1 , - 2 ] ] . forEach ( ( d ) => { pos . moveTo ( from . x + d [ 0 ] , from . y + d [ 1 ] ) ; if ( ! Rule . isOutsideBoard ( pos ) ) { let hobble = d . map ( x => x > 0 ? Math . floor ( x / 2 ) : Math . ceil ( x / 2 ) ) ; if ( board . empty ( new Point ( from . x + hobble [ 0 ] , from . y + hobble [ 1 ] ) ) ) { moves . push ( Rule . possibleMove ( piece , from , pos , board . at ( pos ) ) ) ; } } } ) ; return moves ; } private static getRookPossibleMoves ( piece , pos , board ) { let moves = [ ] ; let from = pos . clone ( ) ; [ [ - 1 , 0 ] , [ 1 , 0 ] , [ 0 , 1 ] , [ 0 , - 1 ] ] . forEach ( ( d ) => { let to = new Point ( from . x + d [ 0 ] , from . y + d [ 1 ] ) ; while ( ! Rule . isOutsideBoard ( to ) && board . empty ( to ) ) { moves . push ( new Move ( from . clone ( ) , to . clone ( ) ) ) ; to . moveBy ( d [ 0 ] , d [ 1 ] ) ; } if ( ! Rule . isOutsideBoard ( to ) ) { moves . push ( Rule . possibleMove ( piece , from , to , board . at ( to ) ) ) ; } } ) ; return moves ; } private static getPawnPossibleMoves ( piece , pos , board ) { let moves = [ ] ; let from = pos . clone ( ) ; [ [ - 1 , 0 ] , [ 1 , 0 ] , [ 0 , - 1 ] , [ 0 , 1 ] ] . forEach ( ( d ) => { if ( ( piece . isOwnedBy ( board . upSidePlayer ) && d [ 1 ] != - 1 ) || ( piece . isOwnedBy ( board . blewSidePlayer ) && d [ 1 ] != 1 ) ) { if ( Rule . isCrossRiver ( from , piece . owner , board ) || d [ 0 ] == 0 ) { let to = new Point ( from . x + d [ 0 ] , from . y + d [ 1 ] ) ; if ( ! Rule . isOutsideBoard ( to ) ) { moves . push ( Rule . possibleMove ( piece , from , to , board . at ( to ) ) ) ; } } } } ) ; return moves ; } private static getCannonPossibleMoves ( piece , pos , board ) { let moves = [ ] ; let from = pos . clone ( ) ; [ [ - 1 , 0 ] , [ 1 , 0 ] , [ 0 , 1 ] , [ 0 , - 1 ] ] . forEach ( ( d ) => { let to = new Point ( from . x + d [ 0 ] , from . y + d [ 1 ] ) ; while ( ! Rule . isOutsideBoard ( to ) && board . empty ( to ) ) { moves . push ( new Move ( from . clone ( ) , to . clone ( ) ) ) ; to . moveBy ( d [ 0 ] , d [ 1 ] ) ; } if ( ! Rule . isOutsideBoard ( to ) ) to . moveBy ( d [ 0 ] , d [ 1 ] ) ; while ( ! Rule . isOutsideBoard ( to ) && board . empty ( to ) ) { to . moveBy ( d [ 0 ] , d [ 1 ] ) ; } if ( ! Rule . isOutsideBoard ( to ) ) { moves . push ( Rule . possibleMove ( piece , from , to , board . at ( to ) ) ) ; } } ) ; return moves ; } }