Module:Arimaa/board

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

local padding if caption then padding = "3px 3px 0" else padding = "3px" 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 .. "| \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;", width, padding)) 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("") :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

return tostring(div) 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(" Solution: " .. frame.args.move .. " " .. frame.args.desc .. " ")

return ret .. tostring(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(' ', 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' local padding = 6 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('| %s \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