BIA3

好像在我们团队建立的初期,那还是两年多前,就有了打枪的传统。噢,这里要解释一下,这个打枪是指在手机上的FPS(第一人称射击)游戏,不是那个意思啦。那时还是玩BIA2(兄弟连),这个游戏太老了,以至于后面IOS升级后没法玩,官方也出了BIA3,但是联机不是这个游戏的特长,所以有很长的一段时间,打枪文化逐渐消退了。直到最近两个月,发现CFM(穿越火线手游版)可以联机实时PK,我们的这个文化又浮出了水面。每天中午吃完饭休息的时候都要来个三局两胜。你以为这篇是要介绍CFM,那就真的错了,下面我要给大家介绍一下我们打枪背后的那些事,其实就是我们的打枪计分系统。

计分系统1.0

这个计分系统的发起人是Johnny,Johnny喜欢数据,什么都喜欢搞个报表,他一开始的想法是搞个Excel,把我们每局的比分记下来,然后可以看到每个人的发挥趋势和排名;再一想还是搞个系统吧,有研究Excel的工夫,系统都写好了。然后吭哧吭哧,计分系统1.0就出来了。就是下图的样子:

这个其实还是两年前的那个版本。前段时间Johnny又加了一个胜率榜,可以看到谁跟谁组合胜率更高,见下图:

我们每次打完都要看下排名有没有变化,不过第一名就像一个bug样的存在,每个月赛结束总能高出第二名300多分,他就是我们的“录分员”小鑫鑫,录分是给他杀我们这么惨烈的惩罚。

计分系统2.0

1.0的版本已经可以录成绩,看数据了,那怎么还要2.0的版本。这是个好问题啊,就像你问你们产品经理为什么又要改版一样。这时你们产品经理会幽怨地看着你:客户需要啊。没错,2.0版是需求驱动的,只不过需求方就是我们自己。

忘记说了,现在打枪的都是我们的工程师,现在有7个人。既然说到这,我就介绍一下这7个guys:

  • Johnny:闪闪的人民币玩家呀
  • 金刚:BIA时代的枭雄,现在可以在猥琐的角落蹲到比赛结束
  • 超人:相当生猛,总能把队友坑得不要不要
  • 教练:狙神,瞬狙盲狙都不在话下
  • 鑫鑫:最讨厌优秀的人比我们还努力,就是说他,现在经常限制他只用手枪
  • 小旭:创造过一杀零死的辉煌战绩
  • 我:我就是我,是颜色不一样的烟火

每次组队前,我们都要纠结个一分钟,谁都想抱小鑫鑫的大腿,但是腿只有两条,总要分出一个3v4的队形,如果是4v3,那就是有人厚颜无耻拽住了他的第三条腿(污~~),纠结完分组又要纠结打什么图。既然都这么纠结,干嘛不交给程序自动分组,随机选图。于是2.0版就增加了这两个功能:

随机选图就是从数组里随机挑一项,这个很简单。至于自动分组,这真的是个难题。

自动分组

直接跟选图一样,搞个随机的出来不就完了么。随机是简单,但随机出来的队形很有可能一家欢喜一家愁,公平分组才是解决之道。怎么保证公平,1.0版中不是都记录了比赛成绩,按比赛成绩分,让两对分值加和相差不大。是的,描述起来就是这么简单的一句话,转换成程序,真是一个“难题”。这很类似一个经典的NP难题(看,我没唬你们吧),即一维装箱问题,打个比方:

  1. 假设所有人总分加起来是100
  2. 把两队看成两个箱子,每个箱子容积都是50
  3. 把一个箱子装满到50,剩下的都装另一个箱子
  4. 两队总分就都是50了

看起来不错哦,但是上面说了是类似,那不同的地方:

  1. 两队不可能总能分成50:50
  2. 怎么避免1v6这种队形(有小鑫鑫这个bug)
  3. 成绩还有负数啊,装箱的容积怎么能是负数

所以我们要给装箱问题进行一些转换和限制,如:将负数补齐成正数;限制每个箱子至少装3个,最多装4个。具体算法可参考我的github:iammapping/autoGroup

似乎到这应该结束了,如果你这么认为,那就太低估我们工程师的折腾劲了。在2.0中还加入了一个重量级功能——自动录分。

自动录分

我们用录分来惩罚小鑫鑫,每局打完他都会把游戏结果截图,在全部结束后,把成绩录到计分系统里。罚在他身,痛在我们心呀。痛不是心疼他,是要焦急地等他录完成绩看最新排名。人工来录不仅慢还容易犯错,那就自动化吧。

方案验证

这里面最主要的难点就是如何识别出截图里的名字和比分,于是找到了开源的OCR程序tesseract,把整张截图给它识别:

