first commit
This commit is contained in:
@@ -0,0 +1,6 @@
|
|||||||
|
chrome.action.onClicked.addListener((tab) => {
|
||||||
|
chrome.scripting.executeScript({
|
||||||
|
target: { tabId: tab.id },
|
||||||
|
files: ["content.js"]
|
||||||
|
});
|
||||||
|
});
|
||||||
+163
@@ -0,0 +1,163 @@
|
|||||||
|
// ## kif.js
|
||||||
|
|
||||||
|
// KIF is the most popular file format used to represent full shogi games,
|
||||||
|
// studies of games, tsume problems and other types of puzzles.
|
||||||
|
// For more information, see https://lishogi.org/page/kif and
|
||||||
|
// https://deepwiki.com/gunyarakun/python-shogi/3.1-kif-format
|
||||||
|
|
||||||
|
const pieceMap = {
|
||||||
|
P: "歩",
|
||||||
|
L: "香",
|
||||||
|
N: "桂",
|
||||||
|
S: "銀",
|
||||||
|
G: "金",
|
||||||
|
B: "角",
|
||||||
|
R: "飛",
|
||||||
|
K: "玉",
|
||||||
|
"+P": "と",
|
||||||
|
"+L": "成香",
|
||||||
|
"+N": "成桂",
|
||||||
|
"+S": "成銀",
|
||||||
|
"+B": "馬",
|
||||||
|
"+R": "竜"
|
||||||
|
};
|
||||||
|
|
||||||
|
const japaneseNumberMap = {
|
||||||
|
1: "一",
|
||||||
|
2: "二",
|
||||||
|
3: "三",
|
||||||
|
4: "四",
|
||||||
|
5: "五",
|
||||||
|
6: "六",
|
||||||
|
7: "七",
|
||||||
|
8: "八",
|
||||||
|
9: "九"
|
||||||
|
};
|
||||||
|
|
||||||
|
function kifCoord(square) {
|
||||||
|
const file = square[0];
|
||||||
|
const rank = square[1];
|
||||||
|
return `${file}${japaneseNumberMap[rank]}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A struct to hold the source or destination square of a move
|
||||||
|
// file and rank are numbers from 1-9
|
||||||
|
const Square = (file, rank) => ({ file, rank });
|
||||||
|
|
||||||
|
function parseSquare(square) {
|
||||||
|
// square is a string like "7g"
|
||||||
|
// first character is the file (1-9), second character is the rank (a-i)
|
||||||
|
const file = parseInt(square[0], 10);
|
||||||
|
const rank = "abcdefghi".indexOf(square[1]) + 1;
|
||||||
|
return Square(file, rank);
|
||||||
|
}
|
||||||
|
|
||||||
|
function toKIF(line, prevDest) {
|
||||||
|
// move number
|
||||||
|
const move = line.replace(/^\d+:\s*/, "").trim();
|
||||||
|
|
||||||
|
// gote/sente player indicator
|
||||||
|
const player = move[0] === "☗" ? "▲" : "△";
|
||||||
|
var body = move.slice(1);
|
||||||
|
|
||||||
|
// piece
|
||||||
|
if (move[0] === "+") { // promoted piece
|
||||||
|
// first two characters are the piece, then the destination
|
||||||
|
const piece = body.slice(0, 2);
|
||||||
|
body = body.slice(2);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// first character is the piece, then the destination
|
||||||
|
const piece = body[0];
|
||||||
|
body = body.slice(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// source square (optional)
|
||||||
|
if (body[0] >= "1" && body[0] <= "9") {
|
||||||
|
const sourceSquare = body.slice(0, 2);
|
||||||
|
body = body.slice(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// move type
|
||||||
|
moveType = body[0]; // either '-' for a normal move, 'x' for a capture, or '*' for a drop
|
||||||
|
body = body.slice(1);
|
||||||
|
|
||||||
|
// destination square
|
||||||
|
destSquare = body.slice(0, 2);
|
||||||
|
body = body.slice(2);
|
||||||
|
|
||||||
|
if (body.length > 0) {
|
||||||
|
if (body[0] === "+" || body[0] === "=") {
|
||||||
|
const promoFlag = body[0];
|
||||||
|
body = body.slice(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// to KIF format
|
||||||
|
var kifMove = `${player}${kifCoord(destSquare)}${pieceMap[piece]}${moveType === "*" ? "打" : ""}`;
|
||||||
|
if (promoFlag) {
|
||||||
|
kifMove += promoFlag === "+" ? "成" : "不成";
|
||||||
|
}
|
||||||
|
return kifMove;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ## content.js
|
||||||
|
|
||||||
|
// This module provides functions to convert the shogiplayground
|
||||||
|
// (https://play.mogproject.com/) format to KIF.
|
||||||
|
|
||||||
|
(() => {
|
||||||
|
const sidebar = document.querySelector("div.sidebar-left-content");
|
||||||
|
if (!sidebar) {
|
||||||
|
alert("Sidebar not found!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const branchSelect = sidebar.querySelector("select");
|
||||||
|
const copyButton = sidebar.querySelector("button");
|
||||||
|
if (!branchSelect || !copyButton) {
|
||||||
|
alert("Branch selector or copy button not found!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const results = [];
|
||||||
|
|
||||||
|
const collectMoves = async () => {
|
||||||
|
for (let i = 0; i < branchSelect.options.length; i++) {
|
||||||
|
branchSelect.selectedIndex = i;
|
||||||
|
branchSelect.dispatchEvent(new Event("change"));
|
||||||
|
await new Promise(r => setTimeout(r, 200));
|
||||||
|
|
||||||
|
const text = copyButton.getAttribute("data-clipboard-text") || "";
|
||||||
|
let prevDest = null;
|
||||||
|
const lines = text.split("\n").map(l => {
|
||||||
|
const kif = toKIF(l, prevDest);
|
||||||
|
if (kif) {
|
||||||
|
const match = kif.match(/^[▲△](同|\d[一二三四五六七八九])/);
|
||||||
|
if (match) prevDest = match[1] === "同" ? prevDest : match[0].slice(1);
|
||||||
|
}
|
||||||
|
return kif;
|
||||||
|
}).filter(Boolean);
|
||||||
|
|
||||||
|
const label = branchSelect.options[i].textContent.trim();
|
||||||
|
results.push(`${label}:\n${lines.join("\n")}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const default_filename = "shogiplayground-plugin.kif";
|
||||||
|
|
||||||
|
collectMoves().then(() => {
|
||||||
|
const finalText = results.join("\n\n");
|
||||||
|
const blob = new Blob([finalText], { type: "text/plain" });
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
const a = document.createElement("a");
|
||||||
|
a.href = url;
|
||||||
|
a.download = default_filename;
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
document.body.removeChild(a);
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
|
||||||
|
alert(`All branches exported to ${default_filename}!`);
|
||||||
|
});
|
||||||
|
})();
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"manifest_version": 3,
|
||||||
|
"name": "ShogiPlayground Plugin",
|
||||||
|
"version": "1.0",
|
||||||
|
"description": "Copies all branches' moves from Shogi Playground into the clipboard.",
|
||||||
|
"permissions": [
|
||||||
|
"scripting",
|
||||||
|
"activeTab"
|
||||||
|
],
|
||||||
|
"background": {
|
||||||
|
"service_worker": "background.js"
|
||||||
|
},
|
||||||
|
"action": {
|
||||||
|
"default_title": "Copy All Branches"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
https://www.reddit.com/r/shogi/comments/9ngjw8/book_lines_only_based_on_shikenbisha_joseki/
|
||||||
Reference in New Issue
Block a user