Integer multiplication followed by division without overflowing

When doing pure integer calculation, we sometimes face a problem where we have to calculate a * b / c, where a, b, c are signed integers of some size, say 32-bit integers. Considering that a * b might be larger than the maximum value of 32-bit integers, the common way to prevent possible overflowing is to cast them to larger integers, in this case 64-bit integers, and do calculation there.

But what if we don’t want to cast them? (For whatever reason – maybe the integers themselves are already large so you can’t cast them to larger integers without writing your own large integer class; or maybe you just want some intellectual challenge.)

The Algorithm

First of all we can assume a <= b < c: obviously a and b are interchangeable so we can have a <= b; and if a >= c or b >=c, let’s say if a >= c, then we have:

a = kc + a’, k >= 1

and so

ab / c = (kc + a’)b / c = kb + a’b / c.

If kb already overflows then there is no way we can prevent overflowing with any algorithm anyway – just use larger data types. Otherwise, we can calculate kb separately since it’s just integer multiplication, and reduce the calculation to a’b / c where a < c. The same applies to b, so we can simplify the problem down to a <= b < c.

It follows that the final result will not overflow because ab < c^2, and so ab/c < c.

Now that a < c, we can calculate c / a. Let c = ma + n where m is the quotient and n is the remainder. Here we have two branches:

(1) If b <= m, then ab <= am <= c.

If b = m and n = 0, we have ab == c. Thus the quotient of ab/c is 1 and the remainder is 0.

If n != 0 then ab < c, so the quotient 0 and the reminder is ab.

The algorithm terminates here.

(2) Otherwise b > m. In this case we calculate b / m. Let b = km + l where k is the quotient and l is the remainder.

Thus, ab = a(km + l) = kma + al = k(ma + n – n) + al = k(ma + n) – kn + al = kc – kn + al.

It then follows that ab/c = k + (al – kn) / c.

We know that k does not overflow (since k <= b). Set k aside, and focus on calculating the second term.

First recall that l is the remainder of b / m so l < m, we have al < am <= c. Thus the quotient of al/c is 0 and the remainder is al. This does not overflow as al < c.

Then recall that we also have n < a (since n is the remainder of c / a) and k <= b. Thus, recursively running this algorithm on kn/c will eventually terminate by reaching branch (1). Collect the quotient and remainder as u, v such that kn = uc + v.

With the results above, we know that (al – kn) / c = (al – uc – v) / c = (al – v) / c – u.

If al >= v, then 0 <= al – v < c, so the quotient of ab/c is (k – u) and the remainder is (al – v).

If al < v, then -c < al – v < 0. the quotient is (k – u – 1) and the remainder is (c + al – v).

This concludes the algorithm.

Afterword

With this algorithm we can solve the calculation of a * b / c, and also the mod calculation of a * b % c (where % is the mod operator in C).

For the latter problem it could even solve the situation where the initial passed in a * b / c overflows: the reduction step where we make a = kc + a’ will make sure that ab == a’b (mod c), thus the remainder calculated with this algorithm will remain correct.

Sample code shown as below:

void mulDiv(int a, int b, int c, Vector2& outResult)
{
  if (a == 0 || b == 0)
  {
    outResult.set(0, 0);
    return;
  }

  // Keeping a < b helps us iterate faster.
  if (a > b)
  {
    int t = a;
    a = b;
    b = t;
  }

  // Decompose c = m * a + n, and b = k * m + l, so that ab = k * c + (a * l - k * n)
  int m = c / a;
  int n = c - a * m;

  // Early out: if b <= m, then ab <= am <= c
  if (b <= m)
  {
    if (n == 0 && b == m)
    {
      outResult.set(1, 0);
    }
    else
    {
      outResult.set(0, a * b);
    }
    return;
  }

  int k = b / m;
  int l = b - k * m;

  // Thus the result = k + [(a * l - k * n) / c].
  int result = k;

  // Since a * m < c and l < m, we know a * l < c and thus it doesn't overflow.
  int rem = a * l;

  // We iteratively calculate k * n / c while accumulating the remainders.
  Vector2 remainderResult;
  mulDiv(k, n, c, remainderResult);

  result -= remainderResult.x;
  if (remainderResult.y <= rem)
  {
    rem -= remainderResult.y;
  }
  else
  {
    rem += c - remainderResult.y;
    result -= 1;
  }

  outResult.set(result, rem);
}

