2015年小结

现在已经是2016年的4月份,又到了抽h1b的时候。种种不确定性再次萦绕在心头,一如一年前的这个时候。尽管一拖再拖,我决定还是要写一下这个总结。

2015年对我算是一个重大的改变,发生了很多事情。现在想想都很遥远似乎无关痛痒,但当时身处其中的感受是很清晰的。

年初的时候在准备Epic的各种烦人的考试。在那里每个人都带着抵触的情绪去学医疗数据方面的东西,同时寄希望于一点小聪明和运气可以在考试的时候低空飞过,传闻如果挂掉的次数太多的话可能就会丢掉工作,这种心情非常矛盾。那段时间我日子过得其实还不错,每天上完课就可以走人,之后和同事去某个中餐馆吃饭,回到家再和小喵视频一会。但是始终有一个问题放在那里,我不能假装看不见它。

二月份的时候小喵拿了亚马逊的offer,与此同时我也开始再一次刷题骑驴找马。onsite以后觉得非常有信心,感觉十拿九稳了。想到马上可以在西雅图团聚我们兴奋异常地讨论之后的度假方案。结果马上就被狠狠打脸了。现在回忆起当时面试的表现其实有很多问题的,然而我自己选择性地无视了。

当时非常沮丧,但如果我知道接下来的几个月会发生什么或许对这种小事就不会在意了。小喵让我高兴点,因为她有一个月的gap,这样可以来麦迪逊陪我呆一段时间。她总是能在这样的时刻给我安慰,让我宽心不少;或许是因为当时我总是极力表现出一副有为青年的样子,这种安慰我现在已经很少听到了。。。

二月份的麦迪逊还非常寒冷,基本没什么地方可去,我们做得最多的就是窝在家里看电影吃东西,或者在网上斗地主。这是我们第一次长时间待在一起。相互都有些拘谨,同时又很开心。这段时间平静又快乐的日子让之前的沮丧完全消失了。我的找工作意志也有所松懈,再加上h1b抽签将近,我们决定等10月份一切都有定论之后再作打算。

在这期间由于人事原因我被调到另一个老板手下,我原来做了一半的project被直接停掉,转而去做一个完全不了解的项目,这让我颇为不爽,除此之外倒也没怎么在意,并继续打我的酱油。然而接下来发生的事让我始料未及。事情始于一个巨傻无比的QA note,简单来说就是在打开菜单的时候原来的代码会打一个日志,’actions take place in XXX’。然后这个QA就说,要是我就打开菜单什么都不做,那这个日志就不准确啊,没有action take place啊,另外你们要把具体什么行为也写到日志,如果没有操作就要写’no action’… 我 f***。读者可能以为不就是改个日志吗,顺手一改又有多难。如果我没有去epic,可能也是这样想。事实上我们组为这个日志建了一个record,专门记录这种行为。如果不了解什么是record,简单讲一下就是epic把所有前端的内容,比如字体,菜单样式,等等都在后端建一个record,要修bug,先改record。然而我发现这个record只负责一件日志,’actions take place in XXX’,这下我懵逼了,如果要实现那位QA大哥的建议,这一个record显然干不过来,所以要建多个record。但是我没有这个权限,建新record是一个新项目,要有设计文档, 以及完整的在各个环境下测试的方案。我找了找,发现最早建这个record的人是我们组一个三哥。我去找三哥。三哥说这个record是和其他一批一起建的,当时经过了两个组的头头一起sign off才决定这样写的,你要不找找这个QA再说说?我回去给QA哥打了个电话,不在,内网一搜,一看还是个senior的,可能比较忙,我发个邮件给他说,哥你看这个东西改不了,你将就吧。过几天他回我啦,我觉得这是你们组的问题啊,讲道理这就是一个bug啊,要不你们组商量下看看怎么搞。我无语,你多找一个bug能多发奖金还是咋的。抄送给我老板看,老板回我说你别管了我去和他说,我就把note扔到一边去了。

过了大概一周,一天早上还在路上突然接到一个电话,居然是我们组大老板,他说你人在哪,你好像有个长时间没处理的QA note,我要和你谈谈。我吓尿了,这时候大概是10点,前一天晚上玩的有点晚所以起晚了,都打到我手机那肯定是找过我了。赶到公司,大老板第一句话就是,你平时都这么晚到公司吗?我一通解释。大老板又问,这个qa note怎么回事,我又一通解释。他看上去并不满意,直接拉出我的project记录,说你看你的工作量比别人少很多,你丫是不是偷懒不好好工作。我明白是我的新老板给我穿小鞋了,他在刚接手我的时候给我提过我的进度有点慢,这货一定是反映给上级了。对于这个老板的为人我虽有耳闻,但不知道他下手如此之重。我都明白了,这一切本身就是醉翁之意不在酒,就不再解释了。大老板说,再给你个机会,一周之内把你手上的项目做完,不然就走人。

我算看清了资本家的嘴脸。直接冲到老板的办公室,这个胖子显然早就知道了这一切,他一脸无辜地说,对于那个qa note我很抱歉,事情太多我忘记问了,但这个是你自己的事,你要自己操心啊;话说你最近的表现确实不太好啊,这个项目你要好好做啊。最后摆出一种‘我也无能为力’的样子。

没时间计较这些,我的当务之急就是完成手上这个一知半解的项目。每天和VB死磕,到处求爷爷告奶奶,期间又遇到数次系统升级,环境挂掉,耽误了不少时间。每次有一点进展,就发现有更多不会的东西横在面前,让人心生绝望。我记得有个理论说人的最佳效率是在任务难度稍稍高于自身能力,需要花一番功夫才能完成时达到;而我当时的情形是完全的手足无措,更不知道能否在限定时间内完成。那一周正逢memorial day假期,公司里没有什么人,我独自坐在四面都是墙壁的办公室加班,桌上堆了泡面和零食。偶尔在茶水间碰到其他加班的人,彼此只是相视一笑,颇有同是天涯沦落人的味道。奇怪的是自己在当时那种悲惨境遇下并没有什么多余的想法,唯一的想法就是尽快完成这个项目。当人处在生存的压力之下,其他一切的问题都不成问题了。

最后算是完成了主要的功能和需求,就去和老板交差了。不用说,这个仓促赶工出来的产品有一些明显的漏洞。我的努力感动了自己,却没能感动我的老板。这件事的最终结局就是公司给我一个月时间自己找后路,并且期间还要上班,完成基本的日常工作。说起来心酸,却也是没有办法的事。职场就是这样,前一分钟大家有说有笑其乐融融,下一秒大难领头各奔东西。所有的繁华都是假象,一切都建立在非常脆弱的基础之上。

未完待续。。。

Hadoop upgrading

在hadoop 从2.0.0 升级到 2.3.0或更高的版本时。运行mapreducey有时会报出类似以下错误:

java.lang.NoSuchMethodError: org.apache.hadoop.conf.Configuration.addDeprecations

java.lang.VerifyError: class org.apache.hadoop.yarn.proto.YarnProtos$ApplicationIdProto overrides final method getUnknownFields.s

job_1395502230567_0001 failed with state FAILED due to: Application application_1395502230567_0001 failed 2 times due to AM Container for appattempt_1395502230567_0001_000002 exited
with exitCode: 1 due to: Exception from container-launch: org.apache.hadoop.util.Shell$ExitCodeException:

这些基本可以断定为版本冲突。比如Configuration.addDeprecations是2.3.0新加入的函数,如果你的hadoop client版本是2.3.0,在使用一些deprecated的configuration时系统会调用这一函数。如果mapreduce.task.classpath.user.precedence被设成true,hadoop就去用户的classpath里找这个方法, 然而在用户自定义的项目中可能使用到一些用hadoop 2.0.0来编译的library,如此一来当系统在此library中寻找该函数的实例时就会报出找不到方法的错误。比如你可以试试在configuration中把mapreduce.task.classpath.user.precedence设成false 有时候就不报错了。因为系统自带的library是2.3.0版本的。当然最彻底的解决方法就是把项目中用到的library重新用新版本的hadoop重新编译一下。

Hadoop 比较大文本文件

最近工作中经常碰到这样的问题:有两个Sequence File,求出他们之间key的交集或者差集;亦或是有一个包含key的map file,需要在一个大文件中找出所有这些key对应的条目。这样的问题利用hadoop自带的性质可以有巧妙的解法。

在这之前有几个事实需要理解:

1、SequenceFile是hdfs自带的key, value格式文件

2、SequenceFile通常根据reducer的数目被分成许多part

3、每个part内部按照key排序,各个part之间是独立关系,全局的key是无序的

直观的想法是把两个文件排序,然后用merge的方法比较。但是刚才说了sequenceFile是局部有序,全局无序。如果我们把两个大文件都排序好了再一行一行比较不仅效率低下还失去了用hadoop的意义。那么利用局部排序的特点能不能做同样的事?可以!

整个过程包含两个mapper过程和一个reducer过程。第一个mapper简单地输出文件1的key,value pair.到一个临时文件,这个临时文件被分成多个part;第二个mapper输出文件2的key, value pair到下游的reducer。之后每个reducer先装载第一个mapper的输出文件的对应part,然后建一个指针用来记录当前所在的行数,对于每一个新分配到的文件2中的key,不断往下移动文件指针判断是否存在文件中就可以了。

这个方法可行的条件是文件1分成的part数目一定要和之后reducer的数目一样,这样可以保证同样的key被partition到同一个reducer,所以reduce task-00001的key只可能在文件part-00001中被找到。

Run GNU Parallel with pipe

The following paragraph can also be found at https://www.gnu.org/software/parallel/parallel_tutorial.html

However somehow it’s buried in the middle.

–pipe

The –pipe functionality puts GNU Parallel in a different mode: Instead of treating the data on stdin (standard input) as arguments for a command to run, the data will be sent to stdin (standard input) of the command.

The typical situation is:

  command_A | command_B | command_C

where command_B is slow, and you want to speed up command_B.

So you can just do this:

command_A | parallel --pipe command_B | command_C

 

Scott语录

今天整理东西的时候看game design的笔记,里面记了一些professor的语录还蛮有意思的。。。

1. If you’re having ramen without sauce everyday, then upgrade your life to ramen with sauce and don’t bother buying the text book.

2. I’m happy with you sitting quietly look suspicious.

3. We are formally behind schedule.

4. If you are opposed to it (extend due date) and really worried that you might be murdered by your classmates, winkle your eyes to me.

5. That was awesome.

6. (ABET people are coming.) They’ll ask you something…actually I have no idea what they gonna ask.

7. Professor: Project two due Thursday.

Student: We have test on Thursday.

Professor: What test?

Student: OS.

Professor: How hard can that be?

8. We have an outstanding issue.

9. (About homework) Drink a cup of coffee, talk to yourself, if nobody is listening.

10. I’m a bad professor.

11. Where does my life go?

Leetcode: Simplify Path

Given an absolute path for a file (Unix-style), simplify it.

For example,
path = "/home/", => "/home"
path = "/a/./b/../../c/", => "/c"

Corner Cases:

  • Did you consider the case where path = "/../"?
    In this case, you should return "/".
  • Another corner case is the path might contain multiple slashes '/' together, such as "/home//foo/".
    In this case, you should ignore redundant slashes and return "/home/foo".

 

这道题如果没见过的话会不知道如何下手。我一开始往正则表达式的方向上考虑,然后就没有然后了。其实只要用栈就行了,如果遇到”..”就pop, 遇到”.”无视。要用到java里的string split把原来的path分成tokens,这个在别的题目里用到的比较少。Corner cases讲到的是在根目录下遇到”..”也要无视,用程序来说也就是栈为空的情况。另外,有重复的”/”的话split出来的是空字符串,这些也要无视掉。最后要注意的是题目里讲清楚了是”absolute path”,也就是一定会以”/”开头的,这样就不会出现”../”之类的情况,而”../”和”/..”必然是不同的,我在这个地方卡了很久也没想到好办法。所以结论就是看清题目很重要!

Leetcode: Largest Rectangle in Histogram

Given n non-negative integers representing the histogram’s bar height where the width of each bar is 1, find the area of largest rectangle in the histogram.

Above is a histogram where width of each bar is 1, given height = [2,1,5,6,2,3].

 

The largest rectangle is shown in the shaded area, which has area = 10 unit.

 

For example,
Given height = [2,1,5,6,2,3],
return 10.

 

又是一道丧心病狂的题目。一开始的想法是双层循环,对每一个 i,计算所有在i左边的长方形面积,比如0到i,1到i。。。长方形的高就是i到那个点之间最小的高度。用这个算法大集合超时,仔细想想也难怪,因为很多结果是重复计算。后来在网上找到了一个优化的办法,大概意思就是,只计算高度下降时候的i的所有左边长方形。可以这样想,对于一个上升的序列,计算某个中间的结果是没有意义的,因为只要高度还在上升,前面的长方形的底边都在不断扩大;而一旦下降就不行了,那些高度超标的长方形就被终结了,所以这个时候对左边的长方形来一次总的清算。这个算法能过大集合,但仍是O(n*n)的复杂度。试想整个序列是一升一降的形式,那和第一种算法就没什么区别了。

