元旦早晨,我起床后准备烧水泡茶,手握住水壶的那一刻,觉得好冷,手都僵了。这个时候,突然想起这两天玩的游戏,放逐之城。

放逐之城(Banished)是一款短小精悍的模拟经营游戏。游戏开始,你带领一群被放逐之人来到一个山谷之中,然后从头开始,建造家园,繁衍后代,生存下去。游戏一开始有五个家庭,也就是十个成年人,十二个小孩,孩子到了十岁,就会长大成人,可以开始工作。游戏的主要目标就是生存,首先,人活着就会消耗食物。其次,到了冬天,还需要燃料来保持温暖。如果这两个主要的资源得不到补充,村民就会挨饿或者受冻,如果情况一直持续,就会死亡。

要获得食物和各种资源,玩家需要安排工人获得在地表的三种主要资源:木材,石料和铁来建造各种设施,然后指派成年人到设施中进行工作。 比如,游戏中获得食物的设施主要有以下几种:

  1. 采集类,狩猎小屋可以获得鹿肉,钓场可以捕鱼,采集小屋可以获得各种山珍野味。采集类的设施每个季度都有收获。但是需要确保周围的森林不受破坏。
  2. 农田和果园,需要有人耕种和维护,只有秋天才会收获,并且果园需要几年时间等果树长大后才会有收成
  3. 牧场。后期通过贸易所交易获得鸡牛羊等家禽后,可以开始建造牧场来畜牧,这些设施可以提供鸡蛋和肉类。

另外,通过建造新的住宅,适龄的青年就会住在一起组成新的家庭,然后就会有孩子出生。

虽然游戏的机制看上去不复杂,但是我第一次玩到50人口,就失败了。 原因有以下几点: 首先,过快的人口扩张,导致食物的需求量一下陡增,这个时候,需要建造新的设施来提供食物。 偏偏这个时候,村民工作使用的工具也宣告短缺,而工具的生产,需要铁,祸不单行,露天的铁矿已经被采空,依山而建的铁矿坑的产出速度太慢。于是一系列的连锁反应导致食物和柴火的短缺,最终第一个村子中村民纷纷饿死。

攻略

所以,以下是我的经验和教训

首先要了解整体的供求关系。

供给方面的数据其实比较容易获得,因为点击每个设施的数据面板,可以看到这个设施的当前产量和去年一整年的产量。大多数的食物设施在满员工作的情况下,每年的年产量是800。所以把所有的设施的去年产量加在一起,就可以估计出总体产量了。等到后期建造了市政厅之后,数据会更加快捷的获得,不需要手动的计算了。

需求方面的数据比较难获得,游戏中只提供了库存信息,而并没有消耗信息。我所采用的方法,是在游戏稳定下来的一个阶段,记录下来当时的库存量,一年之后的库存量,以及这中间的产出量,去年库存 + 去年总产量 - 今年库存就可以计算出人均消耗的资源数量,作为后期进行预测的重要指标。

通过这种方法,我的经验是每年需要准备150 * 人口数的食物产量才能保证食物的充裕。对于柴火,这个系数是16。相当于一个伐木场一年不间断工作的柴火产量可以满足50个人的需求

设施的布局

我的布局是以集市为中心的一个布局。集市是一个覆盖范围超大的设施,可以帮忙调配覆盖范围内的需求和产出的调配,这样,工人可以专注于自己的生产,而家中需要的食物和柴火可以由集市的小贩来负责运送。

以集市为中心,四方可以有两个方位建造一开始的采集中心,每个中心分别建造一个狩猎小屋,一个采集小屋和一个护林小屋,其中一个可以加上草药小屋。 另外两个方位,可以作为居民区重点发展,还有一个方位,一开始可以建造农田,后期可以按需求进行改变。

后期农田的产量比采集要高,可以把一个采集中心改成农业中心。

在初期进行建造的时候,在建筑工地旁边空出一些地方来设置料堆可以节约工人搬运材料的时间

人口的控制

人口的控制是游戏中最大的难点,首先,玩家的成就就是创建一个尽可能大的稳定的社会,但是,如果一下子让人口激增,就会进入资源短缺的陷阱。另外,如果忘记增加人口,也有可能产生人口萎缩的问题。 对此,我的做法有两条:

  1. 每个季度增加一个房子,不能同时增加多个房子,保证人口增长的稳定性,避免人口生育的高峰
  2. 检查每个新房子入住后,新人的年龄,如果新人的年龄偏大,说明需要增加更多的房子来增加人口,如果新人的年龄偏小,可以放缓房子的建造速度。 我设定的阈值是25,不过这个并没有什么根据。

此外,游戏中还有一个影响人口结构的重要因素就是学校,建立学校。 建立学校之后,孩子会推迟成年,而要先进入学校学习五年之后才能成年。 所以,学校的建立会巨大的改变人口结构,所以建立学校要慎重,最好能够保证所有的资源的富足之后,再进行建造。

资源的控制

一开始,地面上有很多的铁和石料,其实这个时候应该尽快组织工人把这些裸矿先开采完,这样不但有充裕的铁和石料,还能让护林小屋专心生产木材。后期,需要建造采石场和矿坑才能开采铁和石料,一定要所有准备,才能保证资衔接的顺利,否则会遇到建造设施却没有矿石或者工具的问题

感想

元旦里我总共打了三盘放逐之城,这个仅仅80M的游戏让我产生了很多思考。

起初我的指令基于直觉,但是50人口之后,就无法再维持一个稳定的社会了。 于是我意识到,需要系统性的理解游戏规则,于是我把游戏规则简化为供求关系。而第二步,就是要分析供给和需求的量分别是多少,这样,才能估计每一步操作会带来的影响。 具体的方法是,把每个季度的生产数据记录在纸上,汇总统计出总体消费量,从而求出人均的消费量,成为将来决策的关键指标。这里,我的教训是,想要为未来做打算,一定要对过去做总结,同时要抽取出关键的指标来简化管理。

第二,游戏达到150人口之后,我把所有能造的建筑都造了,以为万事大吉,开着十倍速什么都不做,结果过了几年,食物的存储量快速下跌,于是又发生了饥荒。我想,这可能就是很多统治者一开始是开明之君,等到晚年的时候,变的昏庸的原因,那就是因为懈怠。

最后,整个游戏虽然是欧洲中世纪的风格,但却更像远古时代的社会,所有的资源都是共享的,是一种原始共产主义。整个世界没有灾祸,没有饥荒,没有阶级斗争,没有罪犯,没有懒汉,但即便是这么一个桃花源般的理想环境,却也非常难于管理和维持,可见管理一个国家的难度有多高了。

上周末,我在可视化世界历史,主要使用了QlikSense和QlikSense的扩展。然而我遇到了一个问题,不得不深入了解QlikSense扩展的实现原理,从而修改这个插件,满足我的需求。这里记录一下目前对QlikSense扩展开发的认识。

问题

我的世界历史可视化作品,最早的文明是公元前3300年的古埃及,等到我输入到Excel之后,才发现,Excel只支持公元1900年之后的年份。于是我在数据源表格中只记录历史节点的年份信息,然后在QlikSense中使用创建日期的函数MakeDate(Year, 1, 1)来创建日期,没想到,QlikSense也只支持公元后的年份。于是,我被迫使用这么一种变通方法:将最早的公元前年份加上一个Offset,伪装成一个公元后的日期,等到在Dashboard中展示的时候,通过Dual函数,减去Offset,重新渲染成带公元前后的字符串。

这招对QlikSense自带的折线图是有效的,但是对我采用的Timeline扩展确没有起效,于是,我不得不分析QlikSense扩展的实现原理。这里主要参考的是@stefanwalther在github上的QlikSense扩展开发教程

QlikSense Extension开发的一些知识

@stefanwalther老师这个教程本身还处在连载状态,我学习到足够修改Timeline插件的知识之后,就暂时先放下了。这里记录一些学习到的知识,以备后用。

QlikSense Extension的核心

其实QlikSense扩展的核心有两部分,一个是.qext元数据文件,里面包含了扩展的名称,图标,说明等信息。还有一个是JavaScript脚本文件,这个才是重头戏,负责渲染图形,处理各种事件。

define( [ /* dependencies */ ],
	function ( /* returned dependencies as arguments */ ) {
		'use strict';
		return {

			// Paint resp.Rendering logic
			paint: function ( $element, layout ) {
				// Your rendering code goes here ...				
			}
		};
	} );

这个就是扩展脚本的整体框架,核心就是paint函数,里面有两个重要的参数

  • $element, 其实就是被渲染的HTML结构
  • layout, 包含了数据和属性信息

调试

  1. 可以在桌面版QlikSense里,按住Ctrl + Shift + 右键,点击Show DevTools
  2. 打开一个QlikSense应用之后,用自己的浏览器访问localhost:4848/hub,这样还可以使用自己熟悉的前端调试工具,比如FireBug
  3. 可以使用console.log('someMessage', someObject)来打印调试信息

从Qlik Engine中获得数据

接下来的问题是,扩展是怎么样从QlikSense中获得数据的呢?

数据可以通过layout.qHyperCube获得

  • layout.qHyperCube.qDimensionInfo, 包含了使用到的维度的信息
  • layout.qHyperCube.qMeasureInfo,包含了使用到的度量的信息
  • layout.qHyperCube.qDataPages,详细的数据

hc.qDimensionInfo[i].qFallbackTitle就是dimension的名字

hc.qDataPages[0].qMatrix可能没有全量的数据,可以随着图表的滚动,来获得新的数据。

Timeline扩展的分析

通过以上的知识,可以知道,QlikSense扩展的核心是paint函数,在Timeline脚本的284行,作者把qMatrix中的数据,转换成一个dataset,然后赋给vis.DataSet,就会调用vis这个第三方可视化库,创建最终的图形。

var dataItems = new vis.DataSet(dataSet);
var container = document.getElementById(containerId);
var timeline = new vis.Timeline(container);
timeline.setOptions(options);
if (useGroups) timeline.setGroups(groups);
timeline.setItems(dataItems);

构造这个DataSet,使用的是这些数据:

var dataItem = {
	id: e[0].qElemNumber,
	content: e[1].qText,
	start: dateFromQlikNumber(e[2].qNum)
};

所以,最终,只要修改dateFromQlikNumber,加上我的workaround,把QlikSense中得到的日期时间,减去Offset,就可以让公元前的日期显示为一个负数。

我对世界历史感兴趣,我想了解当今这个世界是怎么被塑造起来的。我想了解这个世界上曾经存在过的伟大文明,他们所达到过的最远的边界,他们创造的伟大成就,他们留给我们的遗产,以及他们最终消逝在何方。

我在初中和高中所接受的历史教育,基本差不多:一年中国古代史,一年中国近代史,一年世界历史。当时的环境没有现在好,电脑和网络还没像现在这样普及,所以主要的媒介是纸质的教科书。教科书的主要问题是,信息有限。三年六册薄薄的书本,涵盖不了多少内容。第二个问题是,纸上的文字,很难和时间,地理位置对应上。最终,感觉每一段书上的历史,都是孤立的历史片段。于是,就会有这么一些疑问:曾经建造金字塔的古埃及人后来怎么样了,希腊和罗马后来怎么样了,法国,英国,德国,又是怎么崛起的,他们还没有阔起来之前,又是在哪里。作为一个中国人,又忍不住把中华文明和其他文明进行比较,但是用线性的历史书比较起来很不方便。

我最近突然有个想法,如果把历史朝代用甘特图(Gantt Chart)来表示,同时结合地图,就可以将时间和空间信息结合在一起,把历史进行可视化。我于是使用了用的比较顺手的QlikSense软件来创建一个Prototype。

QlikSense的主要优点有:

  • 单机版是免费的
  • Qlik强大的关联引擎(Associative Engine)可以将数据关联起来,在各种维度上进行钻取,跳转非常方便
  • Sense开放了前端的接口,现在已经有很多第三方开发的前端的扩展,能够提供额外的可视化功能

上周末,我做出了一个简单的prototype,大致长这个样子: WorldHistory

基本有以下几个组件:

  • 左上角有一个文明的选择菜单
  • 坐下有一个地图,不过使用的是现代国家的疆域
  • 主体是通过Timeline Extension实现的历史朝代的甘特图(Gantt Chart)
  • 下方是详细列表和一个时间轴(Timeline目前不支持选择,只支持移动和缩放)