发表在 未分类 | 留下评论

C&C4

我们来谈一下C&C4。没错,就是那个烂游戏。因为其评价之烂导致我一直没有开始打,直至今天——而且只玩了大概1个小时就受不了了(当然也可能是因为战役模式的节奏真的烂……)。

但是烂归烂,C&C4其实有几个方面想的相当有意思。(顺便补充一下背景资料,根据以前某做C&C4的人说,其实这游戏本来没打算做成C&C4的,而是一个完全独立的C&C线上版,后来因为项目阉割才被EA改成了C&C4。所以你也就可以理解为什么整个游戏的核心系统是如下这样的。)

1. 完全摒弃传统的基地建造和资源采集,进而将游戏重点置于兵种构成,布阵配置,以及战斗的执行上。

2. 即使玩家的“基地”——在这个游戏里面既然没有传统的基地建造,那玩家的MCV就成了真正的移动建设车辆,进而变成了如同英雄单位一般的前线要塞——被摧毁,也可以在一段时间之后重生。重生时甚至可以选择不同职业。

3. 由于前述一点,在多人游戏里,游戏基本以多对多的形式进行,且游戏的胜利条件不是“敌全灭”,而是完成地图上的任务(对泰伯伦结点的控制)。

这简直是不是有点像……MOBA!?

中午的时候有人提起周末跟人打starcraft 2,其中几个问题就在于:对于普通玩家来说,这个游戏所需要的操作实在太多太复杂,于是难以吸引新人入门而逐渐变成老手;在多人游戏中如果一个玩家先行出局,则往往要等上半个小时以上才能重新加入下一场游戏;整场游戏几乎从头到尾都处于紧张状态,因为需要顾及的东西太多,导致极其容易疲劳。

当然,由于整个游戏完全没有基地建造和资源采集的概念,导致游戏最大的问题变成了:两边都绝不停息的出兵,从头到尾打个没完,战线的移动极为缓慢(在地图上的同一个地点打两三分钟完全没有移动也是会发生的)。因为兵都不用钱,只要“时间”就可以造好,于是在战斗开始之后基本上就没有注意力上需要放松的机会——只要你不在造兵和控兵,你就比别人落后。两者综合就导致整个战斗的节奏从头到尾几乎没有变化,唯一的不同就是随着科技升级你能造更厉害的兵了。

如果说SC2的精神紧张度是从紧张(准备时期)->高度紧张(战斗)->相对放松(部队撤退/重建)->再高度紧张的话,那C&C4则从头到尾只有“紧张”,不仅没有解决SC2的节奏紧张问题,连发生战斗及其包含的后果使得战斗相对要求更大的精神投入这一点,都因为兵力消耗的无意义性而被移除了。

这就导致游戏经验很快变成“为啥我还在玩这个游戏”,也是其为什么会变成烂作的原因吧。

其实,如果这游戏不是作为正统C&C出,并且在出了之后想办法把节奏改进一下,或许甚至还能乘着MOBA的风潮成为RTS-MOBA的结合体呢?

譬如说,玩家不是进入战场造兵,而是每次进入战场时以一定的总兵力点数直接展开全部部队。总兵力点数,以及兵种的多样性,自然也可以随着战斗的推移而变多(可以对应于玩家的“角色等级”)。兵力的补充也不是以定速逐个补充,而是需要以一个较长的间隔时间一次性补充(可以对应于“复活”)。允许玩家部队执行类似英雄连系列一样的“撤退”指令,可以令部队离开战场并以较短间隔时间复归(可以对应于“回城补血”)。这样是不是会有点玩头?

发表在 特别回顾 | 留下评论

麻将 – Rule set of the destined, Part 1

事先声明这个一定会是坑。

这是一个试图用现在的游戏设计的方式,将麻将规则引向更倾向于技术而不是运气,但同时又能够尽量保持娱乐性的尝试。

一、一段历史

