local shogiobject = {}
-----------------------
-- internal functions
-----------------------
-- returns a japanese character for a roman letter abbreviation
-- called by the shogiboard() function
local function piecesymbol(abbreviation)
-- the abbreviation argument is a string of 1-3 letters that stand for the English names of the shogi pieces
-- the abbreviation is extracted out of a longer string by the processString() function
-- letter to character mapping (hash table)
local piecenames = {
p = '歩',
t = 'と',
l = '香',
pl = '杏',
n = '桂',
pn = '圭',
s = '銀',
ps = '全',
g = '金',
b = '角',
h = '馬',
r = '飛',
d = '龍',
gyoku = '玉',
ou = '王',
tx = '个',
plx = '仝',
pnx = '今',
dx = '竜',
e = '象',
a = '太'
}
-- spit out the character corresponding to abbreviation
-- if the abbreviation string is empty, then spit out nobreak space (for html tables)
piece = piecenames[abbreviation] or ' '
return string.format( piece )
end
-- function separates out the piece, the side, and the boldness info present in the string argument
-- it returns an array with these three values plus the color (for promoted pieces)
-- this info is passed to the makeTD() function
function processString(ss)
-- strip whitespace
ss = mw.text.trim(ss)
-- get the last character of the string
local lastchar = mw.ustring.sub(ss, -1)
-- chop off last character of string
local restofstring = mw.ustring.sub(ss, 1, -2)
-- default is normal font
-- but if the string ends with 'l' for 'last move',
-- then the font should be bold and we need to get a new string with the this 'l' chopped off (with a new last character)
local boldness = 'normal'
if lastchar == 'l' then
boldness = 'bold'
lastchar = mw.ustring.sub(restofstring, -1)
restofstring = mw.ustring.sub(restofstring, 1, -2)
end
-- the side is either 'g' for 'gote' or 's' for 'sente'
-- it's the last character in the string (and if the string ended)
local side = lastchar
-- the leftover string is the shogi piece abbreviation
local pieceabbr = restofstring
-- default is black font
-- if the piece is promoted (one the abbreviations below), then the piece should be red
local color = 'black'
if pieceabbr == 't' or pieceabbr == 'pl' or pieceabbr == 'pn' or pieceabbr == 'ps' or pieceabbr == 'h' or pieceabbr == 'd' then
color = 'red'
end
-- this is an exceptional bit:
-- gote's king is usually 王 instead of 玉 by convention,
-- but it's convenient to use the 'k' code for both sente and gote and let the default character be side-dependent
if pieceabbr == 'k' and side == 'g' then
pieceabbr = 'ou'
end
if pieceabbr == 'k' and side == 's' then
pieceabbr = 'gyoku'
end
-- similar to above exception, reverse default
if pieceabbr == 'ak' and side == 's' then
pieceabbr = 'ou'
end
if pieceabbr == 'ak' and side == 'g' then
pieceabbr = 'gyoku'
end
-- convert abbreviation to Japanese character
local piecechar = piecesymbol(pieceabbr)
-- special matches for arrows (n = north, s = south, etc.)
-- NOTE: ddl, ddr, dul, and dur are named incorrectly
-- yellow square gray square eastward arrow westward arrow northward arrow southward arrow
if ss:match('yy') or ss:match('gr') or ss:match('rat') or ss:match('lat') or ss:match('uat') or ss:match('dat') or
-- horizontal line mid-to-w line mid-to-e line vertical line mid-to-s line mid-to-n line
ss:match('lra') or ss:match('las') or ss:match('ras') or ss:match('uda') or ss:match('das') or ss:match('uas') or
-- sw-to-ne line sw-to-ne arrow ne-to-sw arrow mid-to-ne line mid-to-sw line nw-to-se line
ss:match('da') or ss:match('dau') or ss:match('dad') or ss:match('daus') or ss:match('dads') or ss:match('daa') or
-- nw-to-se arrow se-to-nw arrow mid-to-nw line mid-to-sw line s-to-e arrow s-to-w arrow
ss:match('daad') or ss:match('daau') or ss:match('daaus') or ss:match('daads') or ss:match('kar') or ss:match('kal') or
-- n-to-e arrow n-to-w arrow mid-to-e arrow mid-to-w arrow mid-to-s arrow mid-to-n arrow
ss:match('kadr') or ss:match('kadl') or ss:match('rah') or ss:match('lah') or ss:match('dah') or ss:match('uah') or
-- mid-to-ne arrow mid-to-nw arrow mid-to-se arrow mid-to-sw arrow s-to-ne line s-to-nw line
ss:match('durh') or ss:match('dulh') or ss:match('ddrh') or ss:match('ddlh') or ss:match('ddl') or ss:match('ddr') or
-- n-to-se line n-to-sw line
ss:match('dul') or ss:match('dur') then
piecechar = '[[File:shogi_' .. ss .. '22.svg|20px]]'
side = 'arrow'
color = 'arrow'
boldness = 'arrow'
end
local result = {piecechar, side, color, boldness}
return result
end
-- function makes a <td> containing the piece with CSS stuff
-- uses the info from processString() to customize the CSS based on which side, color, and boldness
function makeTD(stringarg)
-- got to process the string argument into its informational bits
-- this processedstring is an array (or whatever the equivalent is in Lua)
local processedstring = processString(stringarg)
-- saving the pieces of the array as separate objects to be referred to below
local piecechar = processedstring[1]
local side = processedstring[2]
local color = processedstring[3]
local bold = processedstring[4]
-- i guess one needs a root node?
local root = mw.html.create('')
-- the default <td>
local td = root:tag('td')
td:css('border', 'black 1px solid')
td:css('width', '20px')
td:css('height', '20px')
td:css('padding', '0')
td:css('line-height', '0')
td:css('font-family', '"Hiragino Mincho ProN", serif')
if not side:match('arrow') then
td:wikitext( piecechar )
end
-- g = gote
-- gote should be upside down text
if side:match( 'g' ) then
td:css('transform', 'rotate(180deg)')
end
-- for promoted pieces
if color:match( 'red' ) then
-- this is a darkish reddish color
td:css('color', '#E00303')
end
-- for bold pieces
if bold:match( 'bold' ) then
td:css('font-weight', 'bolder')
-- traditionally bold type is gothic (sans serif) in Japanese typesetting of shogi diagrams within Japanese shogi books
td:css('font-family', ' HiraginoSans-W5, sans-serif')
end
-- for arrow svgs
if side:match('arrow') then
td:css('padding', '0')
td:css('width', '20px')
td:css('height', '20px')
td:css('font-size', '1px')
td:css('line-height', '0')
td:wikitext( piecechar )
end
return tostring(root)
end
-- function makes the shogi diagram
-- this is basically a <div> enclosing a .css <div> wrapper with a <table> inside
function shogiboard(args)
-- special args
local special = {}
if args[87] then
-- a list of arguments separated by spaces
for w in string.gmatch(args[87], "%w+") do
if string.sub(w, 1, 1) == "f" then -- flipped board
special.flip = true
elseif string.sub(w, 1, 1) == "j" then -- japanese row labeling
special.jprows = true
end
end
end
-- these are the column coordinate labels 9-1
local colLabels
if special.flip then
colLabels = {'1', '2', '3', '4', '5', '6', '7', '8', '9', ' '}
else
colLabels = {'9', '8', '7', '6', '5', '4', '3', '2', '1', ' '}
end
-- these are the row coordinate labels
local rowLabels
if special.jprows then
if special.flip then
rowLabels = {'九', '八', '七', '六', '五', '四', '三', '二', '一'}
else
rowLabels = {'一', '二', '三', '四', '五', '六', '七', '八', '九'}
end
else
if special.flip then
rowLabels = {'9', '8', '7', '6', '5', '4', '3', '2', '1'}
else
rowLabels = {'1', '2', '3', '4', '5', '6', '7', '8', '9'}
end
end
local headerarg = args[2]
local toppieceinhandarg = args[3]
local root = mw.html.create('div')
-- <div> wrapper
local shogiboardwrapper = root:tag('div')
:addClass('shogiboardwrapper')
:css('padding-left', '4px')
:css('padding-bottom', '2px')
-- the diagram header/caption
local headerstring = mw.text.trim(headerarg)
local header = shogiboardwrapper:tag('div')
header:css('padding', '0')
header:wikitext(headerstring)
header:css('font-size', '14px')
-- this is the 'piece-in-hand' argument for gote
-- strip whitespace
local strippedpieceinhandtop = mw.text.trim(toppieceinhandarg) or ''
-- put it in a <div> with .css formating
local pieceinhandtopdiv = shogiboardwrapper:tag('div')
pieceinhandtopdiv:css('padding', '0')
pieceinhandtopdiv:css('font-size', '12px')
if special.flip then
pieceinhandtopdiv:wikitext('☗ pieces in hand: ')
else
pieceinhandtopdiv:wikitext('☖ pieces in hand: ')
end
-- i was going to put the actual argument text into conditional <span> .css formating, but i couldn't get the logical test in an if/else structure right...
local piecesinhandtopspan = pieceinhandtopdiv:tag('span')
piecesinhandtopspan:css('font-size', '13px')
piecesinhandtopspan:wikitext(strippedpieceinhandtop)
-- the shogi table
local shogitable = shogiboardwrapper:tag('table')
:addClass('shogitable')
:attr('border', '1')
:css('border-collapse', 'collapse')
:css('border', 'none')
:css('padding-top', '0')
:css('background-color', 'white')
-- font size for the shogi piece text
piecefontsize = '17px'
-- font size for the column/row piece coordinate labels (9-1) and (a-i)
colrowfontsize = '11px'
-- padding amount for the row piece coordinate labels (a-i)
padrowlab = '1px'
-- the row for the column coordinate labels
local columnlabelrow = shogitable:tag('tr')
:css('font-size', colrowfontsize)
:css('background-color', '#f9f9f9')
-- iterating over the column label to put each label in a <td>
for i,v in ipairs(colLabels) do
local td = columnlabelrow:tag('td')
:css('border', 'none')
:css('width', '20px')
:css('height', '5px')
:wikitext( v )
end
-- iterate over the 81 shogi piece arguments (left to right, top to bottom)
-- index number displacement/offset
-- this is just the number of arguments that precede the 81 shogi piece arguments that are in the html <table>
-- i just keep the piece arguments as indexes 1-81, then add nx to the index value
nx = 3
-- loop for all rows
for icols = 1,9 do
local trow = shogitable:tag('tr')
:css('font-size', piecefontsize)
-- put a single piece into a <td>
-- iterate over 9 pieces in the row from offset -8 to 0
for irow = (icols*9)-8,(icols*9) do
trow:wikitext( makeTD(args[(irow+nx)]) )
end
-- add row coordinate label <td>
local rowlabel = trow:tag('td')
:css('border', 'none')
:css('font-size', colrowfontsize)
:css('padding-left', padrowlab)
:css('padding-top', '0')
:css('padding-bottom', '0')
:css('background-color', '#f9f9f9')
:wikitext( rowLabels[icols] )
end
-- this is the 'piece-in-hand' argument for sente (same as above for gote)
local strippedpieceinhandbottom = mw.text.trim(args[81+1+nx]) or ''
local pieceinhandbottomdiv = shogiboardwrapper:tag('div')
pieceinhandbottomdiv:css('padding', '0')
pieceinhandbottomdiv:css('font-size', '12px')
if special.flip then
pieceinhandbottomdiv:wikitext('☖ pieces in hand: ')
else
pieceinhandbottomdiv:wikitext('☗ pieces in hand: ')
end
local piecesinhandbottomspan = pieceinhandbottomdiv:tag('span')
piecesinhandbottomspan:css('font-size', '13px')
piecesinhandbottomspan:wikitext(strippedpieceinhandbottom)
return tostring(root)
end
-----------------------
-- main function
-----------------------
function shogiobject.board(frame)
-- need to use getParent().args for reasons i dont understand
local args = frame:getParent().args
return shogiboard(args)
end
return shogiobject