Created
September 18, 2020 11:00
-
-
Save valex/c658e953db633abe32473d49e6757db4 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<template> | |
<div> | |
<div class="position-relative"> | |
<div class="blue merida"> | |
<div class="cg-board-wrap" ref="board"> | |
</div> | |
</div> | |
<PromotionChoice v-if=" ! _.isNull(promotion)" | |
@mousedown="onPromotionCancel" | |
@promote="onPromote" | |
:left="promotion.left" | |
:position="promotionMenuPosition" | |
:color="promotion.color"></PromotionChoice> | |
</div> | |
<div v-if="controls" class="controls-row"> | |
<div :class="{'black':turn==='b', 'white':turn==='w'}" class="turn"> | |
<span v-show="turn==='w'">Ход белых</span> | |
<span v-show="turn==='b'">Ход черных</span> | |
</div> | |
<div class="controls"> | |
<button @mousedown="onHistoryBack" :disabled="history_last_index <= -1" type="button" class="btn border-0 btn-light"><font-awesome-icon icon="caret-left" size="2x" /></button> | |
<button @mousedown="onBoardRotate" type="button" class="btn border-0 btn-light"><font-awesome-icon icon="sync-alt" size="2x" /></button> | |
<button @mousedown="onHistoryForward" :disabled="_.isEmpty(history) || (_.size(history) - 1 === history_last_index)" type="button" class="btn border-0 btn-light"><font-awesome-icon icon="caret-right" size="2x" /></button> | |
</div> | |
</div> | |
</div> | |
</template> | |
<script lang="js"> | |
import PromotionChoice from '../components/PromotionChoice.vue' | |
import { Chessground } from 'chessground'; | |
import * as util from 'chessground/util'; | |
import Chessjs from 'chess.js' | |
export default { | |
name: 'ChessgroundComponent', | |
cg: null, | |
backedUpCgConfig: null, // for restore previous move | |
chess: null, | |
components: { | |
PromotionChoice | |
}, | |
props: { | |
fen:{ | |
type: String, | |
}, | |
initialOrientation:{ // 'white', 'black' | |
type: String, | |
default: 'white' | |
}, | |
notation:{ | |
type: Boolean, | |
default: true | |
}, | |
controls:{ | |
type: Boolean, | |
default: true | |
} | |
}, | |
mounted() { | |
this.$options.chess = new Chessjs(this.fen+' 0 1'); | |
this.updateState(); | |
const el = this.$refs.board; | |
const bounding = el.getBoundingClientRect(); | |
el.setAttribute("style", "width:"+bounding.width+"px; height:"+bounding.width+"px;"); | |
this.$options.cg = Chessground(el, { | |
fen:this.$options.chess.fen(), | |
orientation: this.initialOrientation, | |
turnColor: this.turnColor, | |
movable: { | |
color: this.turnColor, | |
free: false, | |
dests: this.toDests(), | |
events: { | |
after: this.afterMove | |
} | |
}, | |
check: this.in_check, | |
draggable: { | |
showGhost: true | |
}, | |
premovable:{ | |
enabled: false | |
}, | |
drawable:{ | |
enabled: true, // can draw | |
visible: true, // can view | |
eraseOnClick: false | |
} | |
}); | |
this.backupCgConfig(); | |
// this.$options.cg.set({ | |
// drawable:{ | |
// eraseOnClick: false, | |
// brushes:{'grey':{ | |
// key:'gr', | |
// color:'#FF0000', | |
// opacity: 1, | |
// lineWidth:10 | |
// }} | |
// } | |
// }) | |
}, | |
data() { | |
return { | |
promotion: null, | |
turn: null, | |
in_check: null, | |
in_checkmate: null, | |
in_stalemate: null, | |
insufficient_material: null, | |
history: [], | |
history_last_index: -1, | |
orientation: this.initialOrientation, | |
} | |
}, | |
methods: { | |
updateState(){ | |
this.turn = this.$options.chess.turn(); | |
this.in_check = this.$options.chess.in_check(); | |
this.in_checkmate = this.$options.chess.in_checkmate(); | |
this.insufficient_material = this.$options.chess.insufficient_material(); | |
this.in_stalemate = this.$options.chess.in_stalemate(); | |
}, | |
updateHistory(){ | |
this.history = this.$options.chess.history({ verbose: true }); | |
this.history_last_index = _.size(this.history) - 1; | |
console.log(_.last(this.history)); | |
}, | |
updateChessground(){ | |
let cgState = { | |
fen: this.$options.chess.fen(), | |
turnColor: this.turnColor, | |
check: this.in_check, | |
selected: undefined, | |
movable: { | |
color: this.turnColor, | |
dests: this.toDests(), | |
}, | |
lastMove: this.lastMoveFromTo(), | |
selectable: { | |
enabled : true | |
}, | |
draggable: { | |
enabled : true | |
}, | |
}; | |
if(this.in_checkmate || this.in_stalemate || this.insufficient_material){ | |
cgState = _.assign(cgState, { | |
movable: { | |
color: undefined, | |
dests: undefined, | |
}, | |
selectable: { | |
enabled : false | |
}, | |
draggable: { | |
enabled : false | |
}, | |
}); | |
} | |
this.$options.cg.set(cgState); | |
this.backupCgConfig(); | |
}, | |
// will not be call on history move | |
afterMove(orig, dest, meta){ | |
if( ! this.startPromotion(orig, dest, this.makeBoardMove)){ | |
this.makeBoardMove(orig,dest,meta); | |
} | |
}, | |
makeBoardMove(orig, dest, meta){ | |
let chessjsMoveObject = {from: orig, to: dest}; | |
if(_.has(meta, 'promotion')){ | |
// const piece = this.$options.cg.state.pieces[dest]; | |
// if (piece && piece.role === 'pawn') { | |
// const pieces = {}; | |
// pieces[dest] = { | |
// color: piece.color, | |
// role: meta.promotion, | |
// promoted: true | |
// }; | |
// this.$options.cg.setPieces(pieces); | |
// } | |
switch(meta.promotion){ | |
case 'queen': | |
chessjsMoveObject['promotion'] = 'q'; | |
break; | |
case 'knight': | |
chessjsMoveObject['promotion'] = 'n'; | |
break; | |
case 'rook': | |
chessjsMoveObject['promotion'] = 'r'; | |
break; | |
case 'bishop': | |
chessjsMoveObject['promotion'] = 'b'; | |
break; | |
} | |
} | |
this.makeMove(chessjsMoveObject); | |
}, | |
makeJSMove(move){ | |
this.makeMove(move); | |
}, | |
makeMove(moveObject){ | |
const from_fen = this.$options.chess.fen(); | |
this.$options.chess.move(moveObject); | |
this.updateState(); | |
this.updateHistory(); | |
this.updateChessground(); | |
// let cgState = { | |
// turnColor: this.turnColor, | |
// check: this.in_check, | |
// movable: { | |
// color: this.turnColor, | |
// dests: this.toDests(), | |
// } | |
// }; | |
// if(orig[0] != dest[0]){ //check for enpassant | |
// const piece = this.$options.cg.state.pieces[dest]; | |
// if (piece && piece.role === 'pawn') { | |
// cgState['fen'] = this.$options.chess.fen(); | |
// } | |
// } | |
this.$emit('move', from_fen); | |
}, | |
onHistoryBack(){ | |
if(this.history_last_index <= -1) | |
return; | |
this.$options.chess.undo(); | |
this.history_last_index--; | |
this.updateState(); | |
this.updateChessground(); | |
this.$emit('history-move'); | |
}, | |
onHistoryForward(){ | |
let next_history_index; | |
next_history_index = 1 + this.history_last_index; | |
if(_.isNil(this.history[next_history_index])) | |
return; | |
this.history_last_index = next_history_index; | |
const move = this.history[this.history_last_index]; | |
this.$options.chess.move(move); | |
this.updateState(); | |
this.updateChessground(); | |
this.$emit('history-move'); | |
}, | |
startPromotion(orig, dest, callback){ | |
let s = this.$options.cg.state; | |
let piece = s.pieces[dest]; | |
if(piece && | |
piece.role == 'pawn' && | |
((dest[1] == '8' && s.turnColor == 'black') || | |
(dest[1] == '1' && s.turnColor == 'white'))) | |
{ | |
let left = (8 - util.key2pos(dest)[0]) * 12.5; | |
if (this.orientation === 'white') left = 87.5 - left; | |
this.promotion={ | |
orig, | |
dest, | |
callback, | |
color: piece.color, | |
left | |
}; | |
return true; | |
} | |
return false; | |
}, | |
onPromote(role){ | |
if (this.promotion.callback) { | |
this.promotion.callback(this.promotion.orig, this.promotion.dest, {promotion:role}); | |
} | |
this.promotion = null; | |
}, | |
onPromotionCancel(){ | |
this.$options.cg.set(this.$options.backedUpCgConfig); | |
this.promotion = null; | |
}, | |
onBoardRotate(){ | |
this.toggleOrientation(); | |
}, | |
toggleOrientation(){ | |
this.orientation = this.orientation === 'white' ? 'black' : 'white'; | |
this.$options.cg.toggleOrientation(); | |
}, | |
toDests(){ | |
const dests = {}; | |
this.$options.chess.SQUARES.forEach(s => { | |
const ms = this.$options.chess.moves({square: s, verbose: true}); | |
if (ms.length) dests[s] = ms.map(m => m.to); | |
}); | |
return dests; | |
}, | |
lastMove() { | |
let history = this.$options.chess.history({ verbose: true }); | |
if(_.isEmpty(history)) | |
return null; | |
return _.last(history); | |
}, | |
lastMoveFromTo() { | |
const lastMove = this.lastMove(); | |
if(_.isNil(lastMove)) | |
return []; | |
return [lastMove.from, lastMove.to] | |
}, | |
backupCgConfig(){ | |
let s = this.$options.cg.state; | |
this.$options.backedUpCgConfig = { | |
fen : this.$options.cg.getFen(), | |
turnColor : s.turnColor, | |
movable : { | |
color: s.movable.color, | |
dests: s.movable.dests | |
}, | |
check: this.in_check, | |
lastMove: s.lastMove | |
}; | |
} | |
}, | |
computed: { | |
_(){ | |
return _; | |
}, | |
turnColor() { | |
return (this.turn === 'w') ? 'white' : 'black'; | |
}, | |
promotionMenuPosition(){ | |
if(_.isNil(this.promotion)) | |
return; | |
if(this.promotion.color === 'white' && this.orientation === 'black') | |
return 'bottom'; | |
if(this.promotion.color === 'black' && this.orientation === 'white') | |
return 'bottom'; | |
return 'top'; | |
}, | |
}, | |
} | |
</script> | |
<style scoped> | |
.controls-row{ | |
display:flex; | |
flex: 1 1 200px; | |
flex-direction: row; | |
justify-content: space-between; | |
align-items: stretch; | |
width: 100%; | |
height: 40px; | |
-webkit-touch-callout: none; /* iOS Safari */ | |
-webkit-user-select: none; /* Safari */ | |
-khtml-user-select: none; /* Konqueror HTML */ | |
-moz-user-select: none; /* Firefox */ | |
-ms-user-select: none; /* Internet Explorer/Edge */ | |
user-select: none; /* Non-prefixed version, currently | |
supported by Chrome and Opera */ | |
} | |
.controls-row .turn{ | |
text-shadow: 0 1px 1px #000; | |
overflow: hidden; | |
display:flex; | |
flex-direction: row; | |
align-items:center; | |
} | |
.controls-row .turn.white{ | |
background-color: white; | |
border: 1px solid lightgrey; | |
} | |
.controls-row .turn.black{ | |
background-color: #444444; | |
color: white; | |
text-shadow: 0 1px 1px #FFFFFF; | |
} | |
.controls-row .turn span{ | |
padding: 0 1.5rem; | |
} | |
</style> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment