모듈:마작패

2여 (토론 | 기여)님의 2018년 5월 5일 (토) 11:45 판
설명문서 [편집] [역사] [새로고침]

이 모듈은 문서 내에 마작패 이미지를 쉽게 넣기 위해 만들어졌습니다. 마작패 이미지는 그 특성상 여러 이미지를 연달아 이어넣어야 하는데, 미디어위키의 이미지 삽입 문법을 이용해 하나하나 넣게 되면 오류가 발생하기 쉽고, 수정하기 힘들며 위키텍스트를 보았을 때 그 의미를 한 눈에 알아보기 쉽지 않습니다. 이 모듈은 이러한 문제점을 해결하기 위해, 사용자 친화적인 방식으로 마작패 이미지를 넣도록 해 줍니다.

마작패 모듈에는 크게 두 가지 메인 함수와 나머지 보조 함수가 있습니다. 메인 함수 두 가지는 '파싱' 함수와 '출력' 함수입니다.

마작 자체에 대한 자세한 설명은 마작 문서를 참고합니다.이 틀의 상세한 예는 리치마작 문서에서 확인할 수 있으며, 이 틀의 설명 문서를 수정하려면 모듈:마작패/설명문서 문서를 수정합니다.

간단한 사용법

{{#invoke:마작패|파싱|123456789만}}

마작-1만.png마작-2만.png마작-3만.png마작-4만.png마작-5만.png마작-6만.png마작-7만.png마작-8만.png마작-9만.png

'파싱' 이라는 함수 이름 뒤에 구분자 기호 |를 넣고, 그 뒤에 표기법대로 원하는 마작패를 쓰면 됩니다.

마작패 모듈의 표기법

모듈:마작패 에서는 간결한 표기를 위해 각 마작패의 이름에서 한글자씩을 따와 대표어로 사용합니다. 특수한 기호를 대표어 앞에 넣어 추가적인 기능을 사용할 수도 있습니다.

수패
만수패는 '만', 통수패는 '통', 삭수패는 '삭' 이라는 대표어를 씁니다. 대표어 앞에 숫자를 1부터 9까지 쓰며, 숫자 사이에 대표어를 쓸 필요는 없습니다.
111222333444만
자패
자패는 바람패와 삼원패를 각각 '동남서북백발중' 이라는 대표어로 대신합니다.
적도라
적도라는 수패 5에만 있으며, '적5'라는 대표어를 적어 표시합니다.
1234적56789통
뒷면
깡 등으로 인해 뒷면을 표기해야 할 때는 '뒤'라는 대표어를 적어 표시합니다.
뒤22만뒤
'치', '펑' 등으로 인해 가로로 눕힌 패의 표기
눕혀서 표기해야 하는 패 바로 앞에 - 기호를 적어 표기합니다.
북북-북
소명깡(가깡)을 표기하기 위한 이중 표기
눕혀진 패 두 개가 겹쳐져서 표기해야 하는 패 바로 앞에 / 기호를 적어 표기합니다.
/222삭

파싱 함수

파싱 함수는 연속된 마작패 이미지를 출력합니다. '파싱'이라는 함수명 뒤에 구분자를 적고, 표기법에 따라 마작패를 적어줍니다. 중간에 띄어쓰기가 있어도 무시하게 됩니다. 다음은 각 상황에 대한 사용법의 예시입니다.

수패 및 적도라
{{#invoke:마작패|파싱|234적5678삭}}
마작-2삭.png마작-3삭.png마작-4삭.png마작-적5삭.png마작-6삭.png마작-7삭.png마작-8삭.png
자패
{{#invoke:마작패|파싱|동남서북백발중}}
마작-동.png마작-남.png마작-서.png마작-북.png마작-백.png마작-발.png마작-중.png
가로로 눕힌 패
입력한 값 실제 표시되는 모습
{{#invoke:마작패|파싱|-345통}} 마작-3통-누움.png마작-4통.png마작-5통.png
{{#invoke:마작패|파싱|발발 -발}} 마작-발.png마작-발.png마작-발-누움.png
가깡을 위한 이중 표기
입력한 값 실제 표시되는 모습
{{#invoke:마작패|파싱|/345통}}
마작-3통-누움.png
마작-3통-누움.png
마작-4통.png마작-5통.png
{{#invoke:마작패|파싱|발발 /발}}
마작-발.png마작-발.png
마작-발-누움.png
마작-발-누움.png
표기법 조합
입력한 값 실제 표시되는 모습
{{#invoke:마작패|파싱|999 123통}} 마작-9통.png마작-9통.png마작-9통.png마작-1통.png마작-2통.png마작-3통.png
{{#invoke:마작패|파싱|북 123만 중}} 마작-북.png마작-1만.png마작-2만.png마작-3만.png마작-중.png
{{#invoke:마작패|파싱|123만적589삭2통}} 마작-1만.png마작-2만.png마작-3만.png마작-적5삭.png마작-8삭.png마작-9삭.png마작-2통.png
{{#invoke:마작패|파싱|999 99 9만}} 마작-9만.png마작-9만.png마작-9만.png마작-9만.png마작-9만.png마작-9만.png

출력 함수

출력 함수는 파싱 함수의 확장으로, 여러 패를 구분해서 입력해야 하는 경우에 사용합니다. 특히, 펑이나 치, 깡 등을 간단하게 입력하는 데에 최적화되어 있습니다. 파싱 함수와 동일한 요령으로 여러 표기법을 제한없이 적다가, 패의 구분이 필요할 때에 구분 기호 | 를 사용하여 입력하면 됩니다. 표기법 중간에 공백이 입력되면 단순히 무시합니다.

출력 함수에서는 #마작패 모듈의 표기법에 있는 표기법 외에도 펑이나 치, 깡을 위한 추가적인 표기법을 사용할 수 있습니다. 그러나 추가 표기법은 반드시 구분 기호로 분리하여 적어야 합니다.

펑 표기법
'펑'이라는 대표어 뒤에 가져온 패를 적고, 바로 이어서 소괄호 안에 누구에게 가져왔는지(상가, 대가 또는 하가) 적습니다.
{{#invoke:마작패|출력|펑4만 (대가)}} 마작-4만.png마작-4만-누움.png마작-4만.png
치 표기법
'치'라는 대표어 뒤에 가져온 패를 적고, 나머지 두 패를 소괄호 안에 적습니다.
{{#invoke:마작패|출력|치 8삭(79삭)}} 마작-7삭-누움.png마작-9삭.png
깡 표기법
'깡'이라는 대표어 뒤에 가져온 패를 적습니다. 안깡인 경우에는 그대로 두고, 타가에게서 받아온 경우에는 펑과 마찬가지로 소괄호 안에 적습니다.
{{#invoke:마작패|출력|깡 발}} 마작-뒤.png마작-뒤.png마작-발.png
{{#invoke:마작패|출력|깡중(하가)}} 마작-중.png마작-중.png마작-중.png마작-중-누움.png
가깡 표기법
파싱 함수의 이중 표기와 유사한 표기법을 사용하며, '펑' 또는 '깡' 대표어 뒤에 / 기호를 사용합니다.
{{#invoke:마작패|출력|펑 /8통 (상가)}} Lua 오류: bad argument #1 to 'match' (string is not UTF-8).
{{#invoke:마작패|출력|깡/서(하가)}}
마작-서.png
표기법 조합
출력 함수는 표기법을 조합해서 구분자 기호를 사용해 여러 패를 동시에 적을 수 있습니다. 물론, 추가된 펑/치/깡 표기법을 사용하지 않아도 됩니다.
입력한 값 실제 표시되는 모습
{{#invoke:마작패|출력|567만|8888통|백백| 깡 발(상가) |중}} 마작-5만.png마작-6만.png마작-7만.png 마작-8통.png마작-8통.png마작-8통.png마작-8통.png 마작-백.png마작-백.png 마작-발.png 마작-중.png
{{#invoke:마작패|출력|깡 동|남남 / 남|깡 서 (대가)|뒤북북뒤|11통}} 마작-뒤.png마작-뒤.png마작-동.png 
마작-남.png마작-남.png
마작-남-누움.png
마작-남-누움.png
 마작-서.png 마작-뒤.png마작-북.png마작-북.png마작-뒤.png 마작-1통.png마작-1통.png
{{#invoke:마작패|출력|깡4만(대가)|123 만|9 통|펑 /4만(상가)}} Lua 오류: bad argument #1 to 'match' (string is not UTF-8).
{{#invoke:마작패|출력|938삭 726만|깡 9통(하가)}} 마작-9삭.png마작-3삭.png마작-8삭.png마작-7만.png마작-2만.png마작-6만.png 마작-9통.png

기타 옵션

옵션 값을 통해 출력 결과물을 조정하거나 기타 정보를 추가할 수 있습니다. 옵션의 순서는 무관하며, {옵션 이름} = {옵션 값} 형식으로 적습니다.

게임과 자리 표기법
현재 게임의 국 수와 자리를 옵션으로 표기할 수 있습니다. 옵션은 '게임'과 '자리'라는 이름을 사용합니다.
{{#invoke:마작패|출력|23만|444555666통|북북|게임=동1국|자리=남}}
(동1국 남가) 마작-2만.png마작-3만.png 마작-4통.png마작-4통.png마작-4통.png마작-5통.png마작-5통.png마작-5통.png마작-6통.png마작-6통.png마작-6통.png 마작-북.png마작-북.png
{{#invoke:마작패|출력|서서|백백백|자리=서|999삭|234만|55통}}
(서가) 마작-서.png마작-서.png 마작-백.png마작-백.png마작-백.png 마작-9삭.png마작-9삭.png마작-9삭.png 마작-2만.png마작-3만.png마작-4만.png 마작-5통.png마작-5통.png
도라와 쯔모, 론패 표기법
각각 '도라'와 '쯔모', '론'이라는 옵션을 사용합니다. 옵션의 순서는 무관합니다. 쯔모와 론 옵션은 동시에 사용할 수 없습니다.
{{#invoke:마작패|출력|서서|북북북|999만|345통|55통|자리=서|쯔모=적5통}}
(서가) 마작-서.png마작-서.png 마작-북.png마작-북.png마작-북.png 마작-9만.png마작-9만.png마작-9만.png 마작-3통.png마작-4통.png마작-5통.png 마작-5통.png마작-5통.png 쯔모마작-적5통.png
{{#invoke:마작패|출력|1통|111만|999만|111삭|999삭|론=1통|도라=19만19삭|자리=동|게임=남4국}}
(남4국 동가) 마작-1통.png 마작-1만.png마작-1만.png마작-1만.png 마작-9만.png마작-9만.png마작-9만.png 마작-1삭.png마작-1삭.png마작-1삭.png 마작-9삭.png마작-9삭.png마작-9삭.png 론마작-1통.png 도라마작-1만.png마작-9만.png마작-1삭.png마작-9삭.png
이미지 크기 조절

size 옵션을 사용하여 이미지의 가로 크기를 조정할 수 있습니다. 현재 업로드된 이미지는 가로 66px, 세로 90px의 이미지로서 이미지의 왜곡이 발생하지 않게 하기 위해서는 소수점이 발생하지 않는 11의 배수로 입력해야 합니다 (반드시 그래야 하는 것은 아닙니다). 기본값은 33입니다.

  • {{#invoke:마작패|출력|123-456만|size=22}} 마작-1만.png마작-2만.png마작-3만.png마작-4만-누움.png마작-5만.png마작-6만.png
  • {{#invoke:마작패|출력|123-456만|size=37}} 마작-1만.png마작-2만.png마작-3만.png마작-4만-누움.png마작-5만.png마작-6만.png
  • {{#invoke:마작패|출력|123-456만|size=44}} 마작-1만.png마작-2만.png마작-3만.png마작-4만-누움.png마작-5만.png마작-6만.png

보조 함수들

보조 함수는 위 파싱 함수와 출력 함수의 단축키 역할을 합니다. 일반적으로 많이 사용될 것으로 예상되는 형태의 패를 미리 설정해 두었습니다.

입력한 값 실제 표시되는 모습
{{#invoke:마작패|만수패}} 마작-1만.png마작-2만.png마작-3만.png마작-4만.png마작-5만.png마작-6만.png마작-7만.png마작-8만.png마작-9만.png
{{#invoke:마작패|통수패}} 마작-1통.png마작-2통.png마작-3통.png마작-4통.png마작-5통.png마작-6통.png마작-7통.png마작-8통.png마작-9통.png
{{#invoke:마작패|삭수패}} 마작-1삭.png마작-2삭.png마작-3삭.png마작-4삭.png마작-5삭.png마작-6삭.png마작-7삭.png마작-8삭.png마작-9삭.png
{{#invoke:마작패|바람패}} 마작-동.png마작-남.png마작-서.png마작-북.png
{{#invoke:마작패|삼원패}} 마작-백.png마작-발.png마작-중.png
{{#invoke:마작패|자패}} 마작-동.png마작-남.png마작-서.png마작-북.png마작-백.png마작-발.png마작-중.png
연습장이나 사용자 문서에서 틀의 사용이나 수정을 연습할 수 있습니다.
분류는 /설명문서에 넣어주세요.

--[[
--   token 변수는 실제 사용자로부터 입력받는 글자, 또는 화면에 출력되는 글자를
--   정의합니다. 만약 이 모듈을 포크해서 다른 위키로 가져가는 경우 이 부분을 필
--   요에 따라 다르게 수정하면 됩니다.
--]]
local token = {
-- mw 파일 표기 prefix--
	file_header = '파일:마작-',
	img_side = '-누움',

-- 수패 --
	man = '만', pin = '통', sou = '삭',

-- 자패 --
	E = '동', S = '남', W = '서', N = '북',
	white = '백', green = '발', red = '중',

-- 꽃패 --
--[[ 꽃패 사용 시 여기와 하단의 주석들을 지워 주세요
	f1 = '춘', f2 = '하', f3 = '추', f4 = '동',
	f5 = '매', f6 = '난', f7 = '국', f8 = '죽',
--]]

-- 펑, 치, 깡 --
	pon = '펑', chi = '치', kan = '깡',
	left = '상가', face = '대가', right = '하가',

-- 뒷면 --
	back = '뒤',

-- 특수표기 --
	dora = '적',
	side = '-',
	double = '/',

-- 쯔모, 론, 도라, 게임 등의 옵션들 --
	tsumo_ = '쯔모',
	ron_ = '론',
	dora_ = '도라',
	wind_ = '자리',
	side_ = '가',
	game_ = '게임',

-- 에러메시지 --
	error = {
		default = "에러가 발생했습니다. 구문을 확인해주세요",
		body = "패가 잘못되었습니다. 구문을 확인해주세요",
		braket = "괄호가 없거나 잘못되었습니다. 구문을 확인해주세요"
	},

-- 공백 --
	space = ' '
}

-- 이미지 기본값 --
local image_default_size = 33
local image_real_width = 66
local image_real_height = 90

--[[ 헬퍼 함수들 ]]--
local ustring = mw.ustring
local image_ratio = function (x) return math.floor(x * image_real_height / image_real_width) end

-- switch-case 문을 위한 함수 --
-- from http://lua-users.org/wiki/SwitchStatement --
local function switch (t)
	t.case = function (self, x)
		local f = self[x] or self.default
		if f then
			if type(f) == "function" then
				f(x, self)
			else
				error("case "..tostring(x).." not a function")
			end
		end
	end
	return t
end

-- mw용 이미지 문법을 생성해주는 함수 --
local function toImg ( data, width, height, wrapper, left )
	width = '|' .. width .. 'px'
	local result = '[[' .. token.file_header .. data .. '.png'..width..'|link=|bottom]]'

	if (wrapper) then
		result = '<div style="margin-bottom: -15px;bottom:'..height..'px;left:'..left..'px;display: inline-block;"><div style="display: block;">'..result..'</div>'
	end
	return result
end

-- 에러 출력용 함수 --
local function err ( message, pos, func )
	func = (func ~= nil) and '['..func..'] ' or ''
	pos = (pos ~= nil) and 'p'..pos..': ' or ''
	error(func..pos..message)
end

-- 펑/깡을 위한 방향 체크 --
local pon_dir = {
	[token.left] = 1,
	[token.face] = 2,
	[token.right] = 3
}
local kan_dir = {
	[token.left] = 1,
	[token.face] = 2,
	[token.right] = 4
}

--[[ 입력받은 값을 분석하는 메인 함수 ]]--
local function single_parse ( data, options )
	if (data == nil) then err(token.error.default) end

	local index = 0
	local stack = {}
	local result = ""
	local post = ""
	local pos = 0
	local leftpx = 0
	local wrapper = false
	local global_wrapper = false

	local size = options.size or image_default_size

	local token_parse = {
		-- 특수표기 --
		[token.dora] = function (x) stack[index+1] = {pre = x} end,
		[token.side] = function (x) post = token.img_side end,
		[token.double] = function (x) wrapper = true ; post = token.img_side end,
		default = function (x) return true end
	}

	-- 수패 --
	local function numbers (x)
		for i = 1, index do
			local width =  (stack[i].post ~= '') and image_ratio(size) or size
			local caption = (stack[i].pre or '') .. stack[i].main .. x .. (stack[i].post or '')
			result = result .. toImg( caption, width, size, stack[i].wrapper, leftpx )
			if (stack[i].wrapper) then -- 이중 처리
				result = result .. toImg( caption, width, size, false, leftpx ) .. '</div>'
			end
			leftpx = leftpx + width
		end
		index = 0 ; stack = {} ; post = ""
	end
	token_parse[token.man], token_parse[token.pin], token_parse[token.sou] = numbers, numbers, numbers

	-- 자패, 뒷면 --
	local function strings (x)
		local width = (post ~= '') and image_ratio(size) or size
		result = result .. toImg( x..post, width, size, wrapper, leftpx )
		if (wrapper) then -- 이중 처리
			result = result .. toImg( x..post, width, size, false, leftpx ) .. '</div>'
		end
		leftpx = leftpx + width
		index = 0 ; stack = {} ; post = ""
	end
	token_parse[token.E] = strings
	token_parse[token.S] = strings
	token_parse[token.W] = strings
	token_parse[token.N] = strings
	token_parse[token.white] = strings
	token_parse[token.green] = strings
	token_parse[token.red] = strings
	token_parse[token.back] = strings

	-- 꽃패 --
	--[[ 꽃패 사용 시 이 주석을 지워 주세요
	token_parse[token.f1] = strings
	token_parse[token.f2] = strings
	token_parse[token.f3] = strings
	token_parse[token.f4] = strings
	token_parse[token.f5] = strings
	token_parse[token.f6] = strings
	token_parse[token.f7] = strings
	token_parse[token.f8] = strings
	--]]

	-- 치 --
	token_parse[token.chi] = function (x)
		local body = ustring.match(data, "%d?.") or ''
		if (#body == 0) then err(token.error.body, pos, token.chi) end
		local tail = ustring.match(data, "%d?.%((.-)%)") or '' -- 괄호 부분 체크
		if (#tail == 0) then err(token.error.braket, pos, token.chi) end
		data = data:sub(#body+#tail+3) -- 괄호 기호 (2개) 포함

		pos = pos + ustring.len(body..tail) + 2

		result = result .. single_parse( token.side..body..tail, options ) -- 치는 반드시 상가에게만 받게됨
	end

	-- 펑 --
	token_parse[token.pon] = function (x)
		local body = ustring.match(data, "/?%d?.") or ''
		if (#body == 0) then err(token.error.body, pos, token.pon) end
		local tail = ustring.match(data, "%d?.%((.-)%)") or '' -- 괄호 부분 체크
		if (#tail == 0) then err(token.error.braket, pos, token.pon) end
		data = data:sub(#body+#tail+3) -- 괄호 기호 (2개) 포함

		pos = pos + ustring.len(body..tail) + 2

		local dir = pon_dir[tail] or -1
		local selector = token.side
		local caption = ''

		local wrapper = false

		-- 가깡 처리 --
		if (body:sub(1, 1) == '/') then
			body = body:sub(2)
			selector = token.double
			wrapper = true
		end

		-- 방향에 맞게 캡션 처리 --
		for i = 1, 3 do
			if (i == dir) then caption = caption .. selector end
			caption = caption .. body
		end

		result = result .. single_parse( caption, options )
	end

	-- 깡 --
	token_parse[token.kan] = function (x)
		local body = ustring.match(data, "%d?.") or ''
		if (#body == 0) then err(token.error.body, pos, token.kan) end
		local tail = ustring.match(data, "%d?.%((.-)%)") or '' -- 괄호 부분 체크
		data = data:sub(#body+1)
		if (#tail > 0) then data:sub(#tail+3); pos = pos + 2 end -- 괄호 기호 (2개) 포함

		pos = pos + ustring.len(body..tail)

		local caption = ''
		if (#tail == 0) then
			-- 안깡 --
			caption = token.back .. body .. body .. token.back
		else
			-- 대명깡 --
			local dir = kan_dir[tail] or -1
			for i = 1, 4 do
				if (i == dir) then caption = caption .. token.side end
				caption = caption .. body
			end
		end

		result = result .. single_parse( caption, options )
	end

	-- switch 문 구성 --
	token_parse = switch(token_parse)

	-- utf8 문자별로 루프 진행
	while #data > 0 do
		local c = ustring.match(data, ".") -- data:match("[%z\1-\127\194-\244][\128-\191]*")
		data = data:sub(#c+1) ; pos = pos + 1

		local may_int = tonumber(c) or -1
		if (may_int >= 1 and may_int <= 9) then -- 수패에 적용되는 숫자 입력이 들어온 경우
			index = index + 1
			stack[index] = stack[index] or {} -- 스택에 이미 들어있던 값은 유지한다

			-- 필요한 값들을 스택에 넣는다
			stack[index].main = c ; stack[index].post = post ; stack[index].wrapper = wrapper
			post = "" ; wrapper = false
		else
			token_parse:case(c) -- 문자가 들어오는 경우는 위 select-case 문에 따라 처리
		end

		global_wrapper = global_wrapper or wrapper
	end

	if (global_wrapper) then
		result = '<div class="majong" style="display:inline-block;margin-top:'..(size * 2 - image_ratio(size))..'px;">'..result..'</div>'
	end

	return result
end

local function parse ( frame ) -- '파싱' 함수 entry point
	return single_parse( frame.args[1]:match("^%s*(.-)%s*$"), frame.args ) -- trim
end

local function print ( frame ) -- '출력' 함수 entry point
	local result = ''
	local data = {}

	for k, line in pairs(frame.args) do -- 옵션값을 제외한 나머지 입력값들을 data에 모음
		if (type(k) == "number") then data[k] = line end
	end

	for i = 1, #data do
		result = result .. single_parse( data[i]:match("^%s*(.-)%s*$"), frame.args ) -- trim
		if (i ~= #data) then result = result .. token.space end
	end

	-- 게임 옵션 출력 --
	local game = ''
	if  (frame.args[token.game_] ~= nil) then
		game = frame.args[token.game_]
	end

	-- 자리 출력 --
	if (frame.args[token.wind_] ~= nil) then
		if (game ~= '') then game = game .. token.space end
		game = game .. frame.args[token.wind_] .. token.side_
	end
	if (game ~= '') then result = '(' .. game .. ')' .. token.space .. result end

	-- 쯔모패 출력 --
	if (frame.args[token.tsumo_] ~= nil) then
		result = result .. token.space .. token.tsumo_ .. single_parse(frame.args[token.tsumo_], frame.args)
	elseif (frame.args[token.ron_] ~= nil) then
		result = result .. token.space .. token.ron_ .. single_parse(frame.args[token.ron_], frame.args)
	end

	-- 도라패 출력 --
	if (frame.args[token.dora_] ~= nil) then
		result = result .. token.space .. token.dora_ .. single_parse(frame.args[token.dora_], frame.args)
	end

	return result
end

return {
	['파싱'] = parse,
	['출력'] = print,

-- 자주 쓰이는 함수들 --
	['만수패'] = function (f) return single_parse( "123456789만", f ) end,
	['통수패'] = function (f) return single_parse( "123456789통", f ) end,
	['삭수패'] = function (f) return single_parse( "123456789삭", f ) end,
	['자패'] = function (f) return single_parse( "동남서북백발중", f ) end,
	['바람패'] = function (f) return single_parse( "동남서북", f ) end,
--  ['꽃패'] = function (f) return single_parse( "춘하추동매난국죽", f ) end,
	['삼원패'] = function (f) return single_parse( "백발중", f ) end
}