通过Qlik的关联引擎,整个Dashboard的图、表都是相互关联的,在地图上点击埃及,就会出现埃及相关的文明(未来我会加上亚述,阿拉伯文明)。在下方的时间轴上选择一个时间范围,就会只显示那个时间范围中的历史信息。

目前,我只有中国的历史朝代的大的年表还有埃及的历史年表。但是,你可以看到,通过简单的可视化,就可以发现,古代埃及文明领先了中国不止1500年。埃及人建造最大的金字塔——胡夫金字塔的时候,中国还是上古神话时代,连有争议的夏朝,都还没有开始。这种有意思的对比,会随着历史信息的不断加入而变得越来越多,结合地图,能够给人更直观的理解。

接下来,我会逐步的添加各个古代文明的信息。主要的参考是斯塔夫里阿诺斯所著的《全球通史》一书。另外,吴军老师的《文明之光》也很不错。

最近开始了一个新项目,需要频繁的密集进行开会。这里来总结一些开会的经验和教训,让会议更加高效,节约每个与会者的时间和生命。

开会前的准备

开会前,首先要发送会议请求给自己认为需要参加这个会议的人。这个会议请求虽然看似简单,也是有很多门道的。

  • 一定在会议预定时间的一天前发送会议请求,特别要考虑到时区和节假日的问题,不要让收到会议邀请的人感觉措手不及,甚至把会议时间设定在他们可能打开邮箱之前。
  • 在会议邀请的邮件正文中,要说明这次会议的目标,议题或者需要解决的问题。如果需要对方做准备,也一定要尽早说明清楚,节省双方的时间
  • 会议的参加者名单,有些人可以设置为Optional。如果和对方是第一次见面,需要简单的说明自己的职位,介绍相互之间的关系和桥梁,让对方明白自己建立这个会议的必要性

会议中

  • 进入会议一定要准时,最好提前两三分钟进入会议室或者电话会议,如果是项目经理,可以跟组内成员定下这个规矩
  • 会议开始后,会议的主持人可以先重申一下本次会议需要的目标和大概的时间安排,使大家专注与讨论跟目标相关的事务
  • 会议过程中,主持人还要把控好会议的节奏,避免出现离题太远的情况
  • 会议即将结束的时候,主持人可以总结归纳今天的内容,比如做出的决定,另外,各个Action Item要明确责任人和截止日期,并且获得当事人的确认

会议后

  • 主持人或者书记需要发送会议纪要
  • Action Item的检查,如果超过截止日期,需要对责任人进行提醒

以上就是我对开会的一些经验和教训。接下来也会不断的践行上面的一些注意点,希望把会议开的更加高效。

这里也推荐这篇文章towards-better-meetings

I

咚哒-咚哒-咚哒。我的心脏要快要从嗓子里跳出来了。浑身上下每个地方都在疼。汗水沿着我的脸流了下来,却不能让我干燥的喉咙得到一丝的水分。我还感受到从左边传来一阵一阵的热浪。我睁开双眼,周围的景色从白茫茫一片逐渐变得清晰起来。

这里是艾欧泽亚大陆北部湖泊银泪湖的中央,帝国战舰残骸上部的一个开阔平台。我所在的小分队正奉命突破这个圆形的据点。在这里,我们遇到了帝国的鸟形机械战机。

我刚睁开的双眼很快认出眼前巨型的大鸟,逆着光,只能辨别出那巨大的机身轮廓,以及那闪着红光的双眼,就像死神的双眼。

在鸟的面前,躺着我的战友,骑士,他已经趴在地上,一动不动。现在,四人小队只剩下我一个人还活着。

难道,就要这样结束了吗?

II

十分钟前。

我和骑士,忍者,治疗法师攻入了这个圆形的露天平台。在这里,视野很好,能够看到远方魔都纳的山脉。但很快,一架鸟形战机便从左边的天空中飞了过来,与我们展开了战斗。

