본문으로 이동

모듈:Chess/board

위키책, 위키책

이 모듈에 대한 설명문서는 모듈:Chess/board/설명문서에서 만들 수 있습니다

local p = {}

local function pgnToFen(pgn)
	--Tries to use Pgn2fen module to convert pgn to fen.
    local success, result = pcall(function()
        local moves = require('Module:Pgn2fen').pgn2fen(pgn)
        return moves[#moves] --returns fen if pgn2fen worked
    end)

    if success then
        return result
    else
		--If it throws an error (invalid pgn), handle this gracefully and return 
		--an empty string
        return ''
    end
end

--From [[w:Module:Chessboard]] via [[Module:Chess]]
local function convertFenToArgs( fen )
	-- converts FEN notation to 64 entry array of positions
	
	local res = { }
	
	-- Loop over rows, which are delimited by /
	for srow in string.gmatch( "/" .. fen, "/%w+" ) do
		-- Loop over all letters and numbers in the row
		for piece in srow:gmatch( "%w" ) do
			if piece:match( "%d" ) then -- if a digit
				for k=1,piece do
					table.insert(res,' ')
				end
			else -- not a digit
				local color = piece:match( '%u' ) and 'l' or 'd'
				piece = piece:lower()
				table.insert( res, piece .. color )
			end
		end
	end

	return res
end

--takes 64 values each corresponding to a chessboard square
--and prints them out in divs to be rendered into a chessboard by css magic.
local function renderBoardFromArgs(args, size, captionText, float, border, scheme)
	
	--sanitise optional parameters.
	size = tonumber(size) or 1
	local allowedFloats = {
		none = true,
		left = true,
		right = true,
		["inline-start"] = true,
		["inline-end"] = true,
		center = true
	}
	float = allowedFloats[float] and float or 'none'
	
	local allowedSchemes = {
		orange = true,
		bw = true,
		green = true,
		brown = true
	}
	scheme = allowedSchemes[scheme] and scheme or 'brown'
	
	if border == 'true' or border == '1' then
    	border = true
	else
    	border = false
	end
	
	local html = mw.html.create('div')
	local container
	
	if border then
	    html:addClass('chessboard-frame')
	        :css({
	            ['font-size'] = size .. 'em'
	        })
        if float == "center" then 
        	html:css({['margin'] = '0 auto'})
        else
        	html:css({['float'] = float})
        end
	
	    local marginStyles = {}
	    if float ~= 'none' and float ~= 'center' then
	        marginStyles['margin-bottom'] = '1.3em'
	    end
	    if float == 'left' then
	        marginStyles['margin-right'] = '1.4em'
	    elseif float == 'right' then
	        marginStyles['margin-left'] = '1.4em'
	    end
	    html:css(marginStyles)
	
	    container = html:tag('div')
	        :addClass('chessboard-container')
	else
	    container = html
	        :addClass('chessboard-container')
	        :css({
	            ['font-size'] = size .. 'em',
	            ['width'] = 'fit-content'
	        })
	    if float == "center" then 
        	container:css({['margin'] = '0 auto'})
        else
        	container:css({['float'] = float})
        end
	
	    local marginStyles = {}
	    if float ~= 'none' and float ~= 'center' then
	        marginStyles['margin-bottom'] = '1.3em'
	    end
	    if float == 'left' then
	        marginStyles['margin-right'] = '1.4em'
	    elseif float == 'right' then
	        marginStyles['margin-left'] = '1.4em'
	    end
	    container:css(marginStyles)
	end

    -- Top file labels
    local fileTop = container:tag('div'):addClass('file-labels-top')
    for _, file in ipairs({ 'a','b','c','d','e','f','g','h' }) do
        fileTop:tag('div'):wikitext(file)
    end

    -- Left rank labels
    local rankLeft = container:tag('div'):addClass('rank-labels-left')
    for i = 8, 1, -1 do
        rankLeft:tag('div'):wikitext(i)
    end

    -- Chessboard grid
    local board = container:tag('div'):addClass('chess-grid')
    for i = 1, 64 do
        local frame = mw.getCurrentFrame()
        local piece = args[i] or ''
		    
	    local square = board:tag('div')
	        :addClass('chess-square')
	        :addClass(scheme)
	        square:tag('div')
	        	:addClass('piece')
	            :addClass('piece-' .. piece)
    end

    -- Right rank labels
    local rankRight = container:tag('div'):addClass('rank-labels-right')
    for i = 8, 1, -1 do
        rankRight:tag('div'):wikitext(i)
    end

    -- Bottom file labels
    local fileBottom = container:tag('div'):addClass('file-labels-bottom')
    for _, file in ipairs({ 'a','b','c','d','e','f','g','h' }) do
        fileBottom:tag('div'):wikitext(file)
    end
    
    if captionText ~= '' then
    	if border then
			html:tag('div'):addClass('chessboard-caption')
				:wikitext(captionText)
		else
			container:tag('div'):addClass('chessboard-caption')
				:wikitext(captionText)
		end
    end

    return tostring(html)
end

local function pagename2pgn(pagename)

	local path = pagename:match("^Chess Opening Theory/(.+)")
	if not path then
		return ""
	end
	
	local line = path:gsub("/", " ") --convert slashes to spaces
	line = mw.text.trim(line)

	return line
end

function p.pagename(frame) --for calling externally
	local pagename = frame.args[1]

	if not pagename or pagename == '' then
		pagename = mw.title.getCurrentTitle().text
	end
	
	local path = pagename:match("^Chess Opening Theory/(.+)")
	if not path then
		return ""
	end
	
	local line = path:gsub("/", " "):gsub("[0-9]+%.%.%.", "")
	line = mw.text.trim(line)

	return pgnToFen(line)
end

--computeChessboard takes either a fen or a pgn (moves)
--converts them to table of 64 values each corresponding to a chessboard square
--and sends them to renderBoardFromArgs() to be rendered.
function p.computeChessboard(frame)
	
	local fen = frame.args.fen or ''
	local moves = frame.args.moves or ''
    
	local parentArgs = frame:getParent().args
	local size = parentArgs.size or '1'
	local captionText = parentArgs.caption or ''
	local float = parentArgs.float or 'none'
	local border = parentArgs.frame or false
	local scheme = parentArgs.scheme or 'brown'
	
	--specifying moves overrides provided fen
	if moves ~= '' then
        fen = pgnToFen(moves)
	elseif fen == '' then 
		local pagenameFEN = pagename2pgn(mw.title.getCurrentTitle().text)
		if pagenameFEN ~= '' then fen = pgnToFen(pagenameFEN) end --defaults to page title
	end

    if fen == '' then
    	fen = 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR'	--fallback to starting pos.
    end
    
    local args = convertFenToArgs(fen)
    return renderBoardFromArgs(args, size, captionText, float, border, scheme)

end

--renderChessboard allows renderBoardFromArgs() to be called indirectly 
--from outside the module and passed a converted fen directly
function p.renderChessboard(frame)
	--if it looks like it is being passed arguments
	if frame.args[1] and frame.args[1] ~= '' then
		return renderBoardFromArgs(frame.args)
	else
		--otherwise default to showing the starting position
		return renderBoardFromArgs(convertFenToArgs('rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR'))
	end
end

return p