Перейти до вмісту

Модуль:Chessboard

Матеріал з Вікіпедії — вільної енциклопедії.
{{i}} Документація модуля[перегляд] [редагувати] [історія] [очистити кеш]
local p = {}

local chars = 'abcdefghijklmnopqrstuvwxyz';

local colornames = {        
    d = {m = 'чорний', f = 'чорна', n = 'чорне'},
    l = {m = 'білий', f = 'біла', n = 'біле'},
}
    
local piecenames = { 
    p = { name = 'пішак', gender = 'm'},
    r = { name = 'тура', gender = 'f'}, 
    n = { name = 'кінь', gender = 'm'},
    b = { name = 'слон', gender = 'm'},
    q = { name = 'ферзь', gender = 'm'},
    k = { name = 'король', gender = 'm'},
    a = { name = 'архієпископ', gender = 'm'},
    c = { name = 'канцлер', gender = 'm'},
    z = { name = 'чемпіон', gender = 'm'},
    w = { name = 'чаклун', gender = 'm'},
    t = { name = 'дурень', gender = 'm'},
    h = { name = 'перевернутий пішак', gender = 'm'},
    m = { name = 'перевернута тура', gender = 'f'},
    s = { name = 'перевернутий кінь', gender = 'm'},
    f = { name = 'перевернутий король', gender = 'm'},
    e = { name = 'перевернутий слон', gender = 'm'},
    g = { name = 'перевернутий ферзь', gender = 'm'},
}

local symnames = { 
    xx = 'чорний хрестик', 
    ox = 'білий хрестик', 
    xo = 'чорний кружок', 
    oo = 'білий кружок',
    ul = 'стрілка вверх-вліво', 
    ua = 'стрілка вверх', 
    ur = 'стрілка вверх-вправо', 
    la = 'стрілка вліво', 
    ra = 'стрілка вправо',
    dl = 'стрілка вниз-вліво', 
    da = 'стрілка вниз', 
    dr = 'стрілка вниз-вправо', 
    lr = 'стрілка вліво-вправо', 
    ud = 'стрілка вверх-вниз',
    x0 = 'нуль', 
    x1 = 'одиниця', 
    x2 = 'двійка', 
    x3 = 'трійка', 
    x4 = 'четвірка', 
    x5 = 'п\'ятірка', 
    x6 = 'шістка', 
    x7 = 'сімка', 
    x8 = 'вісімка', 
    x9 = 'дев\'ятка',
}

local known_boards = {
    ['8x8'] = 'Chessboard480.svg'
}

function colchar(col)
    return (chars):sub( col, col)
end

function image_square(pc, row, col, size, transparent)
    local color = mw.ustring.gsub( pc, '^.*(%w)(%w).*$', '%2' ) or ''
    local piece = mw.ustring.gsub( pc, '^.*(%w)(%w).*$', '%1' ) or ''
    local alt = colchar( col ) .. row .. ' '
    if colornames[color] and piecenames[piece] then
        alt = alt .. colornames[color][piecenames[piece]['gender']] .. ' ' .. piecenames[piece]['name']
    else
        alt = alt .. ( symnames[piece .. color] or piece .. ' ' .. color )
    end
    
    local ld =  transparent and 't' or ((((row + col) % 2) == 0) and 'd' or 'l')
    
    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, size, cols )
    local td = '<td style="padding: 0; vertical-align: inherit;height:18px;width:' .. size .. 'px;">'
    local legends = chars:sub(1, cols)
    local l = mw.text.split( rev and legends:reverse() or legends, '' )
    return '<tr style="vertical-align:middle">\n' 
            .. ( num_lt and '<td style="vertical-align: inherit; padding: 0"></td>' or '' )
            .. td
            .. table.concat( l, '</td>' .. td ) 
            .. '</td>'
            .. ( num_rt and '<td style="vertical-align: inherit; padding: 0"></td>' or '' )
            .. '\n</tr>'
end

