모듈:체스/이론표
보이기
이 모듈에 대한 설명문서는 모듈:체스/이론표/설명문서에서 만들 수 있습니다
local p = {}
--re-written for this purpose, but originally from [[Module:Chess]]
local function pagename2pgn(pagename)
local path = pagename:match("^Chess Opening Theory/(.+)")
if not path then
return ""
end
--clean-up so that parseMoves() can interpet
local line = path:gsub("/", " ") --convert slashes to spaces
line = line:gsub("%d+%.%.%.", "") -- removes 1... etc
line = line:gsub("%d+%." , "") -- removes 1. etc.
line = mw.text.trim(line)
return line
end
local function parseMoves(line)
local moves = {}
line = line or '' --prevents failure if line is nil
--black to move: N... -> ...
line = line:gsub("(%d+)%.%.%.", "... ")
for token in line:gmatch("%S+") do
--removes move number
if not token:match("^%d+%.%.?") then
table.insert(moves, token)
end
end
return moves
end
local function deparseMoves(moves)
local output = {}
local move_number = 1
for i = 1, #moves, 2 do
local white = moves[i]
local black = moves[i + 1]
if black then
table.insert(output, string.format("%d. %s %s", move_number, white, black))
else
table.insert(output, string.format("%d. %s", move_number, white))
end
move_number = move_number + 1
end
return table.concat(output, " ")
end
-- Reconstruct full move path leading up to the divergent move
local function collectParentMoves(lineIndex, lines)
local currentLine = lines[lineIndex]
local parentMoves = {}
-- Number of moves in this line
local numMoves = #currentLine.moves
-- Walk through each move position in currentLine
for pos = 1, numMoves do
local move = currentLine.moves[pos]
if move ~= "..." then
-- We found the divergent move at this position; include it and stop here
table.insert(parentMoves, move)
break
else
-- move == "...", so look backwards to find first explicit move at this position
local found = false
for prevIndex = lineIndex - 1, 1, -1 do
local prevLine = lines[prevIndex]
if prevLine and prevLine.moves[pos] and prevLine.moves[pos] ~= "..." then
table.insert(parentMoves, prevLine.moves[pos])
found = true
break
end
end
end
end
return parentMoves
end
local function renderTheoryTable(lines, startingMove, precedingMoves, links)
local output = {}
startingMove = tonumber(startingMove) - 1
-- Get max number of half moves across all lines
local max_moves = 0
for _, line in ipairs(lines) do
if #line.moves > max_moves then
max_moves = #line.moves
end
end
local full_move_columns = math.ceil(max_moves / 2)
table.insert(output,'<strong>' .. deparseMoves(precedingMoves) .. '</strong>')
table.insert(output, '{| cellspacing="0" cellpadding="4"')
table.insert(output, '|- align="left"')
local header = { "! " }
local isWhiteToMove = (#precedingMoves % 2 == 0)
local columnStart = startingMove + (isWhiteToMove and 1 or 0)
for i = 1, full_move_columns do
table.insert(header, string.format("%d", i + columnStart))
end
table.insert(header, " ")
table.insert(output, table.concat(header, " !! "))
table.insert(output, "|-")
for rowIndex, line in ipairs(lines) do
local row = {}
table.insert(row, string.format('| align="right"| <strong>%s</strong>', line.name or ""))
--the first original move in each variation should be linked.
--firstlink keeps track of whether the 1st original move has been found
--yet for each line
local firstLinked = false
local j = 0 --halfmove counter
for i = 1, max_moves, 2 do --advances in two half moves, i.e. one full turn
local white = line.moves[i] or ""
local black = line.moves[i + 1] or ""
local cell = ""
--half=0=white's half move, half=1=black's half move
for half = 0, 1 do
local move = (half == 0) and white or black --white move if half=0, otherwise black
if move ~= "" then --if line contains a halfmove here (some variations may end earlier than others)
j = j + 1 --counts halfmoves for this line
--if we are at the 1st original move
if not firstLinked and move ~= "..." then
firstLinked = true
--build pgn up to this move. This is to generate the
--article name for the link
local pgn_path = {}
local parent_line = lines[line.parent]
local j2 = j --used for creating the link url. adjusted
--if it's black to move.
local all_moves = collectParentMoves(rowIndex, lines)
--if we had to skip the first move
if parent_line and parent_line.moves[1] == "..." then
j2 = j2 - 1
end
--format link path
for m = 1, #all_moves do
local turn = math.ceil((m + #precedingMoves) / 2)
local suffix = (m + #precedingMoves) % 2 == 1 and (turn .. ". ") or (turn .. "...")
--remove annotations from past moves for the new article name
local clean_move = mw.ustring.gsub(all_moves[m], "[!?+#]+", "")
table.insert(pgn_path, suffix .. clean_move)
end
--add divergent move to link path
local this_turn = math.ceil((j2 + #precedingMoves) / 2)
local this_suffix = (j2 + #precedingMoves) % 2 == 1 and (this_turn .. ". ") or (this_turn .. "...")
--remove annotations from the divergent move for the new article name only
local clean_move = mw.ustring.gsub(move, "[!?+#]+", "")
-- Use clean_move for the path, but keep move (with annotations) for display
--table.insert(pgn_path, this_suffix .. clean_move)
local link = "[[/" .. table.concat(pgn_path, "/") .. "|" .. move .. "]]"
if links then move = link end
end
cell = cell .. (cell ~= "" and "<br>" or "") .. move
end
end
table.insert(row, "|| " .. cell)
end
table.insert(row, "|| " .. (line.eval or ""))
table.insert(output, table.concat(row, " "))
table.insert(output, "|-")
end
table.insert(output, "|}")
return table.concat(output, "\n")
end
function p.parse(frame)
local args = frame:getParent().args
local lines = {}
local links = args.links or true
if links == 'true' or links == '1' then
links = true
elseif links == 'false' or links == '0' then
links = false
end
local startingMove = tonumber(args.beginning) or 0
local precedingMoves
if args.preceding and args.preceding ~= "" then
precedingMoves = parseMoves(args.preceding)
else
precedingMoves = parseMoves(pagename2pgn(mw.title.getCurrentTitle().text))
end
if startingMove == 0 then
startingMove = math.ceil(#precedingMoves / 2)
end
for key, val in pairs(args) do
--filters just for supplied parameters with names like lineN
local index = key:match("^line(%d+)$")
if index and val and val ~= "" then
local name = args["name" .. index] or ""
local eval = args["eval" .. index] or ""
local moves = parseMoves(val)
--determine how deep a variation is by counting
--leading "..." as dotcount
local dotcount = 0
for _, move in ipairs(moves) do
if move == "..." then
dotcount = dotcount + 1
else
break
end
end
--adds each line as a new row in the lines datafame.
table.insert(lines, {
index = tonumber(index),
name = name,
moves = moves,
eval = eval,
dotcount = dotcount,
parent = nil -- to be set later
})
end
end
-- Sort by line number
table.sort(lines, function(a, b)
return a.index < b.index
end)
--determine variation parenthood, based on most recent line with fewer dots
--Set parent line for each line based on dotcount
for i, line in ipairs(lines) do
for j = i - 1, 1, -1 do
if lines[j].dotcount < line.dotcount then
line.parent = j
break
end
end
-- fallback: assume top-level if no parent found
if not line.parent then
line.parent = i
end
end
-- Debug output
--local debug = {}
--for _, row in ipairs(lines) do
-- local summary = string.format("[%s] %s | %s | %s", row.index, row.name or "(no name)", table.concat(row.moves, " "), row.eval)
-- table.insert(debug, summary)
--end
return renderTheoryTable(lines,startingMove,precedingMoves,links)
end
return p