Last active
May 21, 2021 20:13
-
-
Save sebastian-palma/e0ee73da804929a2193f23ea83f0f6bc 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
{"id":"06a4194c4b016917","slug":null,"trashed":false,"description":"","like":false,"likes":0,"publish_level":"private","forks":0,"fork_of":{"id":"17468271a273819d","slug":"visualizing-chess-games","title":"Visualizing chess games","owner":{"id":"9ba5e0b68d3e7972","github_login":"EE2dev","avatar_url":"https://avatars0.githubusercontent.com/u/7562919?v=4","login":"ee2dev","name":"","bio":"","home_url":"https://github.com/EE2dev/","type":"individual"},"version":698},"update_time":"2021-05-21T20:12:48.480Z","publish_time":null,"publish_version":null,"thumbnail":null,"default_thumbnail":null,"roles":["editor","owner"],"sharing":"private","subscription":"comments","edit_unpublished":false,"owner":{"id":"77ad7d1d53486a0d","github_login":"sebastian-palma","avatar_url":"https://avatars.githubusercontent.com/u/11888191?v=4","login":"sebastian-palma","name":"Sebastián","bio":"","home_url":"","type":"individual"},"creator":{"id":"77ad7d1d53486a0d","github_login":"sebastian-palma","avatar_url":"https://avatars.githubusercontent.com/u/11888191?v=4","login":"sebastian-palma","name":"Sebastián","bio":"","home_url":""},"collections":[],"files":[],"comments":[],"commenting_lock":null,"version":755,"title":"","license":null,"copyright":"","nodes":[{"id":123,"value":"md `## Choose a chess game to be visualized`","pinned":false,"mode":"js"},{"id":204,"value":"viewof gameInputSelection = form(html`<form>\n <div>\n Provide the chess game by:\n <br><label><input name=\"game input\" type=\"radio\" value=\"lichess\" checked> <i>copy/paste link from lichess.org</i></label>\n<br><label><input name=\"game input\" type=\"radio\" value=\"pgn\"> <i>copy/paste a game in PGN</i></label>\n <!--br><label><input name=\"game input\" type=\"radio\" value=\"board\"> <i>enter moves on a chess board</i></label-->\n </div>\n</form>`)","pinned":false,"mode":"js"},{"id":207,"value":"viewof myGame = {\n if (gameInputSelection[\"game input\"] === \"lichess\") {\n return form(html`<form>\n <div><label><input name=\"message\" type=\"text\" value=\"https://lichess.org/jHJ5wEwP/black\"> <i>Copy the link to your Lichess game here</i></label></div>\n</form>`)\n } else if (gameInputSelection[\"game input\"] === \"pgn\") { \n return textarea({\n title: \"Chess game to be visualized\", \n placeholder: \"Insert game in PGN format here...\", \n width: \"100%\",\n rows: 10,\n });\n }\n}","pinned":false,"mode":"js"},{"id":128,"value":"md`## Visualizing chess games (Work in progress)`","pinned":false,"mode":"js"},{"id":72,"value":"viewof squareWidth = slider({\n min: 1, \n max: maxWidth, \n step: 1, \n value: defaultWidth, \n title: \"Square width\", \n description: \"adjust the slider to change the width of a chessboard square\"\n})","pinned":false,"mode":"js"},{"id":338,"value":"md`### Version 8: drawing chess pieces, a few and their paths with higher opacity`","pinned":false,"mode":"js"},{"id":342,"value":"version8();","pinned":false,"mode":"js"},{"id":5,"value":"md`## Appendix`","pinned":false,"mode":"js"},{"id":53,"value":"md`#### Libraries and imports` ","pinned":false,"mode":"js"},{"id":8,"value":"d3 = require(\"d3\")","pinned":false,"mode":"js"},{"id":41,"value":"myChess = require(\"chess.js\")","pinned":false,"mode":"js"},{"id":101,"value":"import {form} from \"@mbostock/form-input\"","pinned":true,"mode":"js"},{"id":79,"value":"import {text, textarea, radio, slider} from \"@jashkenas/inputs\"","pinned":false,"mode":"js"},{"id":57,"value":"md`#### Code` ","pinned":false,"mode":"js"},{"id":640,"value":"maxWidth = { return width < (50 + squarePadding) * 9 ? width / 9 : 100;}","pinned":false,"mode":"js"},{"id":647,"value":"defaultWidth = { return maxWidth < 50 ? maxWidth : 50;}","pinned":false,"mode":"js"},{"id":69,"value":"squarePadding = 1","pinned":false,"mode":"js"},{"id":9,"value":"function drawChessboard( squareColors = {white: \"#030\", black: \"black\"}, paddingX = 0, paddingY = 0) {\n \n const squareColorWhite = squareColors.white;\n const squareColorBlack = squareColors.black; \n const squareTotal = squareWidth + squarePadding;\n const w = squareTotal * 8 + squarePadding + paddingX;\n const h = squareTotal * 8 + squarePadding + paddingY;\n const svg = DOM.svg(w, h);\n \n // specify defs \n getDefsForSVG(svg);\n\n d3.select(svg).append(\"rect\")\n .style(\"fill\",squareColorBlack)\n .attr(\"width\", w)\n .attr(\"height\", h);\n \n const numSquares = d3.range(64);\n \n // draw squares\n d3.select(svg)\n .append(\"g\")\n .attr(\"transform\", \"translate(\" + squarePadding + \", \" + squarePadding + \")\")\n .selectAll(\"rect.square\")\n .data(numSquares)\n .join(\"rect\")\n .attr(\"class\", \"square\")\n .style(\"fill\", d => (d % 16 < 8) ? ((d % 2) ? squareColorBlack : squareColorWhite) \n : (!(d % 2) ? squareColorBlack : squareColorWhite))\n .style(\"stroke\", \"none\")\n .attr(\"x\", d => (d % 8) * squareTotal)\n .attr(\"y\", d => Math.floor(d / 8) * squareTotal)\n .attr(\"width\", squareWidth)\n .attr(\"height\", squareWidth); \n return svg;\n}","pinned":false,"mode":"js"},{"id":11,"value":"function getDefsForSVG(svg){\n const defs = d3.select(svg)\n .append(\"defs\");\n \n const filter = defs.append(\"filter\")\n .attr(\"filterUnits\", \"userSpaceOnUse\")\n\t\t.attr(\"id\",\"glow\");\n\n // glow filter as proposed by Nadieh Bremer\n\tfilter.append(\"feGaussianBlur\")\n\t\t.attr(\"class\", \"blur\")\n\t\t.attr(\"stdDeviation\",\"4.5\")\n\t\t.attr(\"result\",\"coloredBlur\");\n\n const feMerge = filter.append(\"feMerge\");\n feMerge.append(\"feMergeNode\")\n .attr(\"in\",\"coloredBlur\");\n feMerge.append(\"feMergeNode\")\n .attr(\"in\",\"SourceGraphic\");\n \n // simple alternative glow filter\n const filter2 = defs.append(\"filter\")\n .attr(\"filterUnits\", \"userSpaceOnUse\")\n\t\t.attr(\"id\",\"glow2\");\n\n\tfilter2.append(\"feGaussianBlur\")\n\t\t.attr(\"class\", \"blur\")\n\t\t.attr(\"stdDeviation\",\"4.5\");\n \n const filter3 = defs.append(\"filter\")\n .attr(\"filterUnits\", \"userSpaceOnUse\")\n\t\t.attr(\"id\",\"glow3\");\n\n\tfilter3.append(\"feGaussianBlur\")\n\t\t.attr(\"class\", \"blur3\")\n\t\t.attr(\"stdDeviation\",\"4.5\");\n \n /*\n filter2.append(\"feColorMatrix\")\n .attr(\"type\", \"matrix\")\n .attr(\"values\", \"1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1.5 0\");\n */\n \n defs\n .append('marker')\n .attr('id', 'arrowhead')\n .attr('viewBox', '-0 -5 10 10')\n // .attr('viewBox', '-0 0 10 15')\n .attr('refX', 13)\n .attr('refY', 0)\n .attr('orient', 'auto')\n .attr('markerWidth', 13)\n .attr('markerHeight',13)\n .attr('xoverflow','visible')\n .append('svg:path')\n .attr('d', 'M 0,-5 L 10 ,0 L 0,5')\n .attr('fill', '#999')\n .style('stroke','none');\n \n return svg;\n}","pinned":false,"mode":"js"},{"id":19,"value":"function squareToPosition(square) { \n const letters = [\"a\",\"b\",\"c\",\"d\",\"e\",\"f\",\"g\",\"h\"];\n const x = (letters.indexOf(square.charAt(0))) * (squareWidth + squarePadding) + squarePadding + squareWidth / 2;\n const y = (7 - (Number(square.charAt(1)) - 1)) * (squareWidth + squarePadding) + squarePadding + squareWidth / 2;\n return {x: x, y: y};\n}","pinned":false,"mode":"js"},{"id":25,"value":"piecePositions = {\n const myMap = new Map();\n let str = \"\";\n let pos = {};\n for (let [key, ar] of pieceMoves) {\n str = \"\";\n ar.forEach( function(ele, index) {\n pos = squareToPosition(ele.square);\n if (index === 0) {\n str += \"M \" + pos.x + \" \" + pos.y;\n } else {\n str += \" L \" + pos.x + \" \" + pos.y;\n }\n myMap.set(key, str);\n });\n };\n return myMap;\n}","pinned":false,"mode":"js"},{"id":27,"value":"pieceMoves = {\n const myMap = new Map();\n \n // initialize\n const pieces = [\"wRa1\", \"wNb1\", \"wBc1\", \"wQd1\", \"wKe1\", \"wBf1\", \"wNg1\", \"wRh1\", \n \"wPa2\", \"wPb2\", \"wPc2\", \"wPd2\", \"wPe2\", \"wPf2\", \"wPg2\", \"wPh2\",\n \"bRa8\", \"bNb8\", \"bBc8\", \"bQd8\", \"bKe8\", \"bBf8\", \"bNg8\", \"bRh8\", \n \"bPa7\", \"bPb7\", \"bPc7\", \"bPd7\", \"bPe7\", \"bPf7\", \"bPg7\", \"bPh7\"\n ];\n let obj, ar;\n \n for (let p of pieces) {\n ar = [];\n obj = {\"square\": p.slice(-2), moves: 0.5, isCaptured: false, \n hasCaptured: false, hasCapturedPiece: \"\", hasEval: 0, hasTimeSpent: 0};\n ar.push(obj);\n myMap.set(p, ar);\n }\n \n // populate with game moves\n let value = {};\n let newValue = {};\n for (let move of currentGame) {\n for (let [key, ar] of myMap) {\n value = ar[ar.length - 1];\n newValue = {};\n if (value.isCaptured) continue; // piece does not exist any more\n if (move.color !== key.charAt(0)) { // wrong color\n if (move.to === value.square) {\n value.isCaptured = true;\n } else {\n value.moves = value.moves + .5;\n }\n ar[ar.length - 1] = value;\n myMap.set(key, ar);\n } else { //same color\n if (move.from === value.square) {\n Object.assign(newValue, obj);\n newValue.square = move.to;\n if (move.captured) {\n newValue.hasCaptured = true;\n newValue.hasCapturedPiece = move.captured.toUpperCase();\n }\n ar.push(newValue);\n } else {\n value.moves = value.moves + .5;\n ar[ar.length - 1] = value;\n }\n myMap.set(key, ar);\n }\n } // end for .. myMap\n } // end for .. game1\n \n return myMap;\n}","pinned":false,"mode":"js"},{"id":35,"value":"currentGame = {\n const chess = new myChess();\n chess.load_pgn(anyPgn);\n \n /*\n // make some moves\n chess.move('e4');\n chess.move('c5');\n chess.move('Nf3');\n chess.move('Nc6');\n chess.move('d4');\n chess.move('cxd4');\n */\n \n return chess.history({ verbose: true });\n}","pinned":false,"mode":"js"},{"id":36,"value":"anyPgn = { \n let lichessAPI;\n let ret;\n let params = new URLSearchParams(window.location.search);\n const game = params.get(\"g\"); \n \n if (!game) {\n if (typeof myGame.message !== \"undefined\") {\n lichessAPI = \"https://lichess.org/game/export/\" + myGame.message.substr(20, 8);\n ret = d3.text(lichessAPI);\n } else {\n ret = myGame;\n }\n } else {\n lichessAPI = \"https://lichess.org/game/export/\" + game.slice(-8);\n ret = d3.text(lichessAPI);\n }\n return ret;\n}","pinned":false,"mode":"js"},{"id":48,"value":"colorOfPieces = { \n const myMap = new Map();\n myMap.set(\"pw\", \"#9ecae1\");\n myMap.set(\"rw\", \"#636363\");\n myMap.set(\"nw\", \"#3182bd\");\n myMap.set(\"bw\", \"#a1d99b\");\n myMap.set(\"qw\", \"#31a354\");\n myMap.set(\"kw\", \"#bdbdbd\");\n \n myMap.set(\"kb\", \"#fdae6b\");\n myMap.set(\"qb\", \"#e6550d\");\n myMap.set(\"bb\", \"#bcbddc\");\n myMap.set(\"nb\", \"#756bb1\");\n myMap.set(\"rb\", \"#fc9272\");\n myMap.set(\"pb\", \"#de2d26\");\n return myMap;\n }","pinned":false,"mode":"js"},{"id":452,"value":"colorOfPieces2 = { \n const myMap = new Map();\n myMap.set(\"pw\", \"#9ecae1\");\n myMap.set(\"rw\", \"#31a354\");\n myMap.set(\"nw\", \"#756bb1\");\n myMap.set(\"bw\", \"#e6550d\");\n myMap.set(\"qw\", \"#de2d26\");\n myMap.set(\"kw\", \"#fdae6b\");\n \n myMap.set(\"kb\", \"#fdae6b\");\n myMap.set(\"qb\", \"#de2d26\");\n myMap.set(\"bb\", \"#e6550d\");\n myMap.set(\"nb\", \"#756bb1\");\n myMap.set(\"rb\", \"#31a354\");\n myMap.set(\"pb\", \"#9ecae1\");\n return myMap;\n }","pinned":false,"mode":"js"},{"id":490,"value":"colorOfPieces3 = { \n const p = \"#e41a1c\";\n const r = \"#377eb8\";\n const n = \"#4daf4a\";\n const b = \"#984ea3\";\n const q = \"#ff7f00\";\n const k = \"#ffff33\";\n \n const myMap = new Map();\n /*\n myMap.set(\"pw\", \"#9ecae1\");\n myMap.set(\"rw\", \"#31a354\");\n myMap.set(\"nw\", \"#756bb1\");\n myMap.set(\"bw\", \"#e6550d\");\n myMap.set(\"qw\", \"#de2d26\");\n myMap.set(\"kw\", \"#fdae6b\");\n \n myMap.set(\"pb\", \"#9ecae1\"); \n myMap.set(\"rb\", \"#31a354\");\n myMap.set(\"nb\", \"#756bb1\");\n myMap.set(\"bb\", \"#e6550d\");\n myMap.set(\"qb\", \"#de2d26\");\n myMap.set(\"kb\", \"#fdae6b\");\n */\n myMap.set(\"pw\", p);\n myMap.set(\"rw\", r);\n myMap.set(\"nw\", n);\n myMap.set(\"bw\", b);\n myMap.set(\"qw\", q);\n myMap.set(\"kw\", k);\n \n myMap.set(\"pb\", p); \n myMap.set(\"rb\", r);\n myMap.set(\"nb\", n);\n myMap.set(\"bb\", b);\n myMap.set(\"qb\", q);\n myMap.set(\"kb\", k);\n \n return myMap;\n }","pinned":false,"mode":"js"},{"id":539,"value":"function positionAt(moveNr) {\n const chess = new myChess(); \n currentGame.forEach((e, i) => { if (i < moveNr) { chess.move(e.san); } });\n \n const rows = [1,2,3,4,5,6,7,8];\n const cols = [\"a\", \"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"];\n \n let piece, square;\n const prefix = \"https://cdn.jsdelivr.net/gh/oakmac/chessboardjs/website/img/chesspieces/wikipedia/\";\n const pieces = [];\n \n for(let r = 0; r < rows.length; r++){\n for(let c = 0; c < cols.length; c++){\n square = cols[c] + rows[r];\n piece = chess.get(square);\n if (piece) { \n let p = {pieceLink: prefix + [piece.color] + piece.type.toUpperCase() + \".png\"\n , piece: piece.type + piece.color\n , x: squareToPosition(square).x\n , y: squareToPosition(square).y};\n pieces.push(p);\n }\n }\n }\n \n return pieces;\n}","pinned":false,"mode":"js"},{"id":254,"value":"function rotate(d) {\n const xFrom = squareToPosition(d.from).x;\n const xTo = squareToPosition(d.to).x;\n if (xTo < xFrom) { \n const rx = xTo + ((xFrom - xTo) / 2);\n const yFrom = squareToPosition(d.from).y;\n const yTo = squareToPosition(d.to).y;\n const ry = (yTo > yFrom) \n ? yFrom + ((yTo - yFrom) / 2)\n : yTo + ((yFrom - yTo) / 2);\n return 'rotate(180 ' + rx + ' ' + ry + ')';\n }\n else {\n return 'rotate(0)';\n }\n}","pinned":false,"mode":"js"},{"id":347,"value":"function version8() { \n // const svg = drawChessboard();\n const svg = drawChessboard({\"white\": \"white\", \"black\": \"lightgrey\"});\n const dataArray = currentGame;\n \n // paths\n const sel1 = d3.select(svg)\n .selectAll(\"path.piece-move\")\n .data(dataArray);\n \n sel1.join(\"path\")\n .attr(\"class\", \"piece-move\")\n .attr(\"d\", (d, i) => {\n let str = {};\n const dx = (i % 2) ? -2 : 2;\n const dy = (i % 2) ? -2 : 2;\n str = \"M \" + (squareToPosition(d.from).x + dx) + \" \" + (squareToPosition(d.from).y + dy);\n str += \" L \" + (squareToPosition(d.to).x + dx) + \" \" + (squareToPosition(d.to).y + dy);\n return str;\n })\n //.style(\"stroke\", (d, i) => (i % 2) ? \"steelblue\" : \"orange\")\n .style(\"stroke\", (d, i) => (i % 2) ? \"white\" : \"black\")\n .style(\"stroke-width\", squareWidth / 60 * 7)\n .style(\"stroke-linejoin\", \"round\")\n .style(\"opacity\", (d,i) => (i > 40 && i < 45) ? 0.9 : 0.2)\n .style(\"fill\", \"none\");\n \n sel1.join(\"path\")\n .attr(\"class\", \"piece-move\")\n .attr(\"d\", (d, i) => {\n let str = {};\n const dx = (i % 2) ? -2 : 2;\n const dy = (i % 2) ? -2 : 2;\n str = \"M \" + (squareToPosition(d.from).x + dx) + \" \" + (squareToPosition(d.from).y + dy);\n str += \" L \" + (squareToPosition(d.to).x + dx) + \" \" + (squareToPosition(d.to).y + dy);\n return str;\n })\n .style(\"stroke\", (d, i) => (i % 2) ? \"black\" : \"white\")\n .style(\"stroke-width\", squareWidth / 60 * 5)\n .style(\"stroke-linejoin\", \"round\")\n .style(\"opacity\", (d,i) => (i > 40 && i < 45) ? 0.9 : 0.2)\n .style(\"fill\", \"none\");\n \n \n // pieces from, to\n const sel2 = d3.select(svg)\n .selectAll(\"circle\")\n .data(dataArray);\n \n sel2\n .join(\"circle\")\n .attr(\"class\", \"piece-square from\")\n .style(\"fill\", (d, i) => (i % 2) ? \"black\" : \"white\")\n .attr(\"cx\", (d, i) => {\n const dx = (i % 2) ? -2 : 2;\n return squareToPosition(d.from).x + dx;\n })\n .attr(\"cy\", (d, i) => {\n const dy = (i % 2) ? -2 : 2;\n return squareToPosition(d.from).y + dy;\n })\n .attr(\"r\", squareWidth / 60 * 8); \n\n sel2\n .join(\"circle\")\n .attr(\"class\", \"piece-square to\")\n .style(\"fill\", (d, i) => d.captured ? ((i % 2) ? \"white\" : \"black\") : (i % 2) ? \"black\" : \"white\")\n .style(\"stroke\", (d, i) => d.captured ? ((i % 2) ? \"black\" : \"white\") : \"none\")\n .style(\"stroke-width\", 6)\n .attr(\"cx\", (d, i) => {\n const dx = (i % 2) ? -2 : 2;\n return squareToPosition(d.to).x + dx;\n })\n .attr(\"cy\", (d, i) => {\n const dy = (i % 2) ? -2 : 2;\n return squareToPosition(d.to).y + dy;\n })\n .attr(\"r\", squareWidth / 60 * 8); \n \n const imageWidth = squareWidth * .7;\n const sel3 = d3.select(svg)\n .selectAll(\"image\")\n .data(dataArray);\n \n sel3\n .join(\"image\")\n //.filter(d => d.piece === \"b\")\n .attr(\"class\", \"piece-square from\")\n .attr(\"xlink:href\", (d, i) => {\n const prefix = \"https://cdn.jsdelivr.net/gh/oakmac/chessboardjs/website/img/chesspieces/wikipedia/\";\n const piece = prefix + d.color + d.piece.toUpperCase() + \".png\";\n return piece;\n })\n .style(\"opacity\", (d,i) => (i > 40 && i < 45) ? 0.9 : 0.2)\n .attr(\"x\", d => squareToPosition(d.from).x - imageWidth / 2)\n .attr(\"y\", d => squareToPosition(d.from).y - imageWidth / 2)\n .attr(\"height\", imageWidth)\n .attr(\"width\", imageWidth);\n\n \n d3.select(svg).selectAll(\".piece-move\")\n // .style(\"filter\",\"url(#glow)\");\n .style(\"filter\",\"url(\" + window.location + \"#glow)\");\n \n return svg;\n}","pinned":false,"mode":"js"}]} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment