Harnessing the art of chess pattern recognition—emphasized by experts, among them Grandmaster Jonathan Rowson in “The Seven Deadly Chess Sins”—the demonstration below utilizes the powerful
quadrille
search method to identify key tactical patterns on the provided board, given in FEN notation. These patterns, which encompass maneuvers like knight forks where a piece simultaneously threatens multiple opponent pieces, can decisively influence the game’s dynamics. The p5.quadrille.js library aids players in detecting and adeptly deploying these patterns, thereby refining gameplay and strategic decision-making on the chessboard.
code
Quadrille.cellLength = 40
const COLS = 8, ROWS = 8
let fenBoard, fen, patternBoard, pattern, hint
const defaultFen = 'r3kb1r/ppN1pppp/5n2/8/8/8/PPP3PP/R3KB1R'
let fenInput
let rows, cols
let pieces
let hits
function setup() {
createCanvas((2 * COLS + 1) * Quadrille.cellLength,
(ROWS + 1) * Quadrille.cellLength)
fenBoard = createQuadrille()
fenInput = createInput()
fenInput.size(8 * Quadrille.cellLength - 5)
fenInput.position(9 * Quadrille.cellLength + 5, 8 * Quadrille.cellLength + 15)
fenInput.changed(() => update())
pieces = createSelect()
pieces.option('clear')
for (const symbol of Object.values(Quadrille.chessSymbols)) {
pieces.option(symbol)
}
pieces.selected('clear')
pieces.position(10, 8 * Quadrille.cellLength + 15)
rows = createSelect()
cols = createSelect()
for (let value = 1; value < 9; value++) {
rows.option(value.toString())
cols.option(value.toString())
}
rows.selected('2')
rows.position(75, 8 * Quadrille.cellLength + 15)
rows.changed(() => {
pattern.height = +rows.value()
patternBoard.height = +rows.value()
patternBoard.fill()
update()})
cols.selected('5')
cols.position(115, 8 * Quadrille.cellLength + 15)
cols.changed(() => {
pattern.width = +cols.value()
patternBoard.width = +cols.value()
patternBoard.fill()
update()})
reset()
update()
}
function draw() {
background('darkkhaki')
drawQuadrille(patternBoard, { tileDisplay: 0 })
drawQuadrille(pattern, { textColor: 'black', tileDisplay: 0 })
drawQuadrille(fenBoard, { row: 0, col: 9, tileDisplay: 0 })
drawQuadrille(fen, { row: 0, col: 9, textColor: 'black', tileDisplay: 0 })
if (hits.length > 0) {
drawQuadrille(hint, { row: hits[0].row,
col: 9 + hits[0].col, outline: 'magenta' })
}
}
function keyPressed() {
if (key === 'r') {
reset()
update()
}
}
function mouseClicked() {
const row = pattern.mouseRow
const col = pattern.mouseCol
const value = pieces.value()
pieces.value() === 'clear' ? pattern.clear(row, col) :
pattern.fill(row, col, pieces.value())
update()
}
function reset() {
fenInput.value(defaultFen)
fen = createQuadrille(fenInput.value())
const r = Quadrille.chessSymbols['r']
const k = Quadrille.chessSymbols['k']
const N = Quadrille.chessSymbols['N']
pattern = createQuadrille([
[r, null, null, null, k],
[null, null, N]
])
patternBoard = createQuadrille(pattern.width, pattern.height).fill()
rows.selected(pattern.height)
cols.selected(pattern.width)
}
function update() {
fen = createQuadrille(fenInput.value())
hits = fen.search(pattern, true)
hint = pattern.clone()
const black = color('black')
hint = Quadrille.neg(pattern, color(red(black), green(black), blue(black), 180))
}
Reset
The reset
function restores the initial FEN and sets up the pattern
for subsequent searches:
function reset() {
// i. set default fen
fenInput.value(defaultFen)
// ii. loads fen quadrille
fen = createQuadrille(fenInput.value())
// iii. creates default pattern
const r = Quadrille.chessSymbols['r']
const k = Quadrille.chessSymbols['k']
const N = Quadrille.chessSymbols['N']
pattern = createQuadrille([
[r, null, null, null, k],
[null, null, N]
])
patternBoard = createQuadrille(pattern.width, pattern.height).fill()
rows.selected(pattern.height)
cols.selected(pattern.width)
}
This function initializes the environment for pattern recognition with a standard starting position.
API references
Update
The update function refreshes the app state in response to user interactions:
function update() {
fen = createQuadrille(fenInput.value())
hits = fen.search(pattern, true)
hint = pattern.clone()
const black = color('black')
hint = Quadrille.neg(pattern, color(red(black), green(black), blue(black), 180))
}
Each action taken by the user triggers this function, which reevaluates the FEN, identifies pattern
matches, and creates a semi-transparent red overlay (hint
) to signify discovered patterns on the board.
API references
Drawing
The draw
function visually represents the chessboard state and identified patterns:
function draw() {
background('darkkhaki')
drawQuadrille(patternBoard, { tileDisplay: 0 })
drawQuadrille(pattern, { textColor: 'black', tileDisplay: 0 })
drawQuadrille(fenBoard, { row: 0, col: 9, tileDisplay: 0 })
drawQuadrille(fen, { row: 0, col: 9, textColor: 'black', tileDisplay: 0 })
if (hits.length > 0) {
drawQuadrille(hint, { row: hits[0].row,
col: 9 + hits[0].col, outline: 'magenta' })
}
}
The rendering sequence is pivotal for correct visual output: the pattern
and its board
on the left, the FEN and hints
on the right. Highlighting occurs only when matches are identified by the search.
Interaction
Keyboard
Pressing the r
key resets the board and updates the view:
function keyPressed() {
if (key === 'r') {
reset()
update()
}
}
This binds the ‘r’ key to reinitialize the game state to its default and process any changes.
Mouse
Clicking the mouse edits the pattern and triggers an update:
function mouseClicked() {
const row = pattern.mouseRow
const col = pattern.mouseCol
if (pieces.value() === 'clear') {
pattern.clear(row, col)
} else {
pattern.fill(row, col, pieces.value())
}
update()
}
Mouse interactions allow the user to modify the pattern on the board and immediately reflect those modifications.
API references