|
|
|
@ -11,84 +11,166 @@
|
|
|
|
|
package com.github.drinkjava2.frog.brain;
|
|
|
|
|
|
|
|
|
|
import java.awt.Color;
|
|
|
|
|
import java.io.Serializable;
|
|
|
|
|
|
|
|
|
|
import com.github.drinkjava2.frog.Application;
|
|
|
|
|
import com.github.drinkjava2.frog.Env;
|
|
|
|
|
import com.github.drinkjava2.frog.Frog;
|
|
|
|
|
import com.github.drinkjava2.frog.util.RandomUtils;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Organ is a part of frog, organ can be saved in egg
|
|
|
|
|
* Organ is a cone-cylinder shape zone inside of brain,, organ can be saved in
|
|
|
|
|
* egg, organ will create cells in brain. Most organ's size, angle, location and
|
|
|
|
|
* cell parameters are randomly created
|
|
|
|
|
*
|
|
|
|
|
* 器官是脑的一部分,多个器官在脑内可以允许重叠出现在同一位置。
|
|
|
|
|
* 器官是脑的一部分,多个器官在脑内可以允许部分或完全重叠出现在同一脑内位置,器官都是可以变异的,所有器官都会保存在蛋里面。
|
|
|
|
|
*
|
|
|
|
|
* 器官是一种外形为锥圆柱体或长方体的脑内区,它的作用是在它的形状范围内在脑细胞内登记行为,当然如果脑细胞不存在它首先会新建一个脑细胞。
|
|
|
|
|
* 器官的数量、形状、大小、角度、位置、神经元分布方式,以及神经元的内部参数可以随机生成和变异, 并由生存竟争来淘汰筛选。
|
|
|
|
|
*
|
|
|
|
|
* 器官可以组合成复杂的网络结构或曲折的信号传导路径,例如一个圆柱形神经传导器官,可以变异为由两个圆柱状传导器官首尾相接,然后这两个器官各
|
|
|
|
|
* 自变异,就有可能形成弯的传导路径或更复杂的网络结构。看起来复杂,但大自然可以做到比这更复杂的进化逻辑,先假设大自然是万能的,只要是有用的
|
|
|
|
|
* 变异逻辑,就有可能出现。
|
|
|
|
|
*
|
|
|
|
|
* 器官描述细胞的行为,其中洞数量和参数是有可能随机变异的。行为则是硬编码不可以变异,模拟单个神经元的逻辑,不同的细胞可以登记属于多个不同的
|
|
|
|
|
* 器官,虽然器官行为不可以变异,但是可以写出尽可能多种不同的行为(貌似神经元的活动也只有有限的几种),由生存竟争来筛选。
|
|
|
|
|
*
|
|
|
|
|
* 器官通常是单例,每个脑细胞都指向一些器官单例,器官这个单例参与神经活动时是无状态的,有状态的神经元的参数是保存在Cell中而不是器官中,这种设计是为了减少
|
|
|
|
|
* Cell的内存占用
|
|
|
|
|
*
|
|
|
|
|
* 信息(光子)的逻辑:
|
|
|
|
|
* 能量上限很低的细胞,相当于信息的中转站,来多少光子就转发多少光子;能量上限高的细胞,可以起到记忆作用,它的原理是多个洞、反复累集的光子能量,
|
|
|
|
|
* 可以在收到某个方向光子的信号后短暂释放,以这个细胞中为心形成一个新的波源,波的发散方向就是洞的分布方向,只有位于波的驻点位置的细胞才会吸收和释放更多
|
|
|
|
|
* 能量,光子总是直线传播,不在合适交点(波的驻点)位置的细胞吸收不到能量。
|
|
|
|
|
*
|
|
|
|
|
* 光子自带传播方向,并在每个测试步长中自动走一格,为什么还需要脑细胞的静态洞和动态洞?<br/>
|
|
|
|
|
* 因为光子只能直线传播,而洞具有分发、收集、改变光子方向等作用,能增加脑的复杂,cell中根据光子的方向动态生成的洞是智能的基石,它能将有关联关系的
|
|
|
|
|
* 重复光子信号从海量的自然界噪声信号中抽取出来产生关联。
|
|
|
|
|
*
|
|
|
|
|
* @author Yong Zhu
|
|
|
|
|
* @since 1.0.4
|
|
|
|
|
*/
|
|
|
|
|
public class Organ extends Zone {
|
|
|
|
|
public class Organ implements Serializable, Cloneable {// 因为要保存在蛋文件里,所以必须支持串行化
|
|
|
|
|
private static final long serialVersionUID = 1L;
|
|
|
|
|
public String name; // 显示在脑图上的器官名称,可选
|
|
|
|
|
public long fat = 0; // 如果活跃多,fat值高,则保留(及变异)的可能性大,反之则很可能丢弃掉
|
|
|
|
|
// public float organWasteEnergy = 0.05f; //
|
|
|
|
|
// 器官在每个测试循环中需要消耗青蛙多少能量,可以通过调节这个参数抑制器官数量无限增长
|
|
|
|
|
public float organActiveEnergy = 1; // 输出器官激活需要消耗每个脑细胞多少能量
|
|
|
|
|
public float organOutputEnergy = 2; // 感觉器官激活会给每个脑细胞增加多少能量
|
|
|
|
|
public boolean initilized; // 通过这个标记判断是否需要手工给定它的参数初值
|
|
|
|
|
|
|
|
|
|
public boolean allowBorrow() { // 是否允许在精子中将这个器官借出
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Each loop step call active method, Child class can override this method */
|
|
|
|
|
public void active(Frog f) { // 每一步都会调用器官的active方法 ,缺省啥也不干
|
|
|
|
|
}
|
|
|
|
|
// 以下是各种器官类型,每个神经元都属于一个器官,每个器官都有一个type类型参数
|
|
|
|
|
public static final int EMPTY = 0;// 空细胞,不处理光子
|
|
|
|
|
public static final int MOVE = 1;// 会让光子沿直线走一格
|
|
|
|
|
public static final int MOVE_JELLY = 2;// 会让光子沿直线走一格,并在下一个细胞上打洞
|
|
|
|
|
public static final int EYE = 3;// 眼细胞,会根据cell激活度产生发散到各个方向的光子
|
|
|
|
|
public static final int EAR = 4;// 耳细胞,类似眼细胞,不同点是为了简化,脑内听觉区和输入区混用一个区,所以它也可吸收光子,倒过来激活cell
|
|
|
|
|
public static final int JELLY = 5; // 光子在当前细胞上可以打出洞来
|
|
|
|
|
public static final int STATIC = 6; // 只有静态洞的细胞,它忽略动态洞参数
|
|
|
|
|
public static final int MIX = 7; // 同时具有静态和动态洞的细胞
|
|
|
|
|
|
|
|
|
|
/** If active in this organ's zone? */
|
|
|
|
|
public boolean outputActive(Frog f) { // 如果一个细胞能量>10,且它的输出触突位于这个器官内,则器官被激活
|
|
|
|
|
for (Cell cell : f.cells) {
|
|
|
|
|
if (cell.energy > organActiveEnergy)
|
|
|
|
|
for (Output output : cell.outputs) { //
|
|
|
|
|
if (this.nearby(output)) {
|
|
|
|
|
cell.organ.fat++;
|
|
|
|
|
cell.energy -= 30;//
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
public static final int TYPE_QTY = 8;// 所有的type都是预先写好在这里的,自动生成的type也只能在写好的type里选一个
|
|
|
|
|
|
|
|
|
|
private static int organNoIndex = 1;
|
|
|
|
|
public int organNo = organNoIndex++; // 每个器官都有一个唯一的编号,作用是同一个编号的光子间将不产生绑定
|
|
|
|
|
public int color = 1;// 这个不重要,表示它生成的光子的显示在脑图中的颜色号,见ColorUtils
|
|
|
|
|
public float fat = 0;// 细胞活跃多,则fat值大,如果fat值很低,则这个器官被丢弃的可能性加大,这个值很重要,它使得孤岛器官被淘汰
|
|
|
|
|
public boolean allowVary;// 是否允许变异,有一些器官是手工创建的,在项目初级阶段禁止它们参与变异和生存竟争。
|
|
|
|
|
public boolean allowBorrow;// 是否允许在精子中将这个器官借出,有一些器官是手工创建的,在项目初级阶段禁止它们借出
|
|
|
|
|
public String organName;// 器官的名字,通常只有手工创建的器官才有名字,可以用frog.findOrganByName来查找到这个器官
|
|
|
|
|
|
|
|
|
|
// ======= 本行以下是一些假设的参数,受变异和生存竟争影响 ,并通过大样本数、随机变异、生存竟争来进行多参数的优化选择 ========
|
|
|
|
|
|
|
|
|
|
public int type; // 器官类型,见上面的常量定义,这个字段通常很稳定。一旦变异,将从根本上改变器官的播种行为和神经元的行为
|
|
|
|
|
|
|
|
|
|
public Shape shape; // 器官的形状,不同的形状要写出不同的播种行为
|
|
|
|
|
|
|
|
|
|
public float cellDistance; // 细胞播种间隔,每隔多少个cell进行器官行为的播种
|
|
|
|
|
|
|
|
|
|
public float centerDensityRate; // 中心相对于边沿的播种密度比,为1时为均匀分布
|
|
|
|
|
|
|
|
|
|
public int holeLimit;// 细胞允许创建动态洞的数量上限,详见Cell类的holes.字段
|
|
|
|
|
|
|
|
|
|
public float energyLimit;// 细胞能量上限,当能量超过能存贮的上限,多余的光子能量将不被细胞吸收,直接煙灭或以光子的形式转发出去
|
|
|
|
|
|
|
|
|
|
public float outputRate;// 细胞激活后,一次脉冲会释放出细胞存贮的能量比率(或释放出一个固定值), 根据能量守恒,这个值通常小于1
|
|
|
|
|
|
|
|
|
|
public float outputDoor;// 输出阀值,细胞当前能量小于这个阀值时,细胞不会产生输出
|
|
|
|
|
|
|
|
|
|
public float inputRate;// 能量接收率,细胞存贮的能量占输入能量的比率(或吸收一个固定值), 根据能量守恒,这个值通常小于1
|
|
|
|
|
|
|
|
|
|
public float inputDoor;// 接收阀值,接收的能量小于这个阀值时,细胞不会吸收这份能量,直接煙灭或以光子的形式转发出去
|
|
|
|
|
|
|
|
|
|
public float radius;// 细胞即使没有洞,也可以处理光子,这个radius是细胞的管辖半径,但处理信号角度只限于穿透和反射或6个正方向
|
|
|
|
|
|
|
|
|
|
public float dropRate;// 是一个介于0~1的值,反映了细胞存贮能量的下降速率,在每一步长中细胞能量都以这个速率损失,可以参考遗忘曲线
|
|
|
|
|
|
|
|
|
|
// =====注意以下三个字段可以让细胞具备一些固定角度的洞,这个不占内存,但缺点是不灵活,不智能,详见与Cell类中动态洞的对比 =====
|
|
|
|
|
public Hole[] holes; // 输出洞
|
|
|
|
|
|
|
|
|
|
public Organ() {// 缺省构造器,生成具有缺省参数但没有形状的器官
|
|
|
|
|
allowVary = true;
|
|
|
|
|
allowBorrow = true;
|
|
|
|
|
type = 0;
|
|
|
|
|
cellDistance = 1;
|
|
|
|
|
centerDensityRate = 1;
|
|
|
|
|
holeLimit = 10;
|
|
|
|
|
energyLimit = 100;
|
|
|
|
|
outputRate = 30;
|
|
|
|
|
outputDoor = 30;
|
|
|
|
|
inputRate = 100;
|
|
|
|
|
inputDoor = 5;
|
|
|
|
|
radius = 1;
|
|
|
|
|
dropRate = 95;
|
|
|
|
|
holes = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Only call once after organ be created */
|
|
|
|
|
public Organ[] vary(Frog f) { // 器官变异,仅会在青蛙下蛋时即new Egg(frog)中被调用一次,返回本身或变异后的一个或一组类似器官返回
|
|
|
|
|
if (!allowVary)
|
|
|
|
|
return new Organ[] { this };// 如果不允许变异,器官就把自身返回,存放在蛋里
|
|
|
|
|
// 各参数 随机有大概率小变异,小概率大变异,极小概率极大变异
|
|
|
|
|
type = RandomUtils.vary(type, 10);// 这个type通常不允许变,所以只给它10%的机率去变, 也就是说在正常变异概率上再乘以10%的变异可能性
|
|
|
|
|
shape = RandomUtils.vary(shape);
|
|
|
|
|
cellDistance = RandomUtils.vary(cellDistance);
|
|
|
|
|
centerDensityRate = RandomUtils.vary(centerDensityRate);
|
|
|
|
|
holeLimit = RandomUtils.vary(holeLimit);
|
|
|
|
|
energyLimit = RandomUtils.vary(energyLimit);
|
|
|
|
|
outputRate = RandomUtils.vary(outputRate);
|
|
|
|
|
outputDoor = RandomUtils.vary(outputDoor);
|
|
|
|
|
inputRate = RandomUtils.vary(inputRate);
|
|
|
|
|
inputDoor = RandomUtils.vary(inputDoor);
|
|
|
|
|
radius = RandomUtils.vary(radius);
|
|
|
|
|
dropRate = RandomUtils.vary(dropRate);
|
|
|
|
|
holes = RandomUtils.vary(holes);
|
|
|
|
|
return new Organ[] { this };
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
/** Only call once when frog created , Child class can override this method */
|
|
|
|
|
public void init(Frog f, int orgNo) { // 在青蛙生成时会调用这个方法,进行一些初始化,通常是根据参数来播种脑细胞
|
|
|
|
|
// 里是器官播种脑细胞的具体代码,对于手工生成的器官,也可以重写这个方法,对于自动生成的器官,必须根据type和shape等来播种,要写死在这里
|
|
|
|
|
if (shape != null)
|
|
|
|
|
shape.createCellsRegOrgan(f, orgNo); // 先均匀播种脑细胞试试
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Set X, Y, Radius, name of current Organ */
|
|
|
|
|
public Organ setXYRN(float x, float y, float r, String name) {
|
|
|
|
|
this.setXYR(x, y, r);
|
|
|
|
|
this.name = name;
|
|
|
|
|
return this;
|
|
|
|
|
/** each step will call Organ's active methodd */
|
|
|
|
|
public void active(Frog f) { // 每一步测试都会调用active方法,通常用于手动生成的器官,自动生成的器官其行为仅由脑细胞来决定
|
|
|
|
|
// do nothing
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Child class can override this method to drawing picture */
|
|
|
|
|
public void drawOnBrainPicture(Frog f, BrainPicture pic) {// 把自已这个器官在脑图上显示出来,子类可以重写这个方法
|
|
|
|
|
if (!Application.SHOW_FIRST_FROG_BRAIN)
|
|
|
|
|
public void drawOnBrainPicture(Frog f, BrainPicture pic) { // 把器官的轮廓显示在脑图上
|
|
|
|
|
if (shape == null)
|
|
|
|
|
return;// 如果没有形状,就不画
|
|
|
|
|
if (!Env.SHOW_FIRST_FROG_BRAIN || !f.alive) // 如果不允许画或青蛙死了,就直接返回
|
|
|
|
|
return;
|
|
|
|
|
pic.setColor(Color.BLACK); // 缺省是黑色
|
|
|
|
|
pic.drawZone(this);
|
|
|
|
|
if (this.name != null)
|
|
|
|
|
pic.drawText(this, String.valueOf(this.name));
|
|
|
|
|
pic.setPicColor(Color.LIGHT_GRAY); // 缺省是灰色
|
|
|
|
|
shape.drawOnBrainPicture(pic);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Only call once when frog created , Child class can override this method */
|
|
|
|
|
public void initFrog(Frog f) { // 仅在Frog生成时这个方法会调用一次,缺省啥也不干,通常用于Group子类的初始化
|
|
|
|
|
public static Organ randomCuboidOrgan() {
|
|
|
|
|
Organ o = new Organ();
|
|
|
|
|
o.shape = RandomUtils.randomCuboid();
|
|
|
|
|
return o;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Only call once after organ be created by new() method */
|
|
|
|
|
public Organ[] vary() { // 在下蛋时每个器官会调用这个方法,缺省返回一个类似自已的副本,子类通常要覆盖这个方法
|
|
|
|
|
Organ newOrgan = null;
|
|
|
|
|
try {
|
|
|
|
|
newOrgan = this.getClass().newInstance();
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
throw new UnknownError("Can not make new Organ copy for " + this);
|
|
|
|
|
}
|
|
|
|
|
copyXYR(this, newOrgan);
|
|
|
|
|
newOrgan.name = this.name;
|
|
|
|
|
newOrgan.fat = this.fat;
|
|
|
|
|
return new Organ[] { newOrgan };
|
|
|
|
|
public static Organ randomConeOrgan() {
|
|
|
|
|
Organ o = new Organ();
|
|
|
|
|
o.shape = RandomUtils.randomCone();
|
|
|
|
|
return o;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|