simsocial 第一步,基本的世界和物种

上接此文 http://nowhere.shareyan.cn/blog/randoms/54a934632e57004ea671c2ba

按照上次的思路最近都在进行尝试中。要实现上面的想法,首先要有一个基本的环境,就是自己要构建一个简单的世界。为了方便起见我就用html,js来构造了自己的测试世界。

项目地址 https://github.com/randoms/simsocial
这是一个二维的世界,基本单位是一个一个的小格子,如下图所示



有了这个简单的二维世界就可以开始构建物种了。

震荡现象
我最先构造的物种就是树。最初的基本想法是,物种要能够生长,繁殖,和死亡。按照这个思路实现之后发现一个很有趣的问题。假如最初我只放两棵树在世界里,很快树会繁殖生长,占满整个屏幕。但是虽然在占满世界前树的总数大体上是向上涨的。但是会出现周期性的大面积死亡,大面积生长。为什么会出现这个呢?这就是传说中的婴儿潮。在某一时间内大量出生之后,假设物种的寿命比较接近,在我的最初设计中所有树的属性数值都是一样的,这样就会出现大量的生物同时死亡。然后由于死亡之后有比较多的资源被空下来,这样就很利于剩下的生物生长。结果又出现大面积出生。由此这样不断的震荡下去。

增加生物多样性
产生震荡现象的原因就是大家的寿命都差不多,所以只要大家的寿命有些差别,死亡时间能够错开点,这样就可以避免震荡。在现实世界中,生物多样性单一也是很致命的。修改后的寿命只是在一个值的基础上加上一个随机数。但是还可以有更好玩的方法。为什么不给他们加上基因呢?

基因的设计
我觉的我的这个基因系统设计的是很不错的,在这里介绍一下。首先基因的功能是什么。就是传递亲代的属性给后代,在传递过程中要发生变异,交叉等等。抽象出来就是一个数。子代要从亲代继承一个数值,然后再继承的过程中要产生一些变化。
我的基因模型设计:首先生成一个很长的字符串作为DNA,在程序中我生成了一个2000位的,由01构成的字符串作为DNA。然后随机的选择其中的一段作为一个基因型。通过一定的计算把这一小串字符转换成一个数字,作为此基因型的数值。这样变异和交叉就可以和现实中一样发生在DNA层次。还是贴一段代码更容易理解。
#在这里输入代码(从下一行开始,不要删除我...)


function Gene(parents){
    this.mutationRate = 0.01;
    this.crossoverRate = 0.7;
    this.geneLength = 2000; // gene 2000bit
    this.phenoLength = 10;
    this.phenoList = {};

    if(typeof parents != "undefined"){
      this.dna = parents;
    }else{
      this.dna = "";
      for(var i=0;i<this.geneLength;i++){
        if(Math.random() > 0.5)this.dna += "1";
        else this.dna += 0;
      }
    }

    this.mutation = function(){
      // 开始变异
      for(var i=0;i<this.geneLength;i++){
        if(Math.random() < this.mutationRate){
          if(this.dna.charAt(i) == "1"){
            this.dna = replacePos(this.dna, i, "0");
          }
          else this.dna = replacePos(this.dna, i, "1");
        }
      }
    }
    this.mutation();

    this.getPhenotype = function(name){
      // 输入一个0-1的数,返回一个0-1的数
      // 起始位置
      var code = Math.random();
      if(this.phenoList.hasOwnProperty(name)){
        code = this.phenoList[name];
      }else{
        this.phenoList[name] = code;
      }
      var start = parseInt(code*this.geneLength);
      var mnum = this.dna.substring(start, start + this.phenoLength);
      return parseInt(mnum, 2)/Math.pow(2, 10);
    }

    this.getNext = function(mate){
      var child = new Gene(this.dna);
      child.phenoList = this.phenoList;
      return child;
    }

    this.crossover = function(mateGene){
      // merge phenoList
      $.extend(this.phenoList, mateGene.phenoList);
      mateGene.phenoList = this.phenoList;

      // random start point
      var start = parseInt(Math.random()*this.geneLength);
      var tempDna = this.dna.substring(start);
      if(Math.random() < this.crossoverRate){
        this.dna = this.dna.substring(0,start) + mateGene.dna.substring(start);
        mateGene.dna = mateGene.dna.substring(0,start) + tempDna;
      }

    }

    this.matewith = function(mate){
      var mchild = this.getNext();
      var mateChild = mate.getNext();
      mchild.crossover(mateChild);
      if(Math.random() > 0.5){
        return mchild;
      }else{
        return mateChild;
      }
      
    }

    function replacePos(strObj, pos, replacetext){
       var str = strObj.substr(0, pos-1) + replacetext + strObj.substring(pos, strObj.length);
       return str;
    }
  }