自从大学二年级由于暑假留在学校无聊而和附近几个寝室的室友打起了国标以来,国标麻将在接下来的两年之间成为了45乙4楼众人的主要日常娱乐活动。当然了,其他的地区麻将的规则也有所接触,但是基本上只是作为国标战之间的搞笑用。在绝大部分的时间里,这群人玩的都是(稍微修改过且不严格执行的)国标规则。

有人说国标算番过于复杂难学,但是对于45乙的这群人来讲,这种东西大概根本是小菜一碟。按照游戏玩家来分类的话,他们绝对算得上是最硬派的游戏玩家。抑或者说,连DOTA里面那么多英雄那么多能力都记得住,区区几个不同的番种怎么可能忘得了。

因此,如果要我以一种规则作为模板来设计统一麻将规则的话,那便一定会是以国标为基础而进行修改,并吸收其他规则中的内容。

除此之外,另一个对我比较有重要影响的规则就是日麻了。不可否认,正在麻将在405寝室附近一带流行的同时,《岭上开花》也开始连载了——当然刚连载了几话就被打回去重练顺便改名叫《咲》。另一个当时开始连载并且在奇怪的方面很有人气的就是小泉纯一郎的恶搞麻将。被这些漫画吸引自然的我也会跑去打一打天凤或者雀龙门,而同时由这几个作品所衍生出的思考,即“如果麻将成为世界性运动,那究竟该采取什么样的规则”,也促成了对几种不同麻将之间的比较,以及对规则融合的尝试。

日麻因为有一些独自的规则(主要是宝牌和振听这两个规则),且本身在日本作为一种竞技运动也有一定规模,因而拿来对统一规则进行补充也是顺理成章的。

二、一些概述

麻将是一个信息不完全条件下的不确定性游戏。从根本上讲,这样一个游戏的竞技性是绝对不如象棋围棋之类信息完全的确定性游戏的。因为留给“运气”的空间越大,越是有可能出现选手的“实力”被运气玩弄的情况。

作为娱乐游戏来讲,即使新手也有将老手击败的可能,自然是一件非常好玩的事情。但是我想要做的麻将,或说是能让我热血起来的麻将,一定要是竞技游戏。大概的目标应该会是像桥牌那样,拥有运气成分,但是选手的实力仍然有非常充分的表现空间。接下来的一部分内容,会提到麻将规则中的运气,以及在统一规则下选取的部分。

除此之外,正如在之前提到的,麻将所拥有的burden of knowledge。这个东西虽然Zileas等人成天提,但是我对此毫不在意——正相反,我认为国标内为数繁多的番种设置有增加竞技性的意义。不过不能否认的是,对于新人来说,要想在国标麻将里凑够8番不是一件容易的事情。在这点上,说不定能够修改一些规则,让新人也能够比较容易和牌。在番种修订说明的部分里,这些修改会被提到。

最后,还有在各个麻将规则里出现的特色规则。这些规则有些是不是能够加入到统一规则中,以提高其竞技性呢?比如说,振听增加了防守方的策略性,却同时也使得进攻方无法做出一些匪夷所思的举动来听牌——这究竟是好呢还是不好?这些在地方规则探讨的部分会有所提及。

在接下来的部分里(如果我记得写下去的话),我们会慢慢对上面几点进行分析,并尝试进行“让这个游戏变得更有意思”的改造。

发表在 西学东渐 | 留下评论

Gundam AGE

先说一句,对于这部低龄向的新高达,我个人可是非常不喜欢的。既然叫做高达,就至少会对世界观之类的设定有一定的先入观念——毕竟作为一个系列,总是应该保持系列的主题。(比如说以描写人类之间的战争为主。)而且,目前公布的人物设定和机械设定实在是让人失望。

当然,当年G高达刚出来的时候,想必也有一堆人觉得竟然把高达拍成龙珠实在是太毛蛋了,结果后来发现G竟然是神作。所以归根结底,还是要看剧情演绎的怎么样才行。毕竟我们都应该知道,“以小孩为主要观众群体” != “骗小孩的”,子供向作品之中也有许多非常不错的作品。最近的例子大概就是去年的HC Precure我就看得很开心。