继续搜到了一个用栈来保存高度的方法。这个方法实在很精妙我觉得在面试的时候根本不可能想到(其实就连上面那个优化我也想不到)。如果在真实面试中用10分钟想出O(n*n)解法,很有可能接下来的follow up要求写出O(n)的解法,那么剩下的半个多小时基本上就酱油了。所以可能用20分钟来写naive的算法还要好一些,剩下的不多的时间和面试官讨论一下follow up的思路,然后时间快到,面试在愉快的气氛中结束。。。好了回到正题。解释一下用栈来做的思路:一次遍历,如果高度不断上升,那么就把新的高度放到栈顶,一旦碰到高度下降,结算之前高度大于该高度的长方形。上图:

比如从index 1开始,这时候栈里的元素是1,注意这个1是index而不是高度,然后遍历index 2, 3,分别将这两个入栈,然后遇到index 4,发现下降,这时候把3出栈,计算index 3到当前index 的面积,继续2出栈,计算index 2到当前index的面积(红色部分),此时栈顶元素是1,而这个高度小于当前index的高度,所以把当前index 4进栈。这个算法的思想是,对于之前的高度大于当前高度的长方形,结算一次就够了,他们的底边不可能增长了,我们可以把这些index都忘掉,而高度小于当前高度的长方形还有长大的可能,所以保存在栈里。所有的长方形的面积只被计算一次,并且是在它被终结的时候结算,即它的底边不可能再增长的时候。代码不写了,随便一搜就有的。

 

Leetcode Best Time to Buy and Sell Stock I, II, III

This is the trickiest problem I’ve seen so far on leetcode. Given that I’ve just done 25 problems this is not so convincing, but to me I really feel like writing something about this problem. I think this problem is different than many other problems in that it can hardly be classified. For example there are tons of problems that requires recursive backtracking and a bunch of related to graph searching, you can easily tell what problem is what if you have seen enough problems. But this problem is kind of ‘gotcha’ as it is not a string problem, not a list/tree problem, and cannot be classified to recursion (although it seems like but it is totally wrong direction to think). I can imagine if I come across this problem in an interview I would be dead.

I am not going to repeat the problem here, nor will I write any code. I want to describe the correct solution and how I went to the wrong way. First part asks about the max profit with exact one buy and one sell. At first sight probably everyone will think: ok let’s find the max value and min value, but then we realize that it won’t work because the min does not necessarily come before the max value. This is the most interesting feature in this problem series.  Then I tried to think of all the possibilities and  found out that we need to keep looking for minimum value and also looking for maximum value after the minimum, and compare the difference with that of the maximum and minimum pair we found before. This sound awkward but it naturally came to me when I first did it. Then I spent lots of effort on finding those maximum and minimum value and ended up with a TLE. Then I searched for the solution and the correct solution is about 2o lines of code. I felt like an idiot when I saw it. (I wonder if someone had the same feeling so I won’t be so sad) The fact is that it makes a trick and bypasses the effort to find a maximum point.  The idea is that while you are progressing, only update the minimum point you come across, and check the current point against the minimum point to get profit (imagine you buy at minimum and sell at current point).  This way you don’t need to know where the maximum point is. This is not straightforward but is magically true. It is based on the fact that the maximum profit you can make at current point depends on the minimum point appears before it. Moreover, not only you find the profit at a maximum point but every point. This property is also used in part III, but I doubt anyone will think of this when they’ve taken a different (maybe a dumb) path like me.

Part II asks for profit with no limitation on number of buys and sells, but every  buy-sell pair happens after the previous pair finishes. The idea in part I does not apply here, because obviously you should sell when the trend is going down and buy at next minimum. So I went back to my previous thought of finding “minimum-maximum” pair. I just couldn’t resist it. But again there’s a much more elegant way to do this. The code is even shorter than the previous one. The idea is just compare the current point to the previous point, if it’s bigger than the previous one, add the difference to the total profit. This way you don’t miss any increasing pair. The maximum profit is nothing more than adding up these increasing pair altogether. I believe this hidden property is less ambiguous than the one in part I. It naturally capture every up-going trend. The take away from this part for me is to think small, think discretely. I don’t know if I explain this clear but in computer science some tricks are made possible due to the discrete property of the problem.

