Модуль:Произвольная шахматная доска

Документация
local p = {}

function chessboard(args, size, rows, cols, offsetx, offsety, rev, trans, lightdark, altprefix, letters, numbers, header, footer, align, clear, noframe, numeration)
    function colchar( col )
        return (col <= 26) and ( "abcdefghijklmnopqrstuvwxyz" ):sub( col, col ) 
        	or ( "abcdefghijklmnopqrstuvwxyz" ):sub( math.floor((col-1)/26), math.floor((col-1)/26) ) 
        		.. ( "abcdefghijklmnopqrstuvwxyz" ):sub( col-math.floor((col-1)/26)*26, col-math.floor((col-1)/26)*26)
    end
    function image_square(pc, row, col, offsetx, offsety, size, t, flip, altprefix, numeration)
        local colornames = { l = 'бел', d = 'чёрн'}
        local piecenames = { -- при добавлении фигур необходимо определить их грамматический род, добавив в соответствующую строку (ниже по тегу #gender)
		p = 'пешка',
		r = 'ладья',
		n = 'конь',
		b = 'слон',
		q = 'ферзь',
		k = 'король',
		a = 'архиепископ',
		c = 'канцлер',
		z = 'чемпион',
		w = 'колдун',
		t = 'шут',
		h = 'перевёрнутая пешка',
		m = 'перевёрнутая ладья',
		s = 'перевёрнутый конь',
		f = 'перевёрнутый король',
		e = 'перевёрнутый слон',
		g = 'перевёрнутый ферзь',
		G = 'жираф',
		U = 'единорог',
		Z = 'зебра'
            }
        local symnames = { 
		xx = 'чёрный крест',
		ox = 'белый крест',
		xo = 'чёрный круг',
		oo = 'белый круг',
		ul = 'стрелка вверх-влево',
		ua = 'стрелка вверх',
		ur = 'стрелка вверх-вправо',
		la = 'стрелка влево',
		ra = 'стрелка вправо',
		dl = 'стрелка вниз-влево',
		da = 'стрелка вниз',
		dr = 'стрелка вниз-вправо',
		lr = 'стрелка влево-вправо',
		ud = 'стрелка вверх-вниз',
		db = 'стрелки вверх-вправо и вниз-влево',
		dw = 'стрелки вверх-влево и вниз-вправо',
		x0 = 'ноль',
		x1 = 'один',
		x2 = 'два',
		x3 = 'три',
		x4 = 'четыре',
		x5 = 'пять',
		x6 = 'шесть',
		x7 = 'семь',
		x8 = 'восемь',
		x9 = 'девять',
		j0 = 'чёрная шашка',
		j1 = 'белая шашка',
		D0 = 'чёрная дамка',
		D1 = 'белая дамка',
		O0 = 'чёрный круг',
		O1 = 'белый круг',
		O2 = 'красный круг',
		O3 = 'зелёный круг',
		O4 = 'синий круг',
		O5 = 'бирюзовый круг',
		O6 = 'жёлтый круг',
		O7 = 'розовый круг',
		O8 = 'коричневый круг',
		O9 = 'фиолетовый круг',
    	}
    	if (pc=='x') then pc = 'j0' end
    	if (pc=='o') then pc = 'j1' end
    	if (pc=='X') then pc = 'D0' end
    	if (pc=='O') then pc = 'D1' end
        local color = mw.ustring.gsub( pc, '^.*(%w)(%w).*$', '%2' ) or ''
        local piece = mw.ustring.gsub( pc, '^.*(%w)(%w).*$', '%1' ) or ''
        local ld = t and 't' or ((((row + col + flip) % 2) == 0) and 'd' or 'l')
        local alt = ''
        if (numeration=='chess') then alt = alt .. colchar( col+offsetx ) .. row+offsety .. ' ' -- нумерация при наведении на клетку
        elseif (numeration=='continuous') then alt = alt .. col+(rows-row)*cols .. ' '
        elseif (numeration=='draughts') then
    	if (ld=='d') then alt = alt ..  math.floor((col+(rows-row)*cols+1)/2) .. ' ' end
		elseif (numeration=='swapdraughts') then
		if (ld=='l') then alt = alt ..  math.floor((col+(rows-row)*cols+1)/2) .. ' ' end
		end
        if ( colornames[color] and piecenames[piece] ) then -- Согласовываем род прилагательного, обозначающего цвет #gender
        		alt = alt .. colornames[color]
        		if (string.find('nbqkaczwtsfegGU',piece)) then alt = alt .. 'ый ' -- Коды фигур мужского рода
    				elseif (string.find('prhmZ',piece)) then alt = alt .. 'ая ' -- Коды фигур женского рода
    					else alt = alt ..'. ' end
        		alt = alt .. piecenames[piece]
  		else
            alt = alt .. ( symnames[piece .. color] or piece .. ' ' .. color )
        end
       
        
        return string.format( '[[File:Chess %s%s%s45.svg|%dx%dpx|alt=%s|%s]]', piece, color, ld, size, size, alt, alt)
    end

    function letters_row( rev, num_lt, num_rt, cols, rows, offsetx, numeration, rev, lightdark, bottomline)
    	blisblack = 1
    	if (lightdark==true) then blisblack = 1 - blisblack end
    	if (((cols+rows)%2==1)and(rev==true)) then blisblack = 1 - blisblack end
       	tlisblack = blisblack
    	if (rows%2==0) then tlisblack = 1- blisblack end
       local res = '<tr style="vertical-align:middle">' .. ( num_lt and '<td style="padding:0; vertical-align:inherit"></td>' or '' ) .. '<td style="padding:0; vertical-align:inherit; height:18px">'
        for k = 1, cols do
        
     	if (numeration=='chess') then res = res .. colchar(rev and (cols - k + 1+offsetx) or k+offsetx) 	-- шахматы
    		elseif (numeration=='continuous') then 												   			-- сквозная
    			if (rev) then res = res .. (cols-k+1)+(1-bottomline)*(rows-1)*cols
    				else res = res .. k+bottomline*(cols*(rows-1)) end
    		elseif (numeration=='draughts') then																--черные
    			if not(rev) then
    			if (bottomline==0)and(k % 2 == tlisblack) then res = res ..  math.floor((k+1)/2) 							-- верх
				elseif (bottomline==1)and(k % 2 == blisblack) then res = res ..  math.floor((cols*(rows-1)+k+1)/2) end		-- низ
				else
					if (bottomline==0)and(k % 2 == tlisblack) then res = res ..  math.floor((rows*cols-k)/2)+1			-- верх
				elseif (bottomline==1)and(k % 2 == blisblack) then res = res ..  math.floor((cols-k)/2)+1 end			-- низ
					end
				elseif (numeration=='swapdraughts') then															--белые
				if not(rev) then
    			if (bottomline==0)and not(k % 2 == tlisblack) then res = res .. math.floor((k+1)/2)								--верх
				elseif (bottomline==1)and not (k % 2 == blisblack) then res = res ..  math.floor((cols*(rows-1)+k+1)/2) end		-- низ
				else
				if (bottomline==0)and not (k % 2 == tlisblack) then res = res ..  math.floor((rows*cols-k)/2)+1			-- верх
				elseif (bottomline==1)and not (k % 2 == blisblack) then res = res ..  math.floor((cols-k)/2)+1 end			-- низ
				end
			end

            res = res ..'</td><td style="padding:0; vertical-align:inherit">'
        end
        res = res .. '</td>' .. ( num_lt and '<td style="padding:0; vertical-align:inherit"></td>' or '' ) .. '</tr>'
        return res
    end
    local letters_tp = letters:match('both') or letters:match('top')
    local letters_bt = letters:match('both') or letters:match('bottom')
    local numbers_lt = numbers:match('both') or numbers:match('left')
    local numbers_rt = numbers:match('both') or numbers:match('right')
    local width = cols * size + 2
    local flip = lightdark and 1 or 0
    if ( numbers_lt ) then width = width + 18 end
    if ( numbers_rt ) then width = width + 18 end

    local b = ''
    local caption = ''
    if ( letters_tp ) then b = b .. letters_row(rev, numbers_lt, numbers_rt, cols, rows, offsetx, numeration, rev, lightdark, 0) .. '\n' end -- 1 - флаг верхней строки для шашечной нумерации
    for trow = 1,rows do
        local row = rev and trow or (rows - trow + 1)
        b = b .. '<tr style="vertical-align:middle">'
      if ( numbers_lt ) then b = b .. '<td style="padding:0; vertical-align:inherit; width:18px">' -- левая колонка цифр
        	if (numeration=='chess') then b = b .. row+offsety
    		elseif (numeration=='continuous') then
    			if not(rev) then b = b .. (trow-1)*cols+1
    				else b = b .. (rows-trow+1)*cols end
    		elseif (numeration=='draughts') then 
    			if (trow%2==tlisblack) then 
    				if not(rev) then b = b .. math.floor((trow-1)*cols/2+1)
					else
					b = b .. math.floor((cols*(rows-trow+1)+1)/2)
					end
				end
		elseif (numeration=='swapdraughts') then
			if not (trow%2==tlisblack) then 
    				if not(rev) then b = b .. math.floor((trow-1)*cols/2+1)
					else
					b = b .. math.floor((cols*(rows-trow+1)+1)/2)
					end
			end
			end
        	b = b .. '</td>'
        	end
        for tcol = 1,cols do
            local col = rev and (cols - tcol + 1) or tcol
            local idx = cols*(rows - row) + col + 2
            if (args[idx] == nil) then args[idx] = '  ' end
            local img = image_square((args[idx]:match('%w%w') or args[idx]:match('%w')) or '', row, col, offsetx, offsety, size, trans, flip, altprefix, numeration)
            local bg = (((trow + tcol + flip) % 2) == 0) and '#ffce9e' or '#d18b47'
            b = b .. '<td style="padding:0; vertical-align:inherit; background-color: ' .. bg .. ';">' .. img .. '</td>'
        end
			if ( numbers_rt ) then b = b .. '<td style="padding:0; vertical-align:inherit; width:18px">' -- правая колонка цифр
        	if (numeration=='chess') then b = b .. row+offsety
    		elseif (numeration=='continuous') then
    			if not(rev) then b = b .. trow*cols
				else b = b .. (rows-trow)*cols +1 end
    		elseif (numeration=='draughts') then 
    			if ((trow+cols+1)%2==tlisblack) then 
    				if not(rev) then b = b .. math.floor((trow*cols+1)/2)
					else
					b = b .. math.floor((cols*(rows-trow))/2)+1
					end
				end
				elseif (numeration=='swapdraughts') then
				if ((trow+cols)%2==tlisblack) then 
    				if not(rev) then b = b .. math.floor((trow*cols+1)/2)
					else
					b = b .. math.floor((cols*(rows-trow))/2)+1
					end
			end
			end
        	b = b .. '</td>'
        	end
    end
    if ( letters_bt ) then b = b .. letters_row(rev, numbers_lt, numbers_rt, cols, rows, offsetx, numeration, rev, lightdark,  1) .. '\n' end -- 2 - флаг нижней строки для шашечной нумерации
    if footer:match('^%s*$')
    then
    else    
        caption = '<div class="thumbcaption">' .. footer .. '</div>\n'
    end
    b = '<table cellpadding=0 cellspacing=0 style="line-height: 0; background:white; font-size:88%; border:1px #b0b0b0 solid;'
        .. 'padding:0; margin:auto">\n' .. b .. '\n</table>'

    if noframe then
        return b
    else
         return '<div class="thumb ' .. align .. '" style="clear:' .. clear .. '; text-align:center; width:' .. width + 8 .. 'px">'
         .. header .. '\n<div class="thumbinner" style="width:' .. width .. 'px;">\n' 
         .. b .. '\n' .. caption .. '</div></div>'

    end
    
end

function convertFenToArgs( fen )
    -- converts FEN notation to an array of positions, offset by 2
    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

function p.board(frame)
	local args = frame.args
    local pargs = frame:getParent().args
    local offsetx = args.offsetx or pargs.offsetx or 0 -- смещение начала координат доски по оси Х вправо (цифры)
    local offsety = args.offsety or pargs.offsety or 0 -- смещение начала координат доски по оси Y вверх (буквы)
    local size = (args.size or pargs.size) or '26'
    local reverse = (args.reverse or pargs.reverse or '' ):lower() == "true"
    local trans = (args.transparent or pargs.transparent or '' ):lower() == "true"
    local lightdark = (args.lightdark or pargs.lightdark or '' ):lower() == "swap"
    if (offsetx+offsety %2 ==1) then lightdark = not lightdark end -- инвертирование цветов клеток доски при смещении
    local altprefix = args.altprefix or pargs.altprefix or ''
    local rows = pargs.rows or args.rows or 8
    local cols = pargs.cols or args.cols or 8
    local letters = ( pargs.letters or args.letters or 'both' ):lower() 
    local numbers = ( pargs.numbers or args.numbers or 'both' ):lower() 
    local header =  mw.ustring.gsub( args[2] or pargs[2] or '', '^%s*(.-)%s*$', '%1' )
    local numeration = (args.numeration or pargs.numeration or 'chess'):lower()
    if not (numeration:match('draughts') or numeration:match('swapdraughts') or numeration:match('continuous')) then numeration = 'chess' end
    -- тип нумерации клеток доски: chess - шахматный, draughts - шашечный со второй клетки,
    -- swapdraughts - шашечный с первой клетки, continuous - шашечный сквозной
    local footer = args[3 + rows*cols] or pargs[3 + rows*cols] or ''
    local align = ( args[1] or pargs[1] or 'tright' ):lower()
    local clear = ( args.clear or pargs.clear ) or ( align:match('tright') and 'right' or 'none' )
    local noframe = (args.noframe or pargs.noframe or ''):lower() == "true"
    local fen = args.fen or pargs.fen
    size = mw.ustring.match(size, '[%d]+') or '26' -- remove px from size
    if (fen) then
        footer = args[3] or pargs[3] or ''
        return chessboard(convertFenToArgs( fen ), size, rows, cols, offsetx, offsety, reverse, trans, lightdark, altprefix, letters, numbers, header, footer, align, clear, noframe, numeration)
    end
    if args[3] then
        return chessboard(args, size, rows, cols, offsetx, offsety, reverse, trans, lightdark, altprefix, letters, numbers, header, footer, align, clear, noframe, numeration)
    else
        return chessboard(pargs, size, rows, cols, offsetx, offsety, reverse, trans, lightdark, altprefix, letters, numbers, header, footer, align, clear, noframe, numeration)
    end
    
end

return p