说到Precure,比较有意思的是初代的市场报告上写的是“主要面向观众为4-12岁的女性和16-35岁的男性”。因此我们大概可以反过来考察这次Gundam AGE的主要面向观众是4-12岁的男性和16-35岁的女性?这样就可以理解主角一家三代人为啥都是正太了。

至于机设,大河原邦男早已有“江河日下男”的绰号,其机械设定与其说退步不如说是在“高达”这个框架下的思维已经僵化了吧。至于日升为什么还是要找大河原来做机械设定,那就不是我等能够知道的了。但是确实,随着机器人动画的没落(已经没落了20年有余了啊……),“机械设定”这个职位似乎逐渐出现了断代现象,最近没有什么能让人感到耳目一新的设定了。像Turn A那样找外国人来做机设,恐怕才是能给机器人动画机设带来一些新鲜感的一条出路吧。

不过现在说归说,等今年秋天这个东西真的出了的时候,我估计还是会老老实实地跑去看的。到时候究竟是好是坏,时间自会给个说法的。

发表在 疾风快报 | 一条评论

动画系统学习笔记1

最近在跟着公司大牛Cyriaque大叔学习动画系统,因为要把我们游戏的动画引擎基本重写一遍。由于我之前没有自己做过真正的“现代化”的动画系统——不管是MUA2,还是我们目前的游戏所使用的动画系统,其实都是非常简单的,或者说是“上个时代”的——因此这当然是一个很好的学习经验。所以嘛,这里简单把一些东西在整个过程中想到和学到的记一下,以便参考。

由于第一行代码是今天早上才开始敲的,所以这个东西很显然会慢慢更新。

0. Know the goal.

作为RTS衍生出来的游戏,我们不需要像刺客信条那样完美的动画系统。但是起码应该做到比如,嗯,按照我们Lead Animator的话说,就是波斯王子那个等级的才行。

1. No one knows animation system.

我最初问C大叔有没有什么讲游戏动画系统的好书,答曰:“没有,现有的书所讲的都是非常简单初步的,而真正重要的部分基本都在人的脑子里。”换句话说,好的动画系统只有少数真正写过好的动画系统的人才知道应该写成什么样子。其他人对此一点概念都没有。怪不得很多游戏的动画系统都很一般。最近玩了玩那个RIFT,其动画系统在我看来就只能说非常原始了——所有的动作都是直接开始播放,而不是从前一个动作渐变到下一个动作。

2. I don’t know what you guys do in Maya.

对于一个好的系统来讲,最重要的是首先有“我们需要什么样的数据”,然后再让工具输出该种格式的数据。所以第一步不是跑去写Maya的插件,而是先把游戏内的系统建立起来,之后再想办法让Art方面能够生成这个系统需要的文件格式。任何相反的方法,其结果都会是灾难性的。

3. Now the tech.

动画数据归根结底就是一堆矩阵。在无压缩格式下,动画就是每一帧每一个骨骼的变换矩阵(或其等价数据,见后)。整个动画系统最基本的步骤,就是:

a. 获取当前每个动画当前帧所有骨骼的变换矩阵。

b. 按照每个动画的权重(由各个外部系统决定)将各个变换矩阵合成一个。

c. 渲染。

其中c相对最简单,最多改改shader。但是a和b都是各家有各家的做法,其所需要涉及到的因素之多实在超出乍看上去其描述的简单程度。这也是为什么好的动画系统难做的原因。

那么既然要重写整个动画系统,就自然要从a开始做起。对了,c不用重写,因为那是图像部分的事了。

发表在 西学东渐 | 留下评论

在东莞的旧家里,奶奶在各个房间之间走来走去,不时打开柜子之类,似乎往里面放些什么东西。问她究竟在做什么,她只答道“在最后的时间给你们一个最后的惊喜”。

数日后,老人留下的遗言是“在这个屋子里有我所藏着的宝物,你们去找吧”。年幼的我在书房的一个书柜中所找到的是一张字条,上面写着“这里收集了孙子送给我的贺卡”。纸条拿开,果然见到下面所放的贺卡种种。

于是我于哭泣中醒来,此梦亦免于被无意识之大海所吞噬。