鸟形战机通体乌黑,十分巨大,翼展至少有30米,两边固定的翅膀分别和机身核心一样宽,机身翅膀以上的部分非常光滑,而翅膀下面则有着复杂的机械线条,装备着各式武器,非常厚实。战机靠着帝国神奇的魔道技术悬浮在空中。

我们按照平常的战术,与鸟展开战斗,骑士负责吸引火力,我和忍者分别进行远程攻击和近程攻击,治疗法师在后方进行回复支援。战机有两种范围攻击,一种是扇形的机枪扫射,范围有限,还有一种是直线贯通导弹。虽然比较麻烦,但是注意躲避还是能够避免的。

在战斗持续了一段时间后,形势突然急转直下。

III

鸟在战斗中突然停止了对骑士的攻击,然后,只听得,咚的重响,它向骑士面前投掷了燃烧弹,顿时一簇火焰在平台上燃烧了起来。骑士转过身开始向前冲刺躲避后面的燃烧弹。然而鸟还是在不停的投掷,整个平台顿时被接二连三的火焰分割成了两个半圆。我和骑士在一块,而忍者和治疗在对面一块。

投掷完燃烧弹的鸟停止了对骑士的追击。反而转向了忍者和治疗。糟了,隔着火墙,我看到鸟把忍者和治疗逼到了死角,再用机枪进行扫射。骑士想要冲过去去解救他们,但是火墙让他只能干望着。可恶,我跟骑士只能眼睁睁看着忍者和治疗被鸟的炮击杀死。看着他们倒下的身躯,我感觉我的腹部就像被人重重的打了一拳。

接下来,没有治疗的回复,我和骑士生存的可能性就非常小。只有速战速决,才可能生存下来。在鸟掉头转向我和骑士的时候,我发动了毕生所学的各种技能,将我的战斗力提升到极限。

咚哒,咚哒,咚哒。我看着前方的骑士在硬撑着鸟的各种攻击。然而,他的动作,他的装备,都感觉快要到极限了。突然,鸟又发动了贯通导弹。一股气浪将我吹飞,重重的摔在的地上。眼前一片漆黑。

银泪湖中央,蔚蓝的天空一尘不染。帝国军舰高处的一个圆形露天平台已经被烈焰分割成了左右两边。在刚才贯通炮的巨响后,硝烟渐渐弥散开。平台上倒着四副躯体。

IV

咚哒-咚哒-咚哒

咚哒-咚哒-咚哒

我睁开眼睛,用双手撑起我的上半身,可恶,浑身疼的厉害,估计肋骨断了不少。我的弓箭还掉落在火墙的旁边。鸟正在向我靠近,看样子,它又要发动下一轮攻击了。贯通炮需要时间重新装填,所以应该是是扇形的机枪攻击。我右手用力,将身体转过来,爬向火墙边,去捡我的弓。每走一步,我都头晕的不行,四肢也疼的要命。我捡起了弓箭,回过头,鸟果然开始开始用底部的机枪进行射击。我用尽所有脚步的力量,又向前挪了几部。几发子弹射进了我刚才蹲过的位置。

我的腿已经动不了,左边是火墙,如果鸟再发射一个范围攻击,我就死了。

我转过身,半蹲着。面对鸟,本能的拔箭,上弦,拉弓,瞄准,发射。

咚哒,咚哒,咚哒

鸟在空中起伏着,红色的闪光连成线团。

拔箭,上弦,拉弓,发射。

咚哒,咚哒,咚哒

鸟的下部开始动起来,贯通炮开始准备发射

拔箭,上弦,拉弓,发射

咚哒,咚哒,咚哒

鸟在空中爆炸了

后记

开了2.5之后,回来开始赌博,顺便补了一下主线剧情和事件屋。在密约塔副本里残血单挑老二boss成功,所以写了这么一篇文章。现在看来,这篇文章的主要问题是对角色本身的刻画不足。主角长什么样,什么性格,伙伴们长什么样,相互之间的关系,都没有进行说明。最关键的是,为什么要战斗,意义何在,除了战斗,还有没有别的方法,这些都没有交代。当然,如果谈起意义,需要说明的背景就会变多,篇幅就远不止这样了。另外一方面,有着“超越之力”,从来不担心死亡的我,根本没有用心想过生死攸关的战斗到底会是什么样。