# Module:Arimaa/board

Module documentation[create]
```p = {}

function p.from_fen(fen)
local ret = {}
for i = 1, 8 do
ret[i] = {}
end

local x = 1
local y = 1
local i = 1
while i <= #fen do
local separator = false
local a = string.match(fen, '^[%d]+', i)
if a then
x = x + a
i = i + #a
else
local a = string.match(fen, '^/?\n?', i)
if a ~= '' then
separator = true
x = 1
y = y + 1
i = i + #a
else
local c = string.sub(fen, i, i)
if(c == ' ') then
x = x + 1
elseif(string.match(c, '[rcdhmeRCDHME]')) then
ret[x][y] = c
x = x + 1
else
break
end
i = i + 1
end
end

local z = math.floor((x - 1) / 8)
x = x - z * 8
y = y + z

if x == 1 and not separator then
local a = string.match(fen, '^/?\n?', i)
i = i + #a
end
end

return ret
end

function p.from_long(s)
local ret = {}
for i = 1, 8 do
ret[i] = {}
end

local a = string.gmatch(s, '[rcdhmeRCDHME][a-h][1-8]')
while true do
local b = a()
if not b then break end
local x = string.byte(string.sub(b,2,2)) - string.byte('a') + 1
local y = tonumber(string.sub(b,3,3))
ret[x][9-y] = string.sub(b,1,1)
end

return ret
end

function p.expand_char(c)
if c == " " then
return "  "
elseif string.match(c, "%u") then
return string.lower(c) .. "g"
else
return c .. "s"
end
end

function p.step_dest(sq, dir)
if dir == "n" then
return {sq[1], sq[2] - 1}
elseif dir == "e" then
return {sq[1] + 1, sq[2]}
elseif dir == "s" then
return {sq[1], sq[2] + 1}
elseif dir == "w" then
return {sq[1] - 1, sq[2]}
else
return sq
end
end

function p.expand(s)
local ret = {}
local k = 1
local g = string.gmatch(s, "[rcdhmeRCDHME][a-h][1-8][neswx]+")
while true do
local expr = g()
if not expr then break end
local x = string.byte(string.sub(expr, 2, 2)) - string.byte("a") + 1
local y = 9 - string.sub(expr, 3, 3)
local sq = {x, y}
for i = 4, #expr do
local dir = string.sub(expr, i, i)
ret[k] = {piece = string.sub(expr, 1, 1),
sq = sq,
dir = dir
}
sq = p.step_dest(sq, dir)
k = k + 1
end
end

return ret
end

function p.play_move(board, move)
for i = 1, #move do
board[move[i].sq[1]][move[i].sq[2]] = nil
if move[i].dir ~= 'x' then
dest = p.step_dest(move[i].sq, move[i].dir)
board[dest[1]][dest[2]] = move[i].piece
end
end
end

function p.diagram1(region, x, y, board, class, caption)
local width, height, min_x, max_x, min_y, max_y
if string.match(region, "w") then
width = 33 + 37 * x
min_x = 1
max_x = x
elseif string.match(region, "e") then
width = 32 + 37 * x
max_x = 8
min_x = 9 - x
else
width = 344
min_x = 1
max_x = 8
end
if string.match(region, "n") then
height = 16 + 37 * y
marginBottom = 1 + 37 * y
min_y = 1
max_y = y
elseif string.match(region, "s") then
height = 17 + 37 * y
marginBottom = 17 + 37 * y
max_y = 8
min_y = 9 - y
else
height = 328
marginBottom = 313
min_y = 1
max_y = 8
end

if caption then
else
end

local margin
if string.match(region, "e") then
margin = "0 0 0 0"
else
margin = "0 0 0 16px"
end
table = '{|cellpadding="0" style="border-collapse: collapse; margin: ' .. margin .. ' !important;"\n'

for j = min_y, max_y do
table = table .. '|- style="height: 37px"\n'
for i = min_x, max_x do
if j == min_y then
table = table .. '| style="width: 37px; padding: 0" '
end
if not board[i][j] then
table = table .. "| \n"
else
table = table .. "| [[Image:Arimaa_" .. p.expand_char(board[i][j]) .. "b74.gif|37px]]\n"
end
end
end
table = table .. "|}"

local div = mw.html.create("div")
if class then
div:attr("class", class)
end
div:attr("style", string.format("width: %dpx; padding: %s; border: 1px solid #b0b0b0; background-color: #f9f9f9;",
div:tag("div")
:attr("style", string.format("height: %dpx; border: 1px solid #b0b0b0; padding: 3px; background-color: #f9f9f9;", height))
:tag("div")
:attr("style", string.format("margin-bottom: %dpx", -marginBottom))
:wikitext("[[Image:Arimaa board " .. region .. x .. y .. ".jpg]]")
:done()
:wikitext("\n" .. table .. "\n")
:done()

if caption then
div:tag('p')
:attr("style", "line-height: 1.4; text-align: left; font-size: 90%; margin: 0.3em")
:wikitext(caption)
end

end

function p.puzzle(frame)
local board

local fen = frame.args.fen
if fen then
if string.sub(fen, 1, 2) == "\\\n" then
fen = string.sub(fen, 3)
end
board = p.from_fen(fen)
elseif frame.args.long then
board = p.from_long(frame.args.long)
end

local ret = p.diagram1(frame.args[1], frame.args[2], frame.args[3], board, frame.args.class1, frame.args.caption1)

p.play_move(board, p.expand(frame.args.move))

local div = mw.html.create("div")
div:attr("class", "collapsible")
div:tag("div")
:attr("class", "title")
:wikitext(frame.args.q)
div:tag("div")
:attr("class", "body")
:wikitext(p.diagram1(frame.args[1], frame.args[2], frame.args[3], board, frame.args.class2, frame.args.caption2))
:wikitext("<p>'''Solution: " .. frame.args.move .. "'''</p><p>" .. frame.args.desc .. "</p>")

return ret .. tostring(div) .. '<div style="clear: both"></div>'
end

function p.pieceImage(pieceset, piece)
local gold = string.match(piece, '%u')
local t = string.lower(piece)
if pieceset == 'stone' or pieceset == 'flat' then
local s = 'Arimaa_' .. t
if gold then
s = s .. 'g'
else
s = s .. 's'
end
if pieceset == 'stone' then
return s .. 'b74.gif'
else
return s .. '.svg'
end
else
local A = {e = '1-elephant', m = '2-camel', h = '3-horse', d = '4-dog', c = '5-cat', r = '6-rabbit'}
if gold then
return string.format('G-%s.svg', A[t])
else
return string.format('S-%s.svg', A[t])
end
end
end

function p.plan_url(board)
local goldString = ''
local silverString = ''
for x = 1, 8 do
for y = 1, 8 do
local xs = string.char(string.byte('a') - 1 + x)
local ys = tostring(9 - y)
if board[x][y] then
if string.match(board[x][y], '%u') then
goldString = goldString .. '%20' .. board[x][y] .. xs .. ys
else
silverString = silverString .. '%20' .. board[x][y] .. xs .. ys
end
end
end
end

return string.format('http://arimaa.com/arimaa/games/planGame.cgi?movelist=1w%s%%0A1b%s', goldString, silverString)
end

-- TODO: accumulation in ret is inefficient
function p.diagram2(board, min_x, max_x, min_y, max_y, caption, side, size, pieceset, border, backgroundColour, boardColour, trapColour, gridColour, captionColour)
if not size or size == '' then size = '28' end
size = tonumber(size)
if not pieceset or pieceset == '' then pieceset = 'flat' end
if not backgroundColour or backgroundColour == '' then backgroundColour = '#cae2ed' end
if not boardColour or boardColour == '' then boardColour = '#fff' end
if not trapColour or trapColour == '' then trapColour = '#9fbbc6' end
if not gridColour or gridColour == '' then gridColour = '#a2adb1' end
if not captionColour or captionColour == '' then captionColour = '#9fbbc6' end

local boardBorder = '2px solid #888'

local labelWidth = 18

local topMargin, rightMargin, bottomMargin, leftMargin
if max_y == 8 then topMargin = 3 else topMargin = 10 end
if max_x == 8 then rightMargin = 3 else rightMargin = 10 end
if min_x == 1 then leftMargin = 3 else leftMargin = 10 end
if min_y == 1 then bottomMargin = 3 else bottomMargin = 10 end

local width = (max_x - min_x + 1) * size + (max_x - min_x + 2) + leftMargin + rightMargin
if min_x == 1 then width = width + labelWidth + 1 end
if max_x == 8 then width = width + labelWidth + 1 end

local outerStyle = string.format('background: %s;', backgroundColour)
if side == 'right' then
outerStyle = outerStyle .. ' float: right; clear: right; margin: 0.75em 0 0.75em 1em;'
elseif side == 'left' then
outerStyle = outerStyle .. ' float: left; clear: left; margin: 0.75em 1.5em 0.75em 0;'
end
if border and border ~= '' then
outerStyle = outerStyle .. ' border: 1px solid #aaa;'
end
local ret = string.format('{| cellpadding="0" cellspacing="0" style="%s"\n', outerStyle)
local boardStyle = string.format('font-weight: bold; font-size: 80%%; text-align: center; border-collapse: collapse; margin: %dpx %dpx %dpx %dpx;',
topMargin, rightMargin, bottomMargin, leftMargin)
ret = ret .. string.format('|\n{| cellpadding="0" style="%s"\n', boardStyle)

local files = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'}
local labelRow = '|-\n|'
if min_x == 1 then
labelRow = labelRow .. ' ||'
end
for x = min_x, max_x - 1 do
labelRow = labelRow .. ' ' .. files[x] .. ' ||'
end
labelRow = labelRow .. ' ' .. files[max_x] .. '\n'

if max_y == 8 then
ret = ret .. labelRow
end

for y = max_y, min_y, -1 do
ret = ret .. string.format('|- style="height: %dpx"\n', size)
if min_x == 1 then
ret = ret .. '|'
if y == max_y then
ret = ret .. string.format(' style="width: %dpx" |', labelWidth)
end
ret = ret .. string.format(' %d\n', y)
end
local s = string.format('border: 1px solid %s;', gridColour)
if y == max_y then
s = s .. string.format(' width: %dpx;', size)
end
if y == 8 then
s = s .. string.format(' border-top: %s;', boardBorder)
elseif y == 1 then
s = s .. string.format(' border-bottom: %s;', boardBorder)
end
for x = min_x, max_x do
local s2 = s
if x == 1 then
s2 = s2 .. string.format(' border-left: %s;', boardBorder)
elseif x == 8 then
s2 = s2 .. string.format(' border-right: %s;', boardBorder)
end
if (x == 3 or x == 6) and (y == 3 or y == 6) then
s2 = s2 .. string.format(' background: %s;', trapColour)
else
s2 = s2 .. string.format(' background: %s;', boardColour)
end
ret = ret .. string.format('| style="%s" |', s2)
if board[x][9-y] then
ret = ret .. string.format(' [[Image:%s|%dpx]]', p.pieceImage(pieceset, board[x][9-y]), size)
end
ret = ret .. '\n'
end
if max_x == 8 then
ret = ret .. '|'
if y == max_y then
ret = ret .. string.format(' style="width: %dpx" |', labelWidth)
end
ret = ret .. string.format(' %d\n', y)
end
end

if min_y == 1 then
ret = ret .. labelRow
end

ret = ret .. '|}\n'

if caption and caption ~= '' then
ret = ret .. '|-\n'
captionStyle = string.format('font-size: 90%%; background: %s; padding: 3px %dpx; margin: 0; width: %dpx;', captionColour, padding, width - 2*padding)
local planUrl = string.gsub(p.plan_url(board), '%%', '%%%%')
caption = string.gsub(caption, 'PLAN_GOLD', planUrl)
caption = string.gsub(caption, 'PLAN_SILVER', planUrl .. '%%0A2wpass')
ret = ret .. string.format('| <p style="%s">%s</p>\n', captionStyle, caption)
end

ret = ret .. '|}\n'

return ret
end

function p.board(frame)
local board

local fen = frame.args.fen
if fen ~= '' then
if string.sub(fen, 1, 2) == "\\\n" then
fen = string.sub(fen, 3)
end
board = p.from_fen(fen)
elseif frame.args.long ~= '' then
board = p.from_long(frame.args.long)
end

local min_x = 1
local max_x = 8
local min_y = 1
local max_y = 8

if frame.args.region ~= '' then
local _, _, a, b = string.find(frame.args.region, '([a-h])-?([a-h])')
if a then
min_x = string.byte(a) - string.byte('a') + 1
max_x = string.byte(b) - string.byte('a') + 1
end
local _, _, c, d = string.find(frame.args.region, '([1-8])-?([1-8])')
if c then
min_y = tonumber(c)
max_y = tonumber(d)
end
end

return p.diagram2(board, min_x, max_x, min_y, max_y, frame.args.caption, frame.args.side, frame.args.size, frame.args.pieceset, frame.args.border, frame.args["background-colour"], frame.args["board-colour"], frame.args["trap-colour"], frame.args["grid-colour"], frame.args["caption-colour"])
end

function p.wrapper(frame)
local board = {}
for x = 1, 8 do
board[x] = {}
end

for y = 1, 8 do
for x = 1, 8 do
if(frame.args[(y-1)*8+x]) then
local p, c = string.match(frame.args[(y-1)*8+x], '([rcdhme])([gs])')
if c == 'g' then
board[x][y] = string.upper(p)
elseif c == 's' then
board[x][y] = p
end
end
end
end

local side
if frame.args.class == 'tright' then
side = 'right'
elseif frame.args.class == 'tleft' then
side = 'left'
end

return p.diagram2(board, 1, 8, 1, 8, frame.args.caption, side, frame.args.size, frame.args.pieceset, frame.args.border, frame.args["background-colour"], frame.args["board-colour"], frame.args["trap-colour"], frame.args["grid-colour"], frame.args["caption-colour"])
end

return p
```