这个就是我的基因对象。用起来也是很方便,比如下面就是一个人的,由基因计算属性的过程。

#在这里输入代码(从下一行开始,不要删除我...)

this.name = name;
this.gene = gene|| new World.Gene();
this.geneGender = this.gene.getPhenotype("gender");
if(this.geneGender >= 0.5){
    this.gender = "male";
    this.background = '../img/male.svg';
}else{
    this.gender = "female";
    this.background = '../img/female.svg';
}


这是由性别基因确定性别的过程。

表现型
有了基因之后,还要考虑环境对于生物的影响。可能一棵树的基因决定它能活很久,但是你却把它种在很差的环境下,那么它可能只能活很短的时间。所以一个生物的属性应该是由基因和环境共同决定的。这样我就给环境增加了一个是否适合植物生存的参数。在这个参数的影响下,即使相同基因也会表现出很不同的性质。

还有更多
考虑了基因和环境,还有更多的要考虑。生物对于环境的耐受程度,生物对于环境的影响,环境的自我变化。这些我都有考虑。最终形成了现在的生物模型。在考虑了环境对生物的影响和生物对环境的影响之后,会发生很多有趣的现象。比如能活下的植物可能会是会污染环境,但是对环境的耐受能力又比较强的物种。亦或是能改善环境,又繁殖能力较强的物种。

demo http://nowhere.shareyan.cn/media/demo/simsocial/src/html/home.html

这个看起来还是非常有意思的。

开始形成社会
基本的生物模型有了下一步就是社会模型的建立了。我的整个想法的核心在于”工作“,城镇通过发布工作来利用市民发展自己,市民通过工作获取自己的基本需求。所以为了进行下一步先要构建一个简单的工作。然后我就构建了巡逻这个工作。城镇通过这个任务从市民那里获取到周围的信息,市民通过这个工作来获取自己的基本物质需求。现在的版本没有涉及到工作产生的过程,这个是整个计划的核心,是以后需要重点设计的地方,现在只是工作被接受后市民完成工作过程的设计。在这里就是市民如何巡逻的。
demo http://nowhere.shareyan.cn/media/demo/simsocial2/src/html/home.html

例子中的这人叫做爱德华,他在名字叫Raw的城内进行巡逻。巡逻要求是城内的每个地方信息的更新时间不能慢于50个回合。但是爱德华的巡逻速度是很快的,经常巡逻完了就休息一会,然后再接着巡逻。

爱德华是有自己的个性的,他有自己独特的基因。但是如何让他的基因来影响他的工作表现我还没想好。

爱德华接受工作的代码也很简单,我故意设计得像自然语言一样。

#在这里输入代码(从下一行开始,不要删除我...)

var mworld = new World.World(context); //生成世界
var rawCity = new Town.Town(mworld, 12, 8, "Raw"); //生成raw城
var adward = new People.People(mworld, 15, 15, "Adward"); //生成爱德华
var patrolJob = rawCity.makeDecision(); // 城镇发布巡逻任务
adward.takeJob(patrolJob); // 爱德华接受巡逻任务

今天就到这里,下次再继续吧。
标签上篇: MineCraft 社会mod计划 次篇: 创建自己的Linux Service