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); // 爱德华接受巡逻任务
今天就到这里,下次再继续吧。