发表在 未分类 | 留下评论

2010: An Earth Odyssey

(其实跑去google了一下,发现不少人也跟我一样起这个标题……)

2010年,已经退化成懒人的我简单说几句吧。

这一年的远征,从美国东岸重新打回了西岸,真乃是又一次横跨大陆的行动。不过这一次总算是找到了一段时间内的安居之地,不用成天到晚想着要再次搬家了吧——至少我是这么希望的。

不过最可惜的是由于各种原因,导致签证下来的非常晚,于是之后连锁反应使得我完全没有办法考/学车,所以直到现在也只能依靠其他人才能在这个地方四处移动。即使他们不介意,我个人也仍对需要有求于他人深感愧疚,距真正能够独立自主还颇有一段时间。这将作为2011年的首要目标吧。

此外的目标还有实现自我的革新,不过这个好象是个巨大长远的目标,只能一步一步慢慢来。不过,每次看到其他人做的游戏的时候,心中那“我也要继续制作游戏”的想法只要还没有消失,就一定能够抵达自己心目中的理想乡吧。

发表在 未分类 | 留下评论

DMV随感

在Culver City的DMV外面墙上画着这样一幅壁画:在荒凉的月球表面,穿着上面印有不同国家国旗的宇航员们正在合力建造一个月球基地。这幅画根据上面的说明,构思于1977年,完成于1979年,至今已有31年历史。

31年前的人类,目光早已放眼月球。然则31年后的现在呢?没想到人类却仍然在地球上爬行着,为着无谓的国际国内事务纠缠不休。这不禁让我觉得这么多年以来,人类作为一个文明,难道竟然毫无成长吗?

话又说回来,许多的科幻游戏/电影都最终沦为“打外星人”这个老套的题材,而并没有过多的真正激发观众对宇宙的向往。当然,这样的题材会很“好看”,或者说也很容易做得吸引人,但是真正能给人带来点思考的作品却寥寥无几。像Planetes/プラネテス这部动画一样,表现人类对宇宙的向往的神作真希望能够多点啊。

在这个意义上,游戏设计者也应该负起责任来,真正为人类的发展做一些有意义同时又有意思的游戏啊。

发表在 未分类 | 2条评论

最近工作的几个随感

1. UNICODE!

现在都什么年代了,如果应用程序不把Unicode放在眼里简直就是愚蠢的行为。不过诚然,C++本身对于Unicode的支持不是很好,于是我们很有可能需要一个包装界面才能对付……不过我想一般说来第一要点就是“凡是‘文本字符串’,都应该用wchar_t和std::wstring”。不然就会出现最近我在做的非常痛苦的在几个不同时代写的代码之间不停转换的情况。

2. 程序员的目的

游戏程序员存在的最终目的,就是要消灭“游戏程序员”的存在必要性。如果说一般程序是M-V-C的结构的话,那么游戏说来:

model = 定义游戏里出现的数据类型及其互动逻辑。很显然,数据类型定义好之后应该可以自动生成读取/存储数据的代码,之后只要用编辑器就可以任意添加修改游戏里的数据了。同时数据类型也可以将其需要公开的接口绑定到脚本,之后只要写脚本就行了。

view = 渲染。不言自明的,你不应该需要每个游戏重写一遍图形引擎。可惜这里可能不可避免的要有游戏本身专用的代码以及shader之类,不过这并不应该需要你大改图形引擎吧。

controller = 用户界面。某种意义上,游戏引擎的部分只要把输入输出进行简单的预处理,之后就扔给脚本好了。

你看,不需要程序员吧!只可惜现实中我们很难以做出这么完美的系统,但并不表示我们不应往这个方向努力。

3. “好的程序员删除代码。”

当我发现我们的代码库里面有三种不同的方式渲染文字,而其中至少一种尽管已经过时不应使用,但仍有几个地方在调用它的时候……

“这么多很明显我们永远不应该再用的代码为啥没人砍掉啊!”

发表在 西学东渐 | 一条评论

Hello, WordPress!

好吧,微軟……我服了你了。

然後發現一件事:以前在Live Space的草稿沒了@_@

发表在 社长记事 | 留下评论