> tesseract hh.png stdout -l eng+chi_sim

结果输出了一坨,我的天呐,真的是一坨:

这没办法解析啊。于是我把名字和比分单独截出来给它识别:

哇塞,它认出来了。确定这个方案可行,那就可以开干了。不会切图的前端不是好工程师,在Fw里标记了每个切割点的区域:

这里为了避免一张图片定位不准确,拿了多张图片调透明重叠在一起,方便找出比较准确的定位。

方案实施

从上图中切片得到位置信息,定义成php数组:

protected $imgMap = array(
	'1334x750' => array(
		'score' => array(
			'left' => array(468, 87, 66, 35),
			'right' => array(797, 87, 66, 35)
		),
		'players' => array(
			'left' => array(
				array(
					'name' => array(208, 176, 188, 27),
					'score' => array(353, 203, 90, 27)
				),
				array(
					'name' => array(208, 268, 188, 27),
					'score' => array(353, 295, 90, 27)
				),
				array(
					'name' => array(208, 360, 188, 27),
					'score' => array(353, 387, 90, 27)
				),
				array(
					'name' => array(208, 453, 188, 27),
					'score' => array(353, 480, 90, 27)
				),
				array(
					'name' => array(208, 547, 188, 27),
					'score' => array(353, 574, 90, 27)
				)
			),
			'right' => array(
				array(
					'name' => array(771, 176, 188, 27),
					'score' => array(920, 203, 90, 27)
				),
				array(
					'name' => array(771, 268, 188, 27),
					'score' => array(920, 295, 90, 27)
				),
				array(
					'name' => array(771, 360, 188, 27),
					'score' => array(920, 387, 90, 27)
				),
				array(
					'name' => array(771, 453, 188, 27),
					'score' => array(920, 480, 90, 27)
				),
				array(
					'name' => array(771, 547, 188, 27),
					'score' => array(920, 574, 90, 27)
				)
			)
		)
	)
);

1334x750是iphone6截图的尺寸,如有必要也可以扩展出其他尺寸的,不过标记位置信息真是个体力活。

大致步骤如下:

  1. 提供比分图片上传接口
  2. 上传后按定义好的的位置切分图片
  3. 使用tesseract splice.png stdout -l eng+chi_sim识别切片的文字,参考:https://github.com/thiagoalessio/tesseract-ocr-for-php
  4. 获取每个位置的识别结果。位置1和位置2是总比分,用来判断比赛输赢;位置3用来识别名字;位置4用来识别个人比分,如17/11,将17记作kill,将11记作die
  5. 位置2数字更大,则右边所有的player都标记为胜利;如果相等,则都不不标记胜利;否则,左边所有的player都标记胜利
  6. 将识别的结果自动填入,可人工校验修改

识别的核心代码参考:Recognize_Score_Model。将图片灰度化和反色对识别速度和准确度都没有太大的影响,就取消了。

完成?

自动录分程序一上,识别的速度和准确度都非常惊人,是惊人的慢和惊人的错误,用下来发现还没人工录入的效率高,不说名字,就连数字3568都分不清,这怎么能忍。主要问题还是出在了tesseract上,一定要想办法优化它的效率,好在它是可以训练的,于是按照这篇博客把它训练了一番。提取了一些样本,拼在一张图上训练:

训练顺利的话,会得到一个训练好的文件cf.traineddata

把这个文件放到/usr/local/share/tessdata/中,在识别的时候指定langcf就可以了,由于我们识别的图片都是切好的单个单词,可以指定psm8,如:

> tesseract test.png stdout -l cf -psm 8

这样一优化,识别的速度和准确度都非常惊人,这次是惊人的快和惊人的准确。速度变快这是出乎意料的,不过再一想也合情合理,之前要在整个英文库和简体中文库匹配,现在只要匹配我们这几个名字和数字就可以了;准确度现在可达到99%,实测下来是完全准确的,扣1分不让它骄傲吧。

真的完成了

现在我们比赛结束,小鑫鑫只要在他iphone6的safari中打开上传页点击上传,勾选每局的截图上传,然后静候3s,以前手工可是要1分多钟,效率提高了至少20倍,上传成功后,成绩就会自动填入下图的表单,还做了一个简单的校验,如果识别有误就像左边出现红色警告,如果像右边都是绿色那就大可放心地点下绿色按钮保存成绩了。

结束

计分系统1.0和2.0差不多都是Johnny和我在业余时间开发,2.0差不多花了我4-5个晚上。短期来看折腾这些BB有意思么,花了那么多时间;但长期来看,节省的时间更多了,个人的见识增长了。

爱折腾,但不瞎折腾;崇尚高效,拒绝重复。这就是我们的工程师文化,也是我们的打枪文化。

题图引自:BIA3