在我的上一篇博文中,我开发了一款Octopress插件,支持通过{% mahjong cardName %}
在行内快速插入一张麻将牌的图片。当时提到后续的内容是开发一个名为mahjonglist
的插件,另起一行显示一组麻将牌,表示某位玩家的手牌。本篇文章就简要叙述一下开发这个插件的流程。
一、基本语法
本插件的基本语法与之前显示单张麻将牌的mahjong
插件类似,但是因为本插件是一次性显示多张麻将牌,所以基本语法较mahjong
插件也有一些扩展:
1. 多张牌连写:比如我有一串顺子“一万、二万、三万”,如果写成1m2m3m
就有些啰嗦,实际上,日本麻将的玩家更喜欢将其简写为123m
。本插件虽然支持前一种写法,但是更推荐后一种写法,将一些同类的牌连写。
2. 日麻中的鸣牌:即吃、碰和杠,为了看起来更加美观,我们通常将自己的每组吃、碰或杠的牌放到手牌的右侧,且与手牌隔开一定距离。在本插件中可以使用分隔符-
实现。
3. 日麻中的加杠:日麻中加杠的时候会将添加的那张牌放到原本横置的牌上面,即同时有两张牌横置。本插件中支持类似4/44/4p
的写法,用一对/
将加杠的部分包裹起来。
二、实现方法
为了连续显示多张图片,我利用html标签<table><tr>...</tr></table>
创建了一个表格,表格中的每个元素对应一张图片<td><img/></td>
。对于加杠的情况,相当于在原本表格中的一栏里又嵌套了一个两行一列的表格,形如下面代码:
实现加杠的方法
1
2
3
4
5
6
| <td>
<table>
<tr><td><img src="第一张麻将牌的地址"></td></tr>
<tr><td><img src="第二张麻将牌的地址"></td></tr>
</table>
</td>
|
另外就是实现的时候要注意逻辑的严密性,考虑清楚所有可能的输入情况。比如在两个加杠符号/
中包裹的牌数量不等于两张,或者在两个加杠符号/
中出现了分隔符-
,或者一个加杠符号/
还没有合拢整个文本就结束的情况等。具体处理方法参见代码:
mahjonglist.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
| module Jekyll
class MahjongListTag < Liquid::Tag
def initialize(tag_name, markup, tokens)
@successful = true #解析是否成功
@curFlag = -1 #m, s, p或j对应0, 1, 2, 3
@information = "" #存储错误信息
@result = "</tr></table>"
#与加杠有关的信息:
@isInKakan = false
@kakanCount = 0
str = "#{markup}".strip
strLen = str.size
strReversed = str.reverse
@index = 0
while @index < strLen do
curChar = strReversed[@index]
if curChar == "m"
@curFlag = 0
elsif curChar == "s"
@curFlag = 1
elsif curChar == "p"
@curFlag = 2
elsif curChar == "j"
@curFlag = 3
elsif curChar == '-' #分隔符
if @isInKakan
@information = "加杠表示符中不能有分割符"
@successful = false
break
end
curLabel = "<td class=\"mahjongTableElement\"><img class=\"mahjong\" src=\"/images/mahjong/separator.png\"/></td>"
@result = curLabel + @result
@curFlag = -1
elsif curChar == '/' #加杠表示符
if @isInKakan
@isInKakan = false
if @kakanCount != 2
@information = "加杠只允许恰好两张麻将牌横置"
@successful = false
break
end
@kakanCount = 0
@result = "<td class=\"mahjongTableElement\"><table>" + @result
else
@isInKakan = true
@result = "</table></td>" + @result
end
else
if @curFlag < 0
@information = "缺少m、p、s、j标签"
@successful = false
break
else
curLabel = read(strReversed)
if @successful
if @isInKakan #如果读取的是加杠表示符中的内容:
@kakanCount = @kakanCount + 1
if @kakanCount > 2
@information = "加杠只允许恰好两张麻将牌横置"
@successful = false
break
end
@result = "<tr>" + curLabel + "</tr>" + @result
else
@result = curLabel + @result
end
else
break
end
end
end
@index = @index + 1
end
#如果仍在加杠状态中:
if @successful && @isInKakan
@information = "加杠表示符“/”没有结尾"
@successful = false
end
@result = "<table style=\"margin-bottom: 10px;\"><tr>" + @result
super
end
def render(context)
if @successful
@result
else
"<strong>错误:#{@information}</strong>"
end
end
def getPath(isRotated, isAkaDora, cardNumber)
direction = "tate"
name = ""
if isRotated || @isInKakan
direction = "yoko"
end
if isAkaDora
if @curFlag == 0
name = "aka5m"
elsif @curFlag == 1
name = "aka5s"
elsif @curFlag == 2
name = "aka5p"
end
elsif cardNumber == 0
name = "back"
else
name = "#{cardNumber + 9 * @curFlag}"
end
"/images/mahjong/#{direction}/#{name}.png"
end
def read(str)
cardNumber = -1
isRotated = false
isAkaDora = false
getNumber = false #是否读到了编号位?读到编号位就不会再往下读了。
result = ""
until getNumber do
curChar = str[@index]
if curChar
if curChar == "$"
isRotated = true
@index = @index + 1
elsif curChar == "!"
isAkaDora = true
@index = @index + 1
else
getNumber = true
cardNumber = curChar.getbyte(0) - 48
if @curFlag == 3 && (cardNumber < 0 || cardNumber > 7)
@information = "j标签对应的编号只能为0到7"
@successful = false
elsif cardNumber < 0 || cardNumber > 9
@information = "m、p、s标签对应的编号只能为0到9"
@successful = false
elsif isAkaDora && (cardNumber != 5 || @curFlag == 3) #红宝牌必须是五索、五万、五饼。
@information = "红宝牌必须是五万、五饼、五索"
@successful = false
elsif (isRotated || @isInKakan) && cardNumber == 0 #暗牌不能横置
@information = "暗牌不能横置"
@successful = false
else #合法的情况
result = "<td class=\"mahjongTableElement\"><img class=\"mahjong\" src=\"#{getPath(isRotated, isAkaDora, cardNumber)}\"/></td>"
end
end
else
@information = "标签匹配的编号出现错误"
@successful = false
break
end
end
result
end
end
end
Liquid::Template.register_tag('mahjonglist', Jekyll::MahjongListTag)
|
与上一篇博客类似,为了覆盖Octopress的默认表格样式,我们在sass/custom/_styles.scss
文件中添加如下代码:
_styles.scss
1
2
3
4
5
| .mahjongTableElement{
border: 0px;
vertical-align: bottom;
line-height: 0px;
}
|
三、功能测试:
正常情况下的测试:
{% mahjonglist 12345m888s22255j %}
会显示成:
- 当然多张牌不连写也是可以的,比如
{% mahjonglist 12m345m8s8s8s222j55j %}
显示的内容应该与上面一条相同:
- 再测试鸣牌的情况,包括明杠与暗杠,比如
{% mahjonglist 4567p-2$13m-44$44s-0660j %}
会显示成:
- 最后测试加杠的情况,比如
{% mahjonglist 12345!67m-2/22/2p-11/11/j %}
会显示成:
错误情况下的测试:
- 无效宝牌:
{% mahjonglist 12m34!5m8s8s8s222j55j %}
显示为错误:红宝牌必须是五万、五饼、五索;
- 数量不正确的加杠:
{% mahjonglist 12m34!5m8s8s8s55j-/444/4j %}
显示为错误:加杠只允许恰好两张麻将牌横置;
- 加杠符号中间穿插分隔符:
{% mahjonglist 12m34!5m8s8s8s55j-4/4-4/4j %}
显示为错误:加杠表示符中不能有分割符;
- 缺少
m、p、s、j
标签:{% mahjonglist 12m34!5m8s8s8s222j55 %}
显示为错误:缺少m、p、s、j标签;
!
或$
等修饰符前面缺少数字:{% mahjonglist $12345m8s8s8s222j55j %}
显示为错误:标签匹配的编号出现错误;
- 企图横置暗牌:
{% mahjonglist 123456m888s5j-/02/22p %}
显示为错误:暗牌不能横置。
四、总结
到此为止,两款插件mahjong
和mahjonglist
就开发完成了。通过开发这两个插件,我了解到了很多东西,包括简单的ruby程序设计、html网页设计以及css样式表等。今后如果有其他需求,我可能会开发更多的Octopress插件,并且更加深入地学习这些知识。