Ok, finally part III. With the knowledge obtained in part I. Hopefully you already find out that each one of the two buy-sell pairs must have the max profit solution in its session. For example, assume we found out the solution to get max profit then there must exist a point ‘i’ after the first sell and before the second buy (the extreme case is that the first sell happens just the same time with second buy, then ‘i’ would be just that point), then what we have done before ‘i’ had better achieved max profit, otherwise it will contradict to our assumption as we can improve the profit in that session and therefore the total profit, and same thing for the case after ‘i’. But I have to say it is still a little against our instinct. If you directly look at this problem, for example some typical thoughts I have are: ‘Should I find out the first/second high point or first/second low point’, ‘How to catch the first/second steepest slope’, ‘Apparently there are ‘n’ choices for each buy and sell, and the problem would be O(n^4) complexity, seriously?’… Anyway, assume we are on the right track now. For every possible point ‘i’, we apply solution from part I on both left and right side of ‘i’, and we get a max profit for each ‘i’, we pick the biggest one at last, sounds easy right? This is close as OJ rewarded me with a TLE. Think more carefully, solution for part I is O(n), here we add an outer loop so it becomes  O(n^2). It is easy to find out we have done a lot of unnecessary scans. Probably we can memorize something when we are doing one scan. Here we need some dynamic programming. The key point is that the max profit before or after a point is fixed, and the only information we need to calculate  is nothing more than the previous max profit, current min/high point, and the value of ‘i’ itself. All these information are available within one scan–we have one variable track the min/high point, and as we progress we can get the current max profit which becomes ‘previous max profit’ for the next point.

One more tricky thing: How to find the maximum profit after point ‘i’? Previously we scan from left to right, when we scan to ‘i’, we know the result of ‘i-1’, but we have no idea what will happen after ‘i’, unless we scan all the way to right end, but this break the rule of O(n). Here we need to think differently. If we know what will happen after ‘i+1’, then we know what will happen after ‘i’. This brings the idea to scan from right to left.  To be honest this idea still sounds mysterious to me. It’s like travel from future to the past. I’d like to compare the difference in two scans. When scan from left to right, we view the min point as a potential buy point and look into future to find a sell point, but when we scan from right to left, we view the max point as sell point and look for buy point in the past. Why? The only explanation I can think is that there exists an order for ‘buy’ to happen before ‘sell’. To calculate things on the left, we can only assume a ‘buy’ already happened somewhere in the past, because we sell before ‘i’,  the minimum point before ‘i’ is the only time we can buy. Same logic, if we want to calculate things on the right, then there must be a ‘sell’ in the future, because we buy after ‘i’, the maximum point after ‘i’ is the only point we can sell. It feels like someone knows what he did in the future and is looking for an answer in the past. Greetings, time traveller.

 

再谈三国杀

昨天看了一个水木火树回忆自己三国杀心路历程的视频,感触良多。我在之前的文章里批驳了三国杀的现状,现在看来有些观点还是偏激了。火树的很多观点我都认同,只是很多问题从不同的角度看会有不同的答案罢了。

我从09年接触三国杀以来断断续续也有四年多了,其间的经历当然不能和大神相比,但是也足够让我对这个游戏产生深刻的感情了。最早知道三国杀是09年上半年的时候,学院里有人买了实体牌,渐渐在寝室楼风靡起来。我一直有所耳闻但是没有见过。终于有一次从别的宿舍借到牌,才有机会玩到了人生第一盘三国杀。我很清楚地记得当时是忠臣,在主公上家,选了张飞。当时没有人会觉得这个选将有多么不妥。教学以及前期过程略去,到残局阶段,剩下主公,我,还有内奸吕蒙。当时吕蒙手上攒到了二十多张牌了,我觉得胜利无望,开始惊人之举:咆哮主公。主公求桃,吕蒙救,再杀,吕蒙没桃,主公跪。当时玩吕蒙的同学对我非常无语,但是我还是坚持自己的打法没有错,我的理论是这样能消耗吕蒙手上的桃。现在回过头来看真是无理取闹。同时候有个叫三国帮的游戏,做得及其山寨,但是为了解决玩不到实体牌的饥渴,很多人都开始玩这个游戏。我比较确信这是国内最早的三国杀网游了,应该要比online还早一些。online当时在内测,只有拿到实体牌的验证码才能玩,所以只有很少的人在玩,直到09年下半年盛大介入才开放注册,这是后话。

