<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-GB">
	<id>https://elite.mistflux.co.uk/w/index.php?action=history&amp;feed=atom&amp;title=Module%3AGoban</id>
	<title>Module:Goban - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://elite.mistflux.co.uk/w/index.php?action=history&amp;feed=atom&amp;title=Module%3AGoban"/>
	<link rel="alternate" type="text/html" href="https://elite.mistflux.co.uk/w/index.php?title=Module:Goban&amp;action=history"/>
	<updated>2026-10-31T18:46:22Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.46.2</generator>
	<entry>
		<id>https://elite.mistflux.co.uk/w/index.php?title=Module:Goban&amp;diff=128311&amp;oldid=prev</id>
		<title>Wikipedia&gt;Kanayoko: add 3 digits</title>
		<link rel="alternate" type="text/html" href="https://elite.mistflux.co.uk/w/index.php?title=Module:Goban&amp;diff=128311&amp;oldid=prev"/>
		<updated>2026-09-05T00:22:17Z</updated>

		<summary type="html">&lt;p&gt;add 3 digits&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;-- Module:Goban&lt;br /&gt;
--&lt;br /&gt;
-- Renders a Go board diagram from per-intersection tile codes.  The board is&lt;br /&gt;
-- drawn with CSS alone (see Module:Goban/style.css, loaded through&lt;br /&gt;
-- TemplateStyles): grid lines, stones and hoshi are styled spans, marks are&lt;br /&gt;
-- plain text.  No images are used.  A text description of the position for&lt;br /&gt;
-- screen readers is included, hidden with {{Screen reader-only}} (see&lt;br /&gt;
-- Module:Goban/accessibility).&lt;br /&gt;
--&lt;br /&gt;
-- Usage from a template:&lt;br /&gt;
--   {{#invoke:Goban|board|rows=19|cols=19|u=u|d=d|l=l|r=r|hoshi=4,4;4,10;...}}&lt;br /&gt;
--&lt;br /&gt;
-- Board configuration is read from the #invoke arguments; the cells, the tile&lt;br /&gt;
-- size and (unless fixed by the #invoke) the border flags are read from the&lt;br /&gt;
-- template&amp;#039;s own arguments.&lt;br /&gt;
--&lt;br /&gt;
-- Template arguments:&lt;br /&gt;
--   1 .. rows*cols   tile code for each intersection, row by row from the top&lt;br /&gt;
--                    left.  Empty (or all-whitespace) cells fall back to the&lt;br /&gt;
--                    board&amp;#039;s default tile for that position.&lt;br /&gt;
--   rows*cols + 1    tile size in pixels (default 24).&lt;br /&gt;
--   u, d, l, r       border codes, only honoured when the #invoke does not&lt;br /&gt;
--                    fix them (this is how {{Goban 5x5}}, {{Goban 9x9}} and&lt;br /&gt;
--                    {{Goban 11x11}} behave; {{Goban}}, {{Goban 13x13}} and&lt;br /&gt;
--                    {{Goban 7x11}} fix their borders and ignore them).&lt;br /&gt;
--   alt              optional text read out by screen readers instead of the&lt;br /&gt;
--                    automatically generated description of the position.&lt;br /&gt;
--&lt;br /&gt;
-- #invoke configuration:&lt;br /&gt;
--   rows, cols       board dimensions (default 19 x 19).&lt;br /&gt;
--   u, d, l, r       border code used for the top, bottom, left and right&lt;br /&gt;
--                    edges.  Corners concatenate them (u..l, u..r, d..l, d..r),&lt;br /&gt;
--                    giving &amp;quot;ul&amp;quot;, &amp;quot;ur&amp;quot;, &amp;quot;dl&amp;quot;, &amp;quot;dr&amp;quot;.  When a flag is omitted&lt;br /&gt;
--                    from the #invoke, the template argument of the same name&lt;br /&gt;
--                    is used instead (default: no border).&lt;br /&gt;
--   hoshi            star points as &amp;quot;row,col&amp;quot; pairs separated by &amp;quot;;&amp;quot; (or&lt;br /&gt;
--                    whitespace), counted from the top-left, e.g. &amp;quot;4,4;4,10&amp;quot;.&lt;br /&gt;
--                    An empty cell on a star point defaults to the &amp;quot;-&amp;quot; tile.&lt;br /&gt;
--&lt;br /&gt;
-- Tile codes (the same as the former &amp;quot;Go &amp;lt;code&amp;gt;.svg&amp;quot; image set):&lt;br /&gt;
--   (empty)          plain intersection; u d l r ul ur dl dr  board edges&lt;br /&gt;
--   - or x           hoshi point;  add a trailing c (c, -c, uc, ulc, ...)&lt;br /&gt;
--                    for a red ring on an empty point&lt;br /&gt;
--   b, w             black and white stones; bT wT triangle, bS wS square,&lt;br /&gt;
--                    b0-b9 w0-w9 numbered stones&lt;br /&gt;
--   00-99, 000-999   numbered stones for game records (odd black, even white)&lt;br /&gt;
--   A-Z except X     letter mark (shown in lower case);  X  an X mark&lt;br /&gt;
--   0-9              a bare digit on the wood, with no grid lines&lt;br /&gt;
--&lt;br /&gt;
-- {{#invoke:Goban|tile|&amp;lt;code&amp;gt;|&amp;lt;size&amp;gt;}} renders a single tile, for&lt;br /&gt;
-- documentation.&lt;br /&gt;
&lt;br /&gt;
local accessibility = require(&amp;#039;Module:Goban/accessibility&amp;#039;)&lt;br /&gt;
&lt;br /&gt;
local p = {}&lt;br /&gt;
&lt;br /&gt;
local STYLES = &amp;#039;Module:Goban/style.css&amp;#039;&lt;br /&gt;
local DEFAULT_SIZE = 24&lt;br /&gt;
local TRIANGLE = &amp;#039;\226\150\179&amp;#039; -- U+25B3 WHITE UP-POINTING TRIANGLE&lt;br /&gt;
&lt;br /&gt;
-- Strip leading and trailing whitespace.&lt;br /&gt;
local function trim(s)&lt;br /&gt;
	if s == nil then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
	return (tostring(s):match(&amp;#039;^%s*(.-)%s*$&amp;#039;))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Returns the trimmed value, or nil when the value is absent or blank.&lt;br /&gt;
local function nonBlank(s)&lt;br /&gt;
	s = trim(s)&lt;br /&gt;
	if s == &amp;#039;&amp;#039; then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
	return s&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Escape characters that are significant in HTML or wikitext.&lt;br /&gt;
local function escape(s)&lt;br /&gt;
	return (s:gsub(&amp;quot;[&amp;lt;&amp;gt;%[%]{}|&amp;amp;&amp;#039;\&amp;quot;=*#:;]&amp;quot;, function(ch)&lt;br /&gt;
		return &amp;#039;&amp;amp;#&amp;#039; .. string.byte(ch) .. &amp;#039;;&amp;#039;&lt;br /&gt;
	end))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Parse &amp;quot;r,c;r,c;...&amp;quot; into a set keyed by &amp;quot;r,c&amp;quot;.&lt;br /&gt;
local function parseHoshi(spec)&lt;br /&gt;
	local set = {}&lt;br /&gt;
	if spec == nil then&lt;br /&gt;
		return set&lt;br /&gt;
	end&lt;br /&gt;
	for r, c in tostring(spec):gmatch(&amp;#039;(%d+)%s*,%s*(%d+)&amp;#039;) do&lt;br /&gt;
		set[tonumber(r) .. &amp;#039;,&amp;#039; .. tonumber(c)] = true&lt;br /&gt;
	end&lt;br /&gt;
	return set&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- A span, given its classes and (already escaped) content.&lt;br /&gt;
local function span(classes, content)&lt;br /&gt;
	return &amp;#039;&amp;lt;span class=&amp;quot;&amp;#039; .. classes .. &amp;#039;&amp;quot;&amp;gt;&amp;#039; .. (content or &amp;#039;&amp;#039;) .. &amp;#039;&amp;lt;/span&amp;gt;&amp;#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- HTML for one intersection.  Every tile is a .goban-pt span; the code&lt;br /&gt;
-- selects modifier classes and, for stones and marks, a child span.&lt;br /&gt;
local function tile(code)&lt;br /&gt;
	-- Empty points, with optional board edges and a red ring.&lt;br /&gt;
	local edges, ring = code:match(&amp;#039;^([udlr]*)(c?)$&amp;#039;)&lt;br /&gt;
	if edges then&lt;br /&gt;
		local classes = &amp;#039;goban-pt&amp;#039;&lt;br /&gt;
		if edges ~= &amp;#039;&amp;#039; then&lt;br /&gt;
			classes = classes .. &amp;#039; goban-&amp;#039; .. edges&lt;br /&gt;
		end&lt;br /&gt;
		if ring == &amp;#039;c&amp;#039; then&lt;br /&gt;
			classes = classes .. &amp;#039; goban-c&amp;#039;&lt;br /&gt;
		end&lt;br /&gt;
		return span(classes)&lt;br /&gt;
	end&lt;br /&gt;
	ring = code:match(&amp;#039;^[%-x](c?)$&amp;#039;)&lt;br /&gt;
	if ring then&lt;br /&gt;
		return span(&amp;#039;goban-pt goban-hoshi&amp;#039; .. (ring == &amp;#039;c&amp;#039; and &amp;#039; goban-c&amp;#039; or &amp;#039;&amp;#039;))&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Stones: b w bT wT bS wS b0-b9 w0-w9, and 00-99 / 000-999 for game&lt;br /&gt;
	-- records.&lt;br /&gt;
	local color, mark = code:match(&amp;#039;^([bw])([ST%d]?)$&amp;#039;)&lt;br /&gt;
	if not color and code:match(&amp;#039;^%d%d%d?$&amp;#039;) then&lt;br /&gt;
		color = (tonumber(code) % 2 == 0) and &amp;#039;w&amp;#039; or &amp;#039;b&amp;#039;&lt;br /&gt;
		mark = code&lt;br /&gt;
	end&lt;br /&gt;
	if color then&lt;br /&gt;
		local classes = &amp;#039;goban-stone goban-&amp;#039; .. color&lt;br /&gt;
		local text = mark&lt;br /&gt;
		if mark == &amp;#039;T&amp;#039; then&lt;br /&gt;
			classes = classes .. &amp;#039; goban-T&amp;#039;&lt;br /&gt;
			text = TRIANGLE&lt;br /&gt;
		elseif mark == &amp;#039;S&amp;#039; then&lt;br /&gt;
			classes = classes .. &amp;#039; goban-S&amp;#039;&lt;br /&gt;
			text = &amp;#039;&amp;#039;&lt;br /&gt;
		elseif #mark == 3 then&lt;br /&gt;
			classes = classes .. &amp;#039; goban-3&amp;#039;&lt;br /&gt;
		end&lt;br /&gt;
		return span(&amp;#039;goban-pt&amp;#039;, span(classes, text))&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Marks on an empty point and bare digits.&lt;br /&gt;
	if code == &amp;#039;X&amp;#039; then&lt;br /&gt;
		return span(&amp;#039;goban-pt&amp;#039;, span(&amp;#039;goban-mark goban-X&amp;#039;, &amp;#039;X&amp;#039;))&lt;br /&gt;
	elseif code:match(&amp;#039;^%u$&amp;#039;) then&lt;br /&gt;
		return span(&amp;#039;goban-pt&amp;#039;, span(&amp;#039;goban-mark&amp;#039;, code))&lt;br /&gt;
	elseif code:match(&amp;#039;^%d$&amp;#039;) then&lt;br /&gt;
		return span(&amp;#039;goban-pt goban-bare&amp;#039;, span(&amp;#039;goban-mark goban-label&amp;#039;, code))&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Anything else is shown verbatim so that the mistake is visible.&lt;br /&gt;
	return span(&amp;#039;goban-pt&amp;#039;, span(&amp;#039;goban-mark goban-unknown&amp;#039;, escape(code)))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- The &amp;lt;templatestyles&amp;gt; tag; without a frame (unit tests) nothing is emitted.&lt;br /&gt;
local function styles(frame)&lt;br /&gt;
	if frame and frame.extensionTag then&lt;br /&gt;
		return frame:extensionTag{ name = &amp;#039;templatestyles&amp;#039;, args = { src = STYLES } }&lt;br /&gt;
	end&lt;br /&gt;
	return &amp;#039;&amp;#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Builds the board HTML.&lt;br /&gt;
--   cfg    #invoke arguments (board configuration)&lt;br /&gt;
--   args   template arguments (cells, size, border flags, alt)&lt;br /&gt;
--   frame  the #invoke frame, used to expand {{Screen reader-only}} and&lt;br /&gt;
--          &amp;lt;templatestyles&amp;gt;&lt;br /&gt;
function p._board(cfg, args, frame)&lt;br /&gt;
	cfg = cfg or {}&lt;br /&gt;
	args = args or {}&lt;br /&gt;
&lt;br /&gt;
	local rows = tonumber(cfg.rows) or 19&lt;br /&gt;
	local cols = tonumber(cfg.cols) or rows&lt;br /&gt;
	local hoshi = parseHoshi(cfg.hoshi)&lt;br /&gt;
&lt;br /&gt;
	-- Border codes: fixed by the #invoke, otherwise taken from the template&lt;br /&gt;
	-- call ({{{u|}}} etc.), otherwise empty.&lt;br /&gt;
	local function border(name)&lt;br /&gt;
		if cfg[name] ~= nil then&lt;br /&gt;
			return trim(cfg[name])&lt;br /&gt;
		end&lt;br /&gt;
		return trim(args[name]) or &amp;#039;&amp;#039;&lt;br /&gt;
	end&lt;br /&gt;
	local up, down, left, right =&lt;br /&gt;
		border(&amp;#039;u&amp;#039;), border(&amp;#039;d&amp;#039;), border(&amp;#039;l&amp;#039;), border(&amp;#039;r&amp;#039;)&lt;br /&gt;
&lt;br /&gt;
	-- Tile size in pixels; it becomes the font-size of the board, and the&lt;br /&gt;
	-- stylesheet measures everything in em.&lt;br /&gt;
	local size = tonumber(nonBlank(args[rows * cols + 1])) or DEFAULT_SIZE&lt;br /&gt;
&lt;br /&gt;
	-- Default tile for an empty intersection.&lt;br /&gt;
	local function defaultCode(r, c)&lt;br /&gt;
		local v = &amp;#039;&amp;#039;&lt;br /&gt;
		if r == 1 then&lt;br /&gt;
			v = up&lt;br /&gt;
		elseif r == rows then&lt;br /&gt;
			v = down&lt;br /&gt;
		end&lt;br /&gt;
		local h = &amp;#039;&amp;#039;&lt;br /&gt;
		if c == 1 then&lt;br /&gt;
			h = left&lt;br /&gt;
		elseif c == cols then&lt;br /&gt;
			h = right&lt;br /&gt;
		end&lt;br /&gt;
		local code = v .. h&lt;br /&gt;
		if code == &amp;#039;&amp;#039; and hoshi[r .. &amp;#039;,&amp;#039; .. c] then&lt;br /&gt;
			return &amp;#039;-&amp;#039;&lt;br /&gt;
		end&lt;br /&gt;
		return code&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local function cellCode(r, c)&lt;br /&gt;
		return nonBlank(args[(r - 1) * cols + c]) or defaultCode(r, c)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local out = {&lt;br /&gt;
		styles(frame),&lt;br /&gt;
		&amp;#039;&amp;lt;div class=&amp;quot;goban&amp;quot; style=&amp;quot;font-size:&amp;#039; .. size .. &amp;#039;px&amp;quot;&amp;gt;&amp;#039;,&lt;br /&gt;
		-- Screen-reader description of the position.&lt;br /&gt;
		accessibility.description(frame, rows, cols, cellCode, args.alt),&lt;br /&gt;
		&amp;#039;&amp;lt;span class=&amp;quot;goban-grid&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;#039;,&lt;br /&gt;
	}&lt;br /&gt;
	local n = #out&lt;br /&gt;
	for r = 1, rows do&lt;br /&gt;
		n = n + 1&lt;br /&gt;
		out[n] = &amp;#039;&amp;lt;span class=&amp;quot;goban-row&amp;quot;&amp;gt;&amp;#039;&lt;br /&gt;
		for c = 1, cols do&lt;br /&gt;
			n = n + 1&lt;br /&gt;
			out[n] = tile(cellCode(r, c))&lt;br /&gt;
		end&lt;br /&gt;
		n = n + 1&lt;br /&gt;
		out[n] = &amp;#039;&amp;lt;/span&amp;gt;&amp;#039;&lt;br /&gt;
	end&lt;br /&gt;
	out[n + 1] = &amp;#039;&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&amp;#039;&lt;br /&gt;
	return table.concat(out)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Entry point for {{#invoke:Goban|board|...}}.&lt;br /&gt;
function p.board(frame)&lt;br /&gt;
	local parent = frame:getParent()&lt;br /&gt;
	local args = parent and parent.args or frame.args&lt;br /&gt;
	-- Allow direct use ({{#invoke:Goban|board|rows=5|cols=5|cell|cell|...}})&lt;br /&gt;
	-- when the invoking page passed no positional cells of its own.&lt;br /&gt;
	if args[1] == nil and frame.args[1] ~= nil then&lt;br /&gt;
		args = frame.args&lt;br /&gt;
	end&lt;br /&gt;
	return p._board(frame.args, args, frame)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Entry point for {{#invoke:Goban|tile|&amp;lt;code&amp;gt;|&amp;lt;size&amp;gt;}}: a single tile, for&lt;br /&gt;
-- documentation.  Screen readers get the code itself.&lt;br /&gt;
function p.tile(frame)&lt;br /&gt;
	local code = trim(frame.args[1]) or &amp;#039;&amp;#039;&lt;br /&gt;
	return p._board(&lt;br /&gt;
		{ rows = 1, cols = 1, u = &amp;#039;&amp;#039;, d = &amp;#039;&amp;#039;, l = &amp;#039;&amp;#039;, r = &amp;#039;&amp;#039; },&lt;br /&gt;
		{ code, frame.args[2], alt = escape(code ~= &amp;#039;&amp;#039; and code or &amp;#039;empty&amp;#039;) },&lt;br /&gt;
		frame)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Wikipedia&gt;Kanayoko</name></author>
	</entry>
</feed>