有时候不要一眼看到事情的全貌比较好。
就像爬山。
一开始就盯着山顶的话,爬几步就会累的不行了。其实并不是脚下的路太长。而是被雄峰的嵯峨压倒了,征服了。
要想爬上一座山,就只有盯住了脚下的那几步路,弯着腰、低着头,一步一步的爬。
等你爬上那巨峰之顶,总有时间给你俯瞰众山小的。
那时候再回过头来看全貌吧,等你征服了群山之后。
Author: hawk
万智牌理论-生物价值
悲剧,由于公司的OOXX规定,貌似现在各种不可说不可说。。。
于是blog只好写点娱乐文章了咩。。
最近沉迷万智牌,烧了不少钱,不过在加班之余,跟同事切磋几盘万智牌已经是最后的人生乐趣了orz|||
关于万智牌的生物价格,一般都是用converted mana cost来计算的。但是,同样价格的生物,价值是大大不同的。当然也需要相同的方法来衡量。
好在我们有万智牌数据库,可以整理数据来支持我的理论。虽然只是玩了一个月左右的菜鸟,我还是大胆提出这个理论:
1点有色费用 = 3点属性(我定义:属性就是power + toughness)
2点无色费用 = 1点有色费用
拿什么来证明我的观点呢:
菁英先锋 | 草原雄狮 |
丛林雄狮 | |
狮群守护者 | 先兆树妖 |
铬铜索墙 | |
2费的墙能到0/6,当然也会有正面异能。这个墙是个2无色费。所以正面异能还得额外付费。否则2无色费只能提供3点属性。但是例外:神器生物这个角色太尴尬了,所以似乎神器生物往往会+3属性。这是参照许多0费的神器生物。
扑翼机 | Phyrexian Walker |
蒙纳兽 | |
腥风狂战士 | 火炬墙 |
火炬墙,1红1无色根据我的推算是4.5个属性。他4攻1防是5个属性。多了那0.5赔了一个负面异能:守军。
贾路的旅伴 | 蒙鸠地降敌兵 |
装甲战马 | 今田家旗本 |
后面那个蒙鸠地降敌兵,由于还多了一个死触异能,所以在3/3之外多了一个负面效果(手中没有妖精就会mana leak)。还好这个效果如果是打妖精套的话几乎可以忽略。并且他是1绿费1无色费,也可以算作是绿色生物的优惠吧。
装甲战马,2攻3防没有异能。亏了1个属性。白色生物嘛,都是这样中规中矩的。
这个金田家旗本很有意思,阻挡和被阻挡的时候变成2/3。那么就同装甲战马相当了(同为白色生物哟)。注意他是一白一无色费,打脸不疼还有容易被杀的弱点也是从这里来的吧。但场上如果有传奇武士就会变大。算是用一个不易触发的异能来把那亏掉的属性补回来了。
总结:
- 1有色费用 = 3属性
- 1无色费用 = 1.5属性
- 神器生物属性额外 +3
至于飞行、践踏这两个异能,我现在估算是 = 1.5属性还没有仔细考究过。其他的异能就比较复杂了,以后再琢磨。这也是这次我列举出的例子都是红、白、绿的缘故。蓝色和黑色生物中规中矩的太少,都是些奇奇怪怪的异能,难以分析。
其实只是这样算价值也不是很有意义,毕竟很多时候生物选择要看组合技、看牌套思路等等。但打快攻的套牌可能会考虑这方面问题,要最优生物嘛。只是一个参考值吧。例如也可以把整套牌的生物价值以及根据cmc算的价格相除算出平均价值,也会有一定的指导意义吧。
至于异能、法术、瞬间等的价值,有机会再慢慢考据~
最后感谢一下我查牌的两个数据库:
复合功能就是代码坏味
这句话要反复重复给自己听。
今天在看java的官方文档的时候看到了这么一个例子:
List suits = ...;
List ranks = ...;
List sortedDeck = new ArrayList();
// BROKEN - throws NoSuchElementException!
for (Iterator i = suits.iterator(); i.hasNext(); )
for (Iterator j = ranks.iterator(); j.hasNext(); )
sortedDeck.add(new Card(i.next(), j.next()));
文档中举这个例子是为了推广foreach语句的使用。但在我看来,之所以容易产生这样的错误,原因是java的iterator的方法不够内聚。
为什么要用iterator.next()返回iterator指向的值?
这让我想起了C++的STL中一系列关于堆栈的操作。获取栈顶的方法是back()或front(),出栈的方法是pop_front()或pop_back()。是这样做,而不是令pop_back()直接返回栈顶值同时弹出。这就保证了函数方法的绝对内聚。从而避免了如同前例那样的潜在错误。
永远保证自己的函数功能单一,没有副作用。不要用小技巧去让一个函数多功能化。有时候潜在的错误远远不是一眼能看得出来的。
搞清C++虚继承
c++虚继承平时很少用到,看书的时候也就是理解个大概。
这次实践中使用到了虚继承,总算有了具体的印象。
基类thunk怎么在子类对象里堆叠的,咱就不赘述了,反正网上、教科书上到处都有讲。这次咱自己遇到的问题主要是虚继承和普通多重继承没搞清区别。
我有一套虚接口hierarchy.
class View {
virtual int ID() = 0;
virtual int Child() = 0;
};
class TextView : public {
virtual const char* getText() = 0;
};
此处省去代码数百行。。。
之后当我开始做这套虚接口的一组实现时,才发现问题。
class SysView : public View {
virtual int ID();
virtual int Child();
};
到这里还是都正常的。
class SysTextView : public SysView, public TextView
{
virtual const char* getText();
};
当实现这个类时,就会发现其对象无法实例化。因为View下面的虚接口没有实现。咦,不是继承了SysView而SysView实现了View下面的所有虚接口吗?问题没有这么简单。因为这个是多重继承,每个父类都会在子类中产生一个拷贝,所以SysTextView里面就出现了两个View类型的父对象。一个是SysView,一个是TextView。而TextView没有实现View的虚接口,因此这个类整个就都不能实例化了。
解决方案就是必须使用虚继承,让编译器知道,TextView继承的那个View和SysView继承的那个View,是同一个View。则需要修改的代码如下:
class SysView: virtual public View {
///...
};
class TextView : virtual public View {
///...
};
注意SysTextView并不需要对其两个父类进行虚继承,除非hierarchy下面还有对这个类进行多继承的情况出现。
虚继承会影响系统性能,应尽量避免。这里用到这个逻辑,只是为了能够使用工厂方法较为优美地创建出各种不同类型的对象,然后调用其虚接口完成操作。并不是性能瓶颈。呜长久以来一个知识漏洞终于通过一次实践补上了。挺好。
Symbian 测试框架调研笔记
symbian没有合适的自动测试框架。不是java的东西就是麻烦。
摘自Nokia wiki, RWindow::Construct
Construct ( const RWindowTreeNode &, TUint32 )
IMPORT_C TInt | Construct | ( | const RWindowTreeNode & | parent, |
TUint32 | aHandle | |||
) |
Completes the construction of the window handle.
This method should be called after the RWindow() constructor, before any other functions are performed on the window. It creates a window in the window server corresponding to the RWindow object. The window is initialised to inherit the size and extent of its parent window, given by the first parameter. If its parent is a group window then it will be full screen.
This function always causes a flush of the window server buffer.
Parameter | Description |
---|---|
parent | The window’s parent. |
aHandle | Client handle for the window. This is an integer value chosen by the client that must be unique within the current server session. The usual way of doing this is to cast the address of the object that owns the window to a TUint32; this allows event handlers which are given a window handle to obtain a reference to the window an event is intended for. For example, CCoeControl uses this technique when it constructs a window. Note that in GUI applications, every window is created and owned by a control. Therefore it is rare for 3rd party code to ever need to call a window’s Construct() function directly. |
Returns: KErrNone if successful, otherwise one of the system-wide error codes.
这里的解释很重要,The usual way of doing this is to cast the address of the object that owns the window to a TUint32; this allows event handlers which are given a window handle to obtain a reference to the window an event is intended for. For example, CCoeControl uses this technique when it constructs a window. Note that in GUI applications, every window is created and owned by a control. 也就是说,RWindow的handle可以立即强转为一个CCoeControl*,并且就是构造他的控件对象。
摘自Nokia wiki, RWindowTreeNode::ClientHandle()
ClientHandle ( )
IMPORT_C TUint32 | ClientHandle | ( | ) | const |
Gets the window’s client handle
The return value is the client’s integer handle that was passed as an argument to the window’s Construct() function: see RWindow::Construct() for a description of the client handle.
This function always causes a flush of the window server buffer.
See also: RWindow::Construct()
Returns: Handle ID for the window.
也就是说,通过这个函数可以获取到对应的控件指针。
摘自Nokia wiki, RWindowTreeNode::Child()
Child ( )
IMPORT_C TUint32 | Child | ( | ) | const |
Gets the first child of the node.
This function always causes a flush of the window server buffer.
Returns: The client handle of the child node that currently has ordinal position 0. This is 0 if there isn’t a child.
获取TreeNode的孩子节点。使用NextSibling继续向下遍历。
注意到RWindowGroup也是继承自RWindowTreeNode的。一个测试思路就是,获取到当前WindowGroup,然后遍历整棵树,获取每个节点对应的CCoeControl。暂时不知道Symbian系统上能不能做Dynamic Cast。理论上来说配合RTTI可以了解到每个节点的类型,并对特定的类型调用特定的方法进一步处理。不过跨进程做这种事情恐怕还是有问题。不知能不能把测试程序作为一个dll植入被测程序去运行。这就可以实现Android上Hierarchy Viewer的功能了。
简单记录以作备忘。哪位兄弟知道成熟的symbian测试框架也请不吝赐教。
生活就是改变世界
今天无聊地想着一个基本粒子的议题的时候,忽然想跑题了。
本来是从,想像,切割一块石头,越切越小,还是石头,究竟什么时候,切那么一下,就不再是石头了,这个话题想起的。
结果忽然发现,其实不对。本来是大石头的,你切了那么一下,就变成小石头了。
无论是石头也好,任何东西也好,你对他产生影响,甚至你只是看了它一眼,你就改变了它(或者说它改变了你)。其实任何人,随时随地,不管睡着醒着,都一直在改变着这个世界。即使死掉了,对这个世界的改变仍然存在,并汇合到整个世界变化的洪流中去,成为历史的不可磨灭的一分子。
所谓生活,其实就是对想要什么样的一个世界,做出选择,并且执行,这样一个过程。
所谓自由,其实就是对想要将世界改变成什么样子,保留的那么一点点权利。
但是,是不是自由就是说,想让世界变成什么样子,世界就会变成什么样子呢?当然不可能。那样的世界与其说是天堂,不如说是地狱。
想要改变世界,总要付出代价的。只是代价有大有小罢了。并非将这个世界改变成理想的状态不可能,只是多数人付不起那个代价罢了(当然你可以说聚集所有人的力量共同来偿付这个代价,问题是不同人的“理想的世界”是决然不同的,所以就像建造巴别巨塔,最终只有失败)。但是只要你付出了代价,对这个世界的改变就是永续的。正如我前面所说的,它将会永远汇合进历史的洪流中去。哪怕你只是随手扔了一片纸屑,或者(如同我现在)随手写了两句博客,都改变了这个世界,产生了或这样或那样的影响。甚至你什么都不说,什么都不做,只是默默地看着这个世界,也一定对这个世界产生了或这样或那样的影响。并不一定是说蝴蝶效应。也有可能,虽然这个影响非常非常小,完全可以忽略,但是历史是不可磨灭的,是不会后退的。随手扔掉的纸屑,你再也没有机会回到那个曾经,去让自己不要扔出那一片。所有对世界的微小影响,最终都会积累起来,汇合到滔滔不绝的时间之河中去,最终成为乾坤调转的重大转折。
所以说嘛,“活着有什么意义”,“人为什么要活着”,从结果上来说,就是改变这个世界。或者说,要保持这个世界防止他改变,这从另一个角度上来说,其实也是改变这个世界。当然不要忘记,自己也是属于世界的一部分。改变世界,同时也是改变自己。或者反过来说也正确,改变自己,就是在改变整个世界。
或者又反过来说,其实这个世界就是我们改变出来的。这个世界从来都是我们想要的。只不过,不是你一个人想要的,而是从古至今无数人类不懈努力,共同改变出来的样子。是全人类想要的世界。
所谓“理想”是不能达到的,其实说的是一个人的理想。我们的这个世界,其实就是全人类的共同的,活着的理想!
系统的民主和独裁
关于系统设计,我想到了两件事情
从性能的角度考虑,系统设计应该尽可能隔离物理设备,向用户提供透明的接口。最好让所有的东西都是普通的变量。例如,为什么要区分内存上的结构体和磁盘里的文件呢?完全可以让用户认为,定义一个变量,他就在内存里,又随时可以固化到磁盘上。至于这个变量,实际上放在哪里,由操作系统决定。例如一个很复杂的结构体数组,就可以开辟在磁盘上,并作内存上的缓冲。当然这种做法其实跟现在系统的内存分页调度方式最终结果是类似的。但是如果一开始选择权就在系统的话,自由度更大。
从这个角度考虑下去,很容易想到的就是文件的结构化管理。为什么二进制文件比纯文本文件更难读懂?因为二进制文件没有统一的管理模式。同样是二进制的,c语言里调试结构体的时候就完全不会觉得费力,为什么呢?因为头文件里声明了结构体的各成员类型。如果能把结构体的声明嵌套在二进制文件里,使其成为自解释的,则二进制文件能够充分发挥其存取方便、节约空间的好处,同时防止其调试困难人类难以理解的缺点。既然文件头的规范已成定则,完全可以考虑把文件的结构声明放在文件尾。当然需要订立标准。假如让我来设计草案的话,可以如下所述:
(1) 为了兼容现有文件结构,自解释信息放在文件末尾,紧接在原始文件的后面。使用特殊的头部以区分其他文件。
(2) 文件结构为树状结构,从根部开始,使用“描述块”来描述每个节点的数据结构定义,直至叶子节点。
(3) 叶子节点使用字节数表示其单位大小
(4) 父节点可以是数组*、结构体
这里有一个需要讨论的问题,就是当父节点是数组的时候,如何确定数组的长度。通常有两种做法决定数组的长度,一者是使用特殊标记的结束符号,二者是使用一个数组长度的数值来确定。当数组长度非常长的时候,可能32位int无法表示,可以总是使用64位int又会浪费空间,假如未来出现超级巨大存储器,甚至可能64位int都不够表示,还需要更加巨大的数字。所以我认为使用结束符号表示数组结尾是更好的方法。当然由于这里只是YY,可以随便想。例如也可以使用一个配置选项来决定使用哪种方式表示。
另外结构体的表示,当其成员用到其他已定义的成员时,也可以使用序号表示那个成员的结构声明。
使用这样的一种方法,程序员想要操作文件系统时将会非常轻松,例如配置文件,可以认为是使用换行符隔开的一系列字符数组。每一个换行符隔开的行单元可以单独地被交换到内存,或者写回文件。由于在文件定义的时候就确定了文件是以行为单位独立的,操作系统在这个层次可以做大量优化。例如并行程序读写不同行时文件无需加锁,而是将这两个被读写的行交换到内存中进行操作。例如编译文件,在某一行插入时,不必对文件的后面部分依次推后重新写回,而可以只在那一个行上做内存缓冲和读写,当系统空闲或卸载磁盘前再做写磁盘操作(大大提高并行性并减少磁盘写数量)。对于大文件,系统可以自动对行进行索引,从而提高随机读写行的性能,等等等等。
另外能够方便程序员的是,这样的文件由于已经附加了结构说明,只需复制这个文件,无需附加其他说明,就可在其他的程序员那里方便地阅读和使用。例如一个游戏的资源包文件,其实是一个复杂的树状结构。如果附加定义清晰的结构说明,其他程序员即使没有源代码也可以轻松地理解其内容。当然如果为了保密,可在发布时去掉文件说明。由于仅是附加在文件尾的部分,去掉也不会对文件本身造成任何影响。
在这个角度YY得太多了。总之这样的思路就是,操作系统完全是独裁的,程序员只能提请求,但这个请求究竟以什么样的方式实现,有操作系统来决定。好处是,系统所处的地位比应用程序高很多。系统知道现在全局资源的情况。总的内存占用、磁盘访问频率、网络流量(甚至可以考虑将部分数据上传到云端作为一种持久化措施,程序员请求固化一段内容,甚至可以不知道是固化在本地了还是在云端了),等等,这些东西作为一个应用程序是没办法想太多的,想太多容易“过早优化”,可是完全不想最后查性能瓶颈又是各种麻烦。如果系统能够智慧地处理各种情况,根据当前系统状态动态选择最合适的策略,则系统性能可以做到最优化,硬件使用率可以达到最高。
但这只是从系统的角度考虑。
假如从应用程序的角度考虑呢?假如我是应用程序的开发者,我绝对不喜欢操作系统额外做很多事。我希望我做的事情没有副作用,可确定。这也能方便我调试,方便我bug复现。例如,假如真的系统可能把数据固化到云端,假如网络通讯部分驱动有bug,可能我请求一个变量,十次有三次不成功,七次成功。因为恰好那三次定义到云端了,而后来的七次定义在本地。如果是这样,一旦程序复杂,bug丛生,程序员会气急败坏。相信程序员,把一切都交给程序员,这就是Unix的系统逻辑。一个绝对民主的系统。
可是,当权利交给程序员的时候,程序员就成为独裁者了。好的代码自然能够绝对有效地利用系统提供的资源,但坏的代码不仅自身运行不好,还会占用其他程序的资源和空间,让其他程序都变得缓慢甚至无法运行。让应用程序在他可见的那个狭窄范围内,做系统级的性能优化决策,又实在太为难应用程序员了吧。相比无数应用程序群氓的独裁,莫不如系统一个人的独裁可靠一些。因为只需把一个系统做好,就可以让所有的应用程序有好的表现。或许这个思路更好?
cygwin中启动cmd
cygwin中如果需要start .打开explorer怎么办?
cygwin中想要调用.bat脚本怎么办?
今天才知道这个超级有趣的解决方案~~
直接输入cmd回车,在cygwin中运行cmd。
效果是,ls, grep, man等命令仍然可以正常运行,同时也可以运行bat脚本,用start .打开explorer。唯一不足就是原有的cygwin命令行配色方案会被cmd冲回黑白的。
不知道这两个程序一起运行会不会有其他的什么冲突,至少现在玩得好好的~~