那个时候还是dota如日中天的时候,所以只有在百无聊赖的时候玩一下三国帮,直到我们寝室搞到了自己的实体牌才开始天天面杀。最早的时候一般是玩七人局,这是我们认为比较平衡的局,但实际上对反贼方很不利。尤其那个时候孙权是能多次制衡的,如果主公选了孙权那就不用打了。所以当时反贼方的策略就是尽量隐藏自己,互相杀,争取先干掉一个忠臣,造成六人的局面。那个时候我们的观念还处在认为这时一个猜身份的游戏,要尽量在复杂的局面分辨出谁是忠,谁是反。反贼直接跳是殊为不智的行为。还有一个有意思的现象就是“墙倒众人推”,只要有人残血了,所有人都会去杀他,一方面是为了拿牌,一方面是看身份,加快游戏进程,有点像后来的国战。这就造成大家都喜欢选4血将,因为不存在爆发一说,保命才是王道。在这种模式下内奸其实很有生存的土壤,内奸想存活到最后很容易,但是很多情况下残局是主忠内,内奸想赢就只能装孙子装到底。这种情况下胡闹同学首先开发出了平衡内的打法,这个打法有点突破道德底线的意味,因为内一上来就有偏反的味道,让主忠方一下子接受不了。不断有人对这种内的打法产生质疑,游戏的氛围一时凝重。但是从实际来看,内反的胜率渐渐提高,游戏也比之前更激烈一些,大家就渐渐接受了这种打法。

这段时间从09年的暑假一直到09年下半年。在寝室里经常能看到一群人光着膀子,嘴里喊着“杀”,“顺手”,“判定判定”,。。。这个时候我们大多还处在对游戏规则的初步认识上,对武将的理解还在非常肤浅的阶段。经常发生闪电在一人面前挂了几轮才想起来判定,隔着加一马杀个不停之类的bug,对诸葛亮这样的强将显然不会有太多认识。那个时候还有很多有意思的发现,比如bug般存在的银月枪,记得我挂着银月枪,被动出了三次杀,波波同学空城的诸葛掉了三次血;再有被捧上神坛的夏侯惇,因为不吸仇恨,单挑还厉害,一度上了banlist;刘备孙尚香华佗的无敌三奶组合;还有我自创的黄盖慢苦的打法,屡屡上演好戏。这段时间反而是我玩三国杀最快乐的一段时间,每一局都有每一局的乐趣;每天都能玩到半夜两三点。平时上课都在想,某某武将该怎么用,某某情况下该怎么打,乐趣无穷。

09年下半年online开放之后大家面杀就少了很多,但是我们经常网杀的几个水平进步非常快。并且大家普遍认为online上标准的八人局比七人局要平衡。唯一让我们有兴趣面杀的原因是加入了风扩。这时候我们对武将有了新的认识,开始偏爱一些三血将。而波波同学对武将的理解总是超前我们一步。比如诸葛控判定牌,提出张辽在六人局中过分强大,张角换无懈的技巧诸如此类。我一直觉得他去参加什么王战之类的肯定能拿个名次。这段时间我们还开发出各种玩法,比如1v1,2v2。2v2的规则就是4个人每人各自选武将,然后按 a1,b1,b2,a2这样的顺序循环,由于不知道队友的选将,多了很多不确定性。当然有刘备都会选,这可以算是3v3的一个雏形吧。说到3v3,我们在面杀时很少打3v3,我记忆里只有屈指可数的三两盘,可能大家觉得打33太累,对抗性太强。记忆中和波波打过一次,虽然我们对选将没什么概念,但是有几个很强的配合还是知道的,波波在那个时候已经玩起了月英主收边的高级打法,自然能血虐我;但也有一盘选了看似强大的妙脆角,在被队友误杀一刀没闪的情况下被我速推。

10年就出了军争和火包,这段时间忙出国和毕业的事情,面杀次数更少了一些。唯一的印象就是铁锁要求的横置武将牌总也搞不清,因为很多人喜欢横置武将牌去盖血牌。。。我心里觉得加入军争之后三国杀已经不太适合面杀了,因为要记的东西太多,又太琐碎,尤其铁锁之后结算伤害,如果再有个小乔,我基本上已经晕了。

