NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name 리체스 보드 시각화 버튼 추가 // @version 1.3 // @description 리체스 사이트에서 보드를 시각화할수있는 버튼들을 추가합니다. // @match https://lichess.org/* // @author projang // @license MIT // @copyright 2021, projang (https://openuserjs.org/users/projang) // @updateURL https://openuserjs.org/meta/projang/리체스_보드_시각화_버튼_추가.meta.js // @grant GM_addStyle // @grant GM_getResourceText // @require https://cdnjs.cloudflare.com/ajax/libs/chess.js/0.10.3/chess.min.js // ==/UserScript== const url = window.location.href; function init() { const pgnMenuElement = document.querySelector("div.pgn-options > div"); if (!pgnMenuElement) throw new Error("pgn 정보가 없습니다!"); pgnMenuElement.insertAdjacentHTML( "afterbegin", `<a data-icon="" class="text" id="drawheat">히트맵</a>` ); document.querySelector("#drawheat").onclick = drawHeatmap; pgnMenuElement.insertAdjacentHTML( "afterbegin", `<a data-icon="" class="text" id="drawcolorheat">흑백 히트맵</a>` ); document.querySelector("#drawcolorheat").onclick = drawColorHeatmap; } init(); function resetBoard() { console.log("reset board"); const oldMenuButtons = document.querySelectorAll("div.pgn-options > div > a"); const listeners = [...oldMenuButtons].map((e) => e.onclick); eval(document.querySelector("body>script:last-child").textContent); setTimeout(() => { const newMenuButtons = [ ...document.querySelectorAll("div.pgn-options > div > a"), ]; for (let i = 0; i < newMenuButtons.length; i++) { newMenuButtons[i].onclick = listeners[i]; } }, 0); } function drawColorHeatmap() { const canvasId = "colorheatmap"; if (!prepareChessArea(canvasId)) { resetBoard(); return; } console.log("draw color heatmap"); var canvas = document.getElementById(canvasId); var ctx = canvas.getContext("2d"); var w = canvas.width; var cw = w / 8; const heatmap = getHeatmap(); const max = Math.max(...heatmap.flat()); const flipped = url.includes("black"); for (var i = 0; i < 8; i++) { for (var j = 0; j < 8; j++) { const x = flipped ? 7 - j : j; const y = flipped ? 7 - i : i; const heat = heatmap[x][y]; const w = convertRange(heat, [0, max], [0, 255]); const color = `rgb(${w},${w},${w})`; ctx.fillStyle = color; const size = cw; ctx.fillRect(j * cw, i * cw, size, size); } } } function drawHeatmap() { const canvasId = "heatmap"; if (!prepareChessArea(canvasId)) { resetBoard(); return; } console.log("draw heatmap"); var canvas = document.getElementById(canvasId); var ctx = canvas.getContext("2d"); var w = canvas.width; var cw = w / 8; const heatmap = getHeatmap(); const max = Math.max(...heatmap.flat()); const min = Math.min(...heatmap.flat()); const flipped = url.includes("black"); for (var i = 0; i < 8; i++) { for (var j = 0; j < 8; j++) { const darkColor = "#c1c18e"; const lightColor = "#ececec"; var fill = (i + j) % 2 ? darkColor : lightColor; ctx.fillStyle = fill; ctx.fillRect(j * cw, i * cw, cw, cw); ctx.fillStyle = "black"; // if flipped, draw from bottom to top const x = flipped ? 7 - j : j; const y = flipped ? 7 - i : i; const heat = heatmap[x][y]; const size = convertRange(heat, [min, max], [0, cw - cw / 4]); ctx.fillRect( j * cw + cw / 2 - size / 2, i * cw + cw / 2 - size / 2, size, size ); } } } let heatmap; function getHeatmap() { if (heatmap) return heatmap; const history = getChessHistory(); const squareRecord = history.map((e) => e.to); // in squareRecord, each element is a string like "e2" // parse squareRecord to get x, y and save to heatmap heatmap = new Array(8).fill(0).map(() => new Array(8).fill(0)); const test = {}; squareRecord.forEach((e) => { test[e] = test[e] ? test[e] + 1 : 1; let [alphabet, x] = e.split(""); const y = alphabet.charCodeAt(0) - 97; x--; heatmap[y][x]++; }); // make test to desending order const test2 = Object.keys(test).map((e) => [e, test[e]]); test2.sort((a, b) => b[1] - a[1]); console.log(test2); // flip heatmap top to bottom heatmap = heatmap.map((e) => e.reverse()); return heatmap; } function prepareChessArea(canvasId) { const cg_container = document.querySelector("cg-container"); if (!cg_container) return false; const { offsetWidth: width } = cg_container; const html = `<canvas id="${canvasId}" class="cg-container">`; cg_container.outerHTML = html; document.getElementById(canvasId).width = document.getElementById( canvasId ).height = width; return true; } function convertRange(value, r1, r2) { return ((value - r1[0]) * (r2[1] - r2[0])) / (r1[1] - r1[0]) + r2[0]; } function getChessHistory() { const pgn = document.querySelector(".pgn").textContent; const chess = new Chess(); chess.load_pgn(pgn); const history = chess.history({ verbose: true, }); return history; }