function create_known_board(board_image, args, size, rows, cols, rev, numbers_lt, numbers_rt)
    local b = '<tr style="vertical-align:middle">'
    if ( numbers_lt ) then b = b .. '<td style="padding: 0; vertical-align: inherit; width:18px;height:' .. size .. 'px">' .. (rev and 1 or rows) .. '</td>' end
    b = b .. '<td colspan=8 rowspan=8 style="padding: 0; vertical-align: inherit;"><div class="chess-board" style="position:relative;">'
    b = b .. string.format( '[[File:%s|%dx%dpx|link=]]', board_image, cols * size, rows * size )
    for trow = 1,rows do
        local row = rev and trow or ( rows + 1 - trow )
        for tcol = 1,cols do
            local col = rev and ( cols + 1 - tcol ) or tcol
            local piece = args[cols * ( rows - row ) + col + 2] or ''
            if piece:match( '%w%w' ) then
               local img = image_square(piece:match('%w%w'), row, col, size, true )
               b = b .. string.format(
                   '<div style="position:absolute;z-index:3;top:%dpx;left:%dpx;width:' .. size .. 'px;height:' .. size .. 'px;">%s</div>\n', 
                   ( trow - 1 ) * size, ( tcol - 1 ) * size, img )
            end
        end
    end
    b = b .. '</div></td>'
    if ( numbers_rt ) then b = b .. '<td style="width:18px;height:' .. size ..'px;padding:0">' .. (rev and 1 or 8) .. '</td>' end
    b = b .. '</tr>'
    if ( numbers_lt or numbers_rt ) then
       for trow = 2, rows do
          local row = rev and trow or ( rows + 1 - trow )
          b = b .. '<tr style="vertical-align:middle">'
          if ( numbers_lt ) then b = b .. '<td style="padding: 0; vertical-align: inherit;height:' .. size .. 'px">' .. row .. '</td>' end
          if ( numbers_rt ) then b = b .. '<td style="padding: 0; vertical-align: inherit;height:' .. size .. 'px">' .. row .. '</td>' end
          b = b .. '</tr>\n'
       end
    end
    return b
end

function create_unknown_board(args, size, rows, cols, rev, numbers_lt, numbers_rt)
    local b = ''
    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">' .. row .. '</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 '', row, col, size, false )
            b = b .. '<td style="padding:0; vertical-align:inherit">' .. img .. '</td>'
        end
        if ( numbers_rt ) then b = b .. '<td style="padding:0; vertical-align:inherit; width:18px">' .. row .. '</td>' end
        b = b .. '</tr>'
    end
    return b
end

function chessboard(args, size, rows, cols, rev, letters, numbers, header, footer, align, clear)
    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
    if ( numbers_lt ) then width = width + 18 end
    if ( numbers_rt ) then width = width + 18 end

    local b = ''
    
    if ( letters_tp ) then b = b .. letters_row( rev, numbers_lt, numbers_rt, size, cols) .. '\n' end
    
    local board_index = string.format( '%dx%d', rows, cols)
    local board_image = known_boards[board_index]
    if board_image then
        b = b .. create_known_board(board_image, args, size, rows, cols, rev, numbers_lt, numbers_rt)
    else
        b = b .. create_unknown_board(args, size, rows, cols, rev, numbers_lt, numbers_rt)
    end
    
    if ( letters_bt ) then b = b .. letters_row( rev, numbers_lt, numbers_rt, size, cols ) .. '\n' end

    local caption = ''
    if not footer:match( '^%s*$' ) then
        caption = '<div class="thumbcaption">' .. footer .. '</div>\n'
    end
    
    return '<div class="thumb ' .. align .. '" style="clear:' .. clear .. '; text-align:center;">'
        .. header .. '\n<div class="thumbinner" style="width:' .. width .. 'px;">\n' 
        .. '<table cellpadding=0 cellspacing=0 style="background:white; font-size:88%; border:1px #b0b0b0 solid; padding:0; '
        .. 'margin:auto;">\n' .. b .. '\n</table>\n' .. caption .. '</div></div>'
end

function convertFenToArgs( fen )
    -- converts FEN notation to 64 entry 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 size = args.size or pargs.size or '26'
    size = mw.ustring.match( size, '[%d]+' ) or '26' -- remove px from size
    
    local reverse = ( args.reverse or pargs.reverse or '' ):lower() == "true"
    local rows = args.rows or pargs.rows or 8
    local cols = args.cols or pargs.cols or 8
    local letters = ( args.letters or pargs.letters or 'both' ):lower() 
    local numbers = ( args.numbers or pargs.numbers or 'both' ):lower() 
    local fen = args.fen or pargs.fen
    
    local align, clear, header, footer;
    
    if (fen) then
        align = args.align or pargs.align or 'tright'
        clear = args.clear or pargs.clear or ( align:match('tright') and 'right' ) or 'none'
        header = args.header or pargs.header or ''
        footer = args.footer or pargs.footer or ''
        
        args = convertFenToArgs(fen);
        
    else
        align = ( args[1] or pargs[1] or 'tright' ):lower()
        clear = args.clear or pargs.clear or ( align:match('tright') and 'right' ) or 'none'
        header = args[2] or pargs[2] or ''
        footer = args[3 + rows*cols] or pargs[3 + rows*cols] or ''
        
        if not args[3] then
            args = pargs;
        end
    end
    
    return chessboard(args, size, rows, cols, reverse, letters, numbers, header, footer, align, clear )

end

return p