10年下半年出国一直到11年,这段时间打了很多的三三。一方面online上迟迟不开军争,另外一方面看到很多大神在玩三三,特别看了火树的几个视频,觉得三三特别有意思。虽然我接触三三很早,但是认认真真开始玩的时候,很多理论已经很完善了。而这些东西都是玩身份局的时候认识不到的。比如冷主攻,暖主守的观点在我之前看来完全没道理,比如我选冷色刘备张飞组合一定让先的。后来在认识到冷色四连的威力之后开始逐渐接受这种理论,然后在面对经验不足的队友时候往往要进行一番教育。这段时间也是玩三三最有乐趣的一段时间,特别喜欢做主选将,有时候欲擒故纵,往往能选到很好的将。还有的时候选到的强将故意不上,却上一些冷门的将,反而对对方想当然的布阵有出其不意的针对效果。三三里面的选将,上将真正诠释了什么是兵无常势。我做主将的时候喜欢选不同的将去赢对面,这么做非常有成就感,有一次被队友夸赞:虽然觉得这个主选将很怪,但每次都能赢。虚荣心得到了极大的满足。online上的玩家普遍偏好防守阵,张辽司马大乔之类。而我则偏好进攻阵。防守固然容易,但是进攻可以千变万化,玩起来更有乐趣。这期间也碰到过很多所谓高手,有时候帮他们当边锋,很容易就能看出他们在选将上的欠缺。有些人只选自己认为厉害的组合,对可能的其他威胁组合视而不见;有些盲目地迷信一些组合,比如司马甄记,华佗黄盖;这些在我看来都是考虑不周或是经验不足的表现。又一次和同学的同学,一个号称高手的人打,我带两个不认识的路人,第一盘输了,但是我看出来他选将有瑕疵,就和队友说不要退,能赢。后来连赢五六盘,每一盘都在选将上压倒了他,那个高手就退了。后来玩三三的人渐渐多了,有了许许多多的家族,很多房间都是一个家族的三个人开黑刷路人,出手时间一般是5秒。这样路人只要有一个手速不行就输了。这些人的水平未必很高,也未必很菜,基本上对33的套路都算很熟了。但是我不太喜欢这样的氛围,其次新鲜的东西渐渐少了,所以就渐渐退出了三三。

12年有很长一段时间没有在玩。这时候online可能已经出到林包了,并且军争也早就出来了,有一天无聊充了个会员,就开始了无尽的花钱之旅。由于很久没有打身份局,很多的观念也开始落伍了,花了不少时间适应。首先有了军争节奏就很快了,加上火包和林包几个过于强大的武将,局面往往很快变得难以控制。以往很强的控场武将比如张辽,大乔作用明显降低,一些高输出的武将比如黄忠,变得厉害了。反贼的高爆发能让主公在一回合内残血。反贼的胜率一般在6成以上,我自己更是打到百分之六十九,相反主忠的胜率降到四成左右。胜率最高的主公又变回孙权,并非孙权抗击打能力变强了,而是孙权是所有主公里最灵活,最可能打出高伤害的。主公能多撑一个回合已经没有什么意义,有意义的是在自己的回合内能做多少事。这段时间也碰到过不错的局,但是总的来说,休闲娱乐的性质已经大过于探索研究了。

12到13年之间,盛大开始了无节操的圈钱活动。从林包到山包到sp,一将,二将,三将,我实在搞不清有多少武将了。其实说实话不能全怪盛大,游戏的界面和系统都比以前好太多了,武将的话我用朋友的号也能玩个遍,只是当初玩游戏的热情已经没有了。对于三国杀来说,单纯的出新武将带来的边际收益会越来越小。要想活下去势必要推出新的玩法,比方说国战就是非常好的玩法,现在学11平台搞排名也是不错的想法。作为休闲卡牌游戏,三国杀已经远远超越了它应该做的。这样一个游戏能如此成长壮大,每个玩家都应该感到欣慰。火树在视频里说了这样一句话,就是玩任何游戏都要用心,这一点我不能更同意了,其实做任何事都是一样。就算现在online上的环境令人失望,只要用心玩我相信还是能发现新的东西。三国杀对于一些人来说只是茶余饭后的消遣,或者聚会时增进感情的一项娱乐活动。和完全不会的妹子玩很快乐,和厉害的高手玩又有不一样的快乐,这是三国杀于我,最纯粹的东西。

I watch, I hear, I learn