Skip to content

Instantly share code, notes, and snippets.

@triestpa
Created May 16, 2017 07:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save triestpa/4c006aef041561e1dda2f280e1feab09 to your computer and use it in GitHub Desktop.
Save triestpa/4c006aef041561e1dda2f280e1feab09 to your computer and use it in GitHub Desktop.
Test
import ChessGame from './utils/chessgame'
/**
* Chessboard VueJS Component.
* Takes a PGN string and a side (w or b) as props
*/
export default {
name: 'chessboard',
props: {
/** Pgn is a string encoded with the current game state */
pgn: {
type: String,
default: null
},
/** side is either 'w' or 'b', and determines which board orientation to display */
side: {
type: String,
default: 'w'
}
},
model: {
prop: 'pgn',
event: 'change'
},
data () {
return {
chessGame: null, // the game logic class
availableMoves: {}, // key/value obj of available moves
selectedIndex: -1, // the currently selected square
squareStyle: { // Square style - values will be reset whenever window is resized
height: '100px',
width: '100px'
}
}
},
computed: {
/** Return the board array to be displayed */
board () {
return this.chessGame.getBoard()
},
/** Return if it is black or white's turn */
turn () {
return this.chessGame.getTurn()
}
},
watch: {
/** Watch the PGN property so that the game can receive new moves from outside the component */
'pgn': function (newVal, oldVal) {
this.syncToPgn(newVal)
},
/** Watch the side property so that the UI reflects side-swaps */
'side': function (newVal, oldVal) {
// Set the board orientation whenever the side changes
this.chessGame.setSide(newVal)
this.selectedIndex = -1
this.availableMoves = {}
}
},
created () {
// Initialize a new game for the provided pgn and side properties
this.chessGame = new ChessGame(this.pgn, this.side)
},
mounted () {
// On mounted,set the square width and hight
this.$nextTick(() => {
// Reset sqaure size every time window resizes
window.addEventListener('resize', this.getSquareStyle)
this.getSquareStyle()
})
},
methods: {
/** Set the square width/height to 1/8 of the total board width */
getSquareStyle (event) {
const board = this.$refs.board
if (board) {
// Subtract 1 from total width to avoid pixel-wrapping edge-case
const squareEdgeLength = (board.offsetWidth - 1) / 8
this.squareStyle = {
height: `${squareEdgeLength}px`,
width: `${squareEdgeLength}px`
}
}
},
/** Apply a new move to the board */
applyNewMove (newMove) {
const srcIndex = this.chessGame.getIndexForPositionString(newMove.from)
const targetIndex = this.chessGame.getIndexForPositionString(newMove.to)
this.movePiece(srcIndex, targetIndex)
},
/** Check if a square index is an available move */
isAvailableMove (index) {
return (this.availableMoves[index] === true)
},
/** Recieve a new pgn, and sync the displayed board to it */
syncToPgn (newPgn) {
// Generate a new game with the new pgn
const newGame = new ChessGame(newPgn)
// Get the histories for the two games
const newHistory = newGame.getHistory()
const oldHistory = this.chessGame.getHistory()
// If the new pgn is one move ahead of the old, make the connecting move
if (newHistory.length === oldHistory.length + 1) {
this.applyNewMove(newHistory.pop())
// Else if they are not the same length, reset the whole board to the new pgn
} else if (newHistory.length !== oldHistory.length) {
this.chessGame = new ChessGame(this.pgn, this.side)
}
},
/** Get the icon for the piece */
getIcon (square) {
if (square.piece) {
return require(`../../assets/icons/${square.piece.color}${square.piece.type}.svg`)
}
},
/** Reset the board to original position */
reset () {
this.chessGame.reset()
this.syncBoard()
},
/** Animate piece from one index to another */
swap (oldIndex = 0, newIndex = 0) {
const temp = this.board[newIndex]
this.$set(this.board, newIndex, this.board[oldIndex])
this.$set(this.board, oldIndex, temp)
},
/** Move a piece to the target index, if move is valid */
movePiece (source, target) {
const result = this.chessGame.calculateMove(source, target)
// if result is undefined, move is invalid
if (result) {
this.swap(target, source) // Swap the contents of the squares
this.syncBoard() // Sync the piece position with the board model
this.availableMoves = {} // Reset available moves
// Emit the move to any parent components
setTimeout(() => this.$emit('change', this.chessGame.getPGN()), 200)
}
},
/** Sync the displayed board with the chessgame object. */
syncBoard () {
/**
* Why not just use "this.board = this.chessGame.getBoard()"? It has to do with
* how Vuejs handles reactivity within arrays. Long-story-short, in order to sync
* any side-effects from the move, such as castles, promotions, and captures, it is best
* for our purposes to manually assign the changed pieces within the array item.
*/
const updatedBoard = this.chessGame.getBoard()
for (let i = 0; i < updatedBoard.length; i++) {
if (this.board[i].piece !== updatedBoard[i].piece) {
this.board[i].piece = updatedBoard[i].piece
}
}
},
/** Display all available moves for the selected piece */
getAvailableMoves (index) {
this.selectedIndex = index
const moves = this.chessGame.getAvailableMoves(index)
if (moves.length < 1) {
// If no available moves, de-select the square
this.selectedIndex = -1
} else {
// Set each move to true in the available moves dict
for (let move of moves) {
// Use $set for the changes to display in the UI
this.$set(this.availableMoves, move, true)
}
}
},
/** On-click for square, select square or try move if suqare is selected */
squareSelected (index) {
this.availableMoves = {}
if (this.selectedIndex > 0) {
this.movePiece(this.selectedIndex, index)
this.selectedIndex = -1
} else {
this.getAvailableMoves(index)
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment