history 016a, OneInputJudge

This commit is contained in:
yong 2024-03-07 02:09:26 -07:00
parent cbc3e390de
commit 6ac3f0ba9d
57 changed files with 4538 additions and 1042 deletions

View File

@ -31,6 +31,13 @@
本次更新在015_testinput3目录下只有一个TestInput3.java文件这是个临时测试类用穷举法来测试一个如果有三个视觉输入细胞、一个执行细胞的情况下能不能实现所有正常模式识别即输入有2的7次方=128种组合输出有0/1两种信号。实测发现这128种情况下有31种输入是无法通过调整权重来实现输出逻辑的也就是说31种情况下是无解的。类似地如果有4个输入在32768组合中有29987种情况是无解的。所以如果输入信号大于两个时必须改进结构增加层数或在水平方向平铺增加输入输出细胞。
目前有两个问题:一是用甜激素增加所有最近活跃的正权重、苦激素增加所有最近活跃的负权重这种方式能不能完全代替穷举法找到所有解;二是有没有办法只使用浅层神经网络实现模式识别?(人脑的皮层占了很大比重,顾名思义,皮层可能就是浅层的,而不是象深度学习一样是极深层的神经网络)这个模式识别不需要很精准能达到约15x15的分辨率就可以接近人脑的模式识别功能了因为人脑还会结合动眼和变焦两个功能来缩小需要进行模式识别信号的像素。这两个问题目前深度学习可能能找到答案但我不知道有没有人采用这种正负权重双通道输入的结构这种双通道输入结构实际上才是实际生物的神经网络构成。
2024-02-29 还是单点信号输入
这个更新在history\016a_OneInput目录下实现当只有一个视信号输入时咬细胞随食物出现激活食物消失停止激活这是个最简单的逻辑。这个实现还是使用连线算法来模拟触突因为细胞少时连线算法编程更方便一点连线算法是以连线的绝对坐标来定位分裂算法是以相对每个细胞的相对坐标来定位。本次更新中有一个视细胞一个甜味感觉细胞一个苦味感觉细胞(咬错时激活),一个活动细胞(始终激活和几个空白细胞。随机连线的参数是在常量随机数组上随机截取一段这次引入了分组功能把每种连线给一个编号每个编号相同的连线拥有相同起始位置的一串固定参数固定参数是指一旦生成就不再改变的参数。可变参数如权重等需要在运行期为每个连线上的参数分配一个内存。固定参数不管有多少每个连线只需要给一个编号即可可以节约内存。这个类似生物脑可变参数不在基因里保存因为可变出生时是空白的也就不需要占用基因位来存贮。一串固定参数可以简化成一个编号然后编号们又由分裂算法来控制海量的细胞可以用很少的字节数来控制这就是为什么人类有100亿个不同位置和功能的脑细胞但基因信息量却只有850MB的原因。
下一步计划是2点、3点、4点线性信号模式识别2x2、3x3二维信号模式识别, 一点一点向上实现,大的图像将引入动眼和变焦细胞。
从2点开始可能引入饥饿细胞在饥饿状态下青蛙会有更多随机试咬动作以增加形成正确条件反射的机率。当然以后随着模式识别复杂化就算青蛙都饿死也很难触发正确的随机动作就要考虑引入人为的训练信号了。
## 运行方式 | Run
运行core或history各个子目录下的run.bat批处理文件即可启动运行history下有多个子目录按版本号顺序排列存放着这个项目演化过程中的主要历史版本供演示。

View File

@ -2,12 +2,12 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.gitee.drinkjava2</groupId>
<artifactId>frog015</artifactId>
<artifactId>frog016b</artifactId>
<packaging>jar</packaging>
<version>15.0</version>
<version>16b.0</version>
<name>frog</name>
<description>当前目标是用分裂算法来实现第一个具备模式识别功能的神经网络</description>
<description>当前目标是用分裂算法来实现模式识别,当前小目标是实现两点输入的模式识别</description>
<url>https://gitee.com/drinkjava2/frog</url>
<issueManagement>

View File

@ -12,6 +12,7 @@ package com.gitee.drinkjava2.frog;
import static com.gitee.drinkjava2.frog.brain.Genes.GENE_NUMBERS;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferedImage;
@ -20,8 +21,8 @@ import java.util.ArrayList;
import javax.imageio.ImageIO;
import com.gitee.drinkjava2.frog.brain.Consts;
import com.gitee.drinkjava2.frog.brain.Genes;
import com.gitee.drinkjava2.frog.brain.Line;
import com.gitee.drinkjava2.frog.egg.Egg;
import com.gitee.drinkjava2.frog.objects.Material;
import com.gitee.drinkjava2.frog.util.GeneUtils;
@ -50,7 +51,10 @@ public abstract class Animal {// 这个程序大量用到public变量而不是ge
public ArrayList<ArrayList<Integer>> genes = new ArrayList<>(); // 基因是多个数列有点象多条染色体每个数列都代表一个基因的分裂次序(8叉/4叉/2叉)
public int[] consts = new int[Consts.CountsQTY]; //常量基因用来存放不参与分裂算法的全局常量这些常量也参与遗传算法筛选规则是有大概率小变异小概率大变异见constGenesMutation方法
public static int CountsQTY = 100; //常量总数多少
public float[] consts = new float[CountsQTY]; // 常量范围0~1之间这些常量并不常会参与遗传算法筛选规则是有大概率小变异小概率大变异
public ArrayList<int[]> lines = new ArrayList<>();
/** brain cells每个细胞对应一个神经元long是64位所以目前一个细胞只能允许最多64个基因64个基因有些是8叉分裂有些是4叉分裂
* 如果今后要扩充到超过64个基因限制可以定义多个三维数组同一个细胞由多个三维数组相同坐标位置的基因共同表达
@ -59,27 +63,27 @@ public abstract class Animal {// 这个程序大量用到public变量而不是ge
public float[][][] energys = new float[Env.BRAIN_SIZE][Env.BRAIN_SIZE][Env.BRAIN_SIZE]; //每个细胞的能量值细胞能量不参与打分打分是由fat变量承担
public int[][][][] holes = new int[Env.BRAIN_SIZE][Env.BRAIN_SIZE][Env.BRAIN_SIZE][]; //每个细胞的洞相当于触突
public int xPos; // animal在Env中的x坐标
public int yPos; // animal在Env中的y坐标
public long fat = 1000000000; // 青蛙的肥胖度, 只有胖的青蛙才允许下蛋, 以前版本这个变量名为energy为了不和脑细胞的能量重名从这个版本起改名为fat
public boolean alive = true; // 设为false表示青蛙死掉了将不参与计算和显示以节省时间
public int ateFood = 0; // 青蛙曾吃过的食物总数
public int ateWrong = 0; // 青蛙咬了个空气的次数
public int ateMiss = 0; // 该咬却没咬下去的次数
public int no; // 青蛙在Env.animals中的序号从1开始 会在运行期写到当前brick的最低位可利用Env.animals.get(no-1)快速定位青蛙
public int animalMaterial;
public Image animalImage;
public Animal(Egg egg) {// x, y 是虑拟环境的坐标
System.arraycopy(egg.constGenes, 0, this.consts, 0, consts.length);//从蛋中拷一份全局参数
public Animal(Egg egg) {//构造方法Animal从蛋中诞生
System.arraycopy(egg.consts, 0, this.consts, 0, consts.length);//从蛋中拷一份全局参数
for (int i = 0; i < GENE_NUMBERS; i++) {
genes.add(new ArrayList<>());
}
int i = 0;
for (ArrayList<Integer> gene : egg.genes)//动物的基因是蛋的基因的拷贝
genes.get(i++).addAll(gene);
lines.addAll(egg.lines); //复制蛋的所有线条
i = 0;
if (Env.BORN_AT_RANDOM_PLACE) { //是否随机出生在地图上?
xPos = RandomUtils.nextInt(Env.ENV_WIDTH);
@ -99,11 +103,15 @@ public abstract class Animal {// 这个程序大量用到public变量而不是ge
}
public void initAnimal() { // 初始化animal,生成脑细胞是在这一步这个方法是在当前屏animal生成之后调用比方说有一千个青蛙分为500屏测试每屏只生成2个青蛙的脑细胞可以节约内存
GeneUtils.geneMutation(this); //有小概率基因突变
Consts.constMutation(this);//常量基因突变
if (RandomUtils.percent(40))
constMutate();//常量基因突变, 线条的参数都在常量里
Line.randomAddorRemoveLine(this);// //Line的随机增删变异
GeneUtils.geneMutation(this); //分裂算法控制的基因突变
if (RandomUtils.percent(5))
for (ArrayList<Integer> gene : genes) //基因多也要适当小扣点分防止基因无限增长
fat -= gene.size();
if (RandomUtils.percent(3)) //线条多也小扣点分防止线条无限增长
fat -= lines.size() / 10;
GeneUtils.createCellsFromGene(this); //根据基因分裂生成脑细胞
}
@ -116,12 +124,27 @@ public abstract class Animal {// 这个程序大量用到public变量而不是ge
kill();
return false;
}
holesReduce(); //所有细胞上的洞都随时间消逝即信息的遗忘旧的不去新的不来
this.setEng(Genes.ACT_POS, 1f); //ACT这个细胞就象太阳永远保持激活某些情况下当无外界信号时它是驱动系统运行的能量来源
Genes.active(this, step); //调用每个细胞的活动重要
Line.active(this, step); //调用每个连线(触突)的活动重要
return alive;
}
public void constMutate() { // 全局参数变异, 这一个方法此动物个体的所有常量
if (RandomUtils.percent(30)) //这个30%机率的变异方法让所有常量都有3%的机率随机在0~1之间重新取值
for (int i = 0; i < CountsQTY; i++) {
if (RandomUtils.percent(3))
consts[i] = RandomUtils.nextFloat(); //debug
}
if (RandomUtils.percent(10)) //这个10%机率的变异方法让所有常量都有5%的机率随机在原基础上变异即大概率有小变异小概率有大变异
for (int i = 0; i < CountsQTY; i++) {
if (RandomUtils.percent(5))
consts[i] = RandomUtils.vary(consts[i]);
}
}
private static final int MIN_FAT_LIMIT = Integer.MIN_VALUE + 5000;
private static final int MAX_FAT_LIMIT = Integer.MAX_VALUE - 5000;
@ -147,10 +170,18 @@ public abstract class Animal {// 这个程序大量用到public变量而不是ge
public void kill() { this.alive = false; changeFat(-5000000); Env.clearMaterial(xPos, yPos, animalMaterial); } //kill是最大的惩罚
//@formatter:on
public void show(Graphics g) {// 显示当前动物
public void showInEnv(Graphics g) {// 在虚拟环境显示当前动物这个方法直接调用Env的Graphics对象
if (g != null) //这个版本借用环境区测试模式功能不需要显示青蛙所以直接跳出
return;
if (no == (Env.current_screen * Env.FROG_PER_SCREEN + 1)) { //如果是当前第一个青蛙给它画个红圈
Color c = g.getColor();
g.setColor(Color.red);
g.drawArc(xPos - 15, yPos - 15, 30, 30, 0, 360);
g.setColor(c);
}
if (!alive)
return;
//g.drawImage(animalImage, xPos - 8, yPos - 8, 16, 16, null);// 减去坐标保证嘴巴显示在当前x,y处
g.drawImage(animalImage, xPos - 8, yPos - 8, 16, 16, null);// 减去坐标保证嘴巴显示在当前x,y处
}
/** Check if x,y,z out of animal's brain range */
@ -166,25 +197,25 @@ public abstract class Animal {// 这个程序大量用到public变量而不是ge
return cells[x][y][z] > 0;
}
public void setEng1(int x, int y, int z) { //打开指定的xyz坐标对应的cell能量值为极大
energys[x][y][z] = 1;
public void setEng(int x, int y, int z, float e) { //打开指定的xyz坐标对应的cell能量值为极大
if (e > 1)
e = 1;
if (e < 0)
e = 0;
energys[x][y][z] = e;
}
public void setEng1(int[] a) { //打开指定的a坐标对应的cell能量值为极大
energys[a[0]][a[1]][a[2]] =1;
}
public void setEng0(int x, int y, int z) { //关闭指定的xyz坐标对应的cell能量值为0
energys[x][y][z] = 0;
}
public void setEng0(int[] a) {//关闭指定的a坐标对应的cell能量值为0
energys[a[0]][a[1]][a[2]] = 0;
public void setEng(int[] a, float e) { //打开指定的xyz坐标对应的cell能量值为极大
if (e > 1)
e = 1;
if (e < 0)
e = 0;
energys[a[0]][a[1]][a[2]] = e;
}
public void addEng(int[] a, float e) {//指定的a坐标对应的cell能量值加e
addEng(a[0], a[1], a[2], e);
}
}
public void addEng(int x, int y, int z, float e) {//指定的a坐标对应的cell能量值加e
if (cells[x][y][z] == 0)
@ -192,117 +223,17 @@ public abstract class Animal {// 这个程序大量用到public变量而不是ge
float eng = energys[x][y][z] + e;
if (eng > 1) //如果饱和不再增加通过这个方法可以实现异或逻辑或更复杂的模式识别详见TestInput3测试
eng = 1;
if (eng < 0) //回到传统方式细胞不允许出现负能量但是权值即树突的正负两个通道中的负通道上可以出现负信号这个与实际细胞的抑制信号相似
if (eng < 0) //回到传统方式细胞不允许出现负能量但是能量可以出现负值这个与实际细胞的抑制信号相似
eng = 0;
energys[x][y][z] = eng;
}
}
public float getEng(int[] a) {//返回指定的a坐标对应的cell能量值
return energys[a[0]][a[1]][a[2]];
}
public float getEng(int x, int y, int z) {//返回指定的a坐标对应的cell能量值
return energys[x][y][z];
}
static final int HOLE_MAX_SIZE = 1000 * 1000;
public void digHole(int[] srcPos, int[] targetPos, int holeSize, int fresh) {
digHole(srcPos[0], srcPos[1], srcPos[2], targetPos[0], targetPos[1], targetPos[2], holeSize, fresh);
}
public void digHole(int sX, int sY, int sZ, int[] targetPos, int holeSize, int fresh) {
digHole(sX, sY, sZ, targetPos[0], targetPos[1], targetPos[2], holeSize, fresh);
}
public static final int HOLE_ARR_SIZE = 5; //洞由几个参数构成
//TODO: =================以下这些方法太复杂删除除或重新整理=================
public void digHole(int sX, int sY, int sZ, int tX, int tY, int tZ, int size, int fresh) {//在t细胞上挖洞将洞的方向链接到源s如果洞已存在扩大洞, 新洞大小为1洞最大不超过100
if (!hasGene(tX, tY, tZ))
return;
if (!Env.insideBrain(sX, sY, sZ))
return;
if (!Env.insideBrain(tX, tY, tZ))
return;
if (getEng(tX, tY, tZ) < 1) //要调整
addEng(tX, tY, tZ, 1); //要调整
int[] cellHoles = holes[tX][tY][tZ];
if (cellHoles == null) { //洞不存在新建一个 洞参数是一个一维数组分别为源坐标X,Y,Z, 洞的大小洞的新鲜度
holes[tX][tY][tZ] = new int[]{sX, sY, sZ, size, fresh}; //
return;
} else {
int emptyPos = -1; //找指定源坐标已存在的洞如果不存在如发现空洞也可以占用
for (int i = 0; i < cellHoles.length / HOLE_ARR_SIZE; i++) {
int n = i * HOLE_ARR_SIZE;
if (cellHoles[n] == sX && cellHoles[n + 1] == sY && cellHoles[n + 2] == sZ) {//找到已有的洞了
if (cellHoles[n + 3] < 1000) //要改成由基因调整
cellHoles[n + 3] += size;
if (cellHoles[n + 4] < 1000) //要改成由基因调整
cellHoles[n + 4] += fresh;
return;
}
if (emptyPos == -1 && cellHoles[n + 3] <= 1)//如发现空洞也可以先记下它的位置
emptyPos = n;
}
if (emptyPos > -1) { //找到一个空洞
cellHoles[emptyPos] = sX;
cellHoles[emptyPos + 1] = sY;
cellHoles[emptyPos + 2] = sZ;
if (cellHoles[emptyPos + 3] < 1000) //要改成由基因调整
cellHoles[emptyPos + 3] += size;
if (cellHoles[emptyPos + 4] < 1000) //要改成由基因调整
cellHoles[emptyPos + 4] += fresh;
return;
}
int length = cellHoles.length; //没找到已有的洞也没找到空洞新建一个并追加到原洞数组未尾
int[] newHoles = new int[length + HOLE_ARR_SIZE];
System.arraycopy(cellHoles, 0, newHoles, 0, length);
newHoles[length] = sX;
newHoles[length + 1] = sY;
newHoles[length + 2] = sZ;
newHoles[length + 3] = size; //要改成由基因调整
newHoles[length + 4] = fresh; //要改成由基因调整
holes[tX][tY][tZ] = newHoles;
return;
}
}
public void holeSendEngery(int[] pos, float e) {//在当前细胞所有洞上反向发送能量光子)le是向左边的细胞发 re是向右边的细胞发
holeSendEngery(pos[0], pos[1], pos[2], e);
}
public void holeSendEngery(int x, int y, int z, float e) {//在当前细胞所有洞上反向发送能量光子)le是向左边的细胞发 re是向右边的细胞发
int[] cellHoles = holes[x][y][z]; //cellHoles是单个细胞的所有洞(触突)4个一组前三个是洞的坐标后一个是洞的大小
if (cellHoles == null) //如洞不存在不发送能量
return;
for (int i = 0; i < cellHoles.length / HOLE_ARR_SIZE; i++) {
int n = i * HOLE_ARR_SIZE;
float size = cellHoles[n + 3];
if (size > 1) {
addEng(cellHoles[n], cellHoles[n + 1], cellHoles[n + 2], e + cellHoles[n + 3] + cellHoles[n + 4]); //向源细胞反向发送常量大小的能量
}
}
}
public void holesReduce() {//所有hole大小都会慢慢减小模拟触突连接随时间消失即细胞的遗忘机制这保证了系统不会被信息撑爆
for (int x = 0; x < Env.BRAIN_SIZE - 1; x++)
for (int y = 0; y < Env.BRAIN_SIZE - 1; y++)
for (int z = 0; z < Env.BRAIN_SIZE - 1; z++) {
int[] cellHoles = holes[x][y][z];
if (cellHoles != null)
for (int i = 0; i < cellHoles.length / HOLE_ARR_SIZE; i++) {
int n = i * HOLE_ARR_SIZE;
int size = cellHoles[n + 3];
if (size > 0)
cellHoles[n + 3] = (int) (size * 0.9);//要改成由基因调整
int fresh = cellHoles[n + 4];
if (fresh > 0)
cellHoles[n + 4] -= Consts.HOLE_REDUCE;//要改成由基因调整
}
}
}
}

View File

@ -18,6 +18,18 @@ import com.gitee.drinkjava2.frog.brain.Genes;
/**
* Application's main method start the program
* Application是程序入口
*
* 关于本项目代码格式化工具的约定:
* 1.不使用tab而是使用空白符
* 2.源码采用UTF-8编码
* 3.源码换行设定为UNIX风格单个LF字符结尾 git里设定git config --global core.autocrlf input即提交时把CRLF改成单个LF字符签出时不改
* 4.所有注释内容不允许被格式化
* Never join lines 设为true不允许自动合并注释行
* Enable Javadoc comment formatting 设为false
* Enable block comment formatting 设为false
* Enable line comment formatting 设为false
* 5.其他人提交时只能修改自已修改的部分不要随便使用代码格式化工具如果要使用代码格式化工具也必须参照以上内容设置成不能变动未修改的其它行
*
* @author Yong Zhu
* @since 1.0
@ -70,7 +82,7 @@ public class Application {
button.setBounds(buttonXpos, Env.ENV_HEIGHT + 8, buttonWidth, buttonHeight);
ActionListener al = new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {//显示或隐藏脑图
public void actionPerformed(ActionEvent arg0) {// 显示或隐藏脑图
Env.SHOW_FIRST_ANIMAL_BRAIN = !Env.SHOW_FIRST_ANIMAL_BRAIN;
checkIfShowBrainPicture(button);
}
@ -112,7 +124,7 @@ public class Application {
label.setBounds(buttonXpos - 90, stopButton.getY() + 23, 100, buttonHeight);
mainFrame.add(label);
//是否把egg文件存盘
// 是否把egg文件存盘
JCheckBox saveFileCheckBox = new JCheckBox("Save egg file");
saveFileCheckBox.setBounds(buttonXpos, Env.ENV_HEIGHT + 80, 120, 22);
ActionListener saveAction = new ActionListener() {
@ -126,7 +138,7 @@ public class Application {
saveFileCheckBox.addActionListener(saveAction);
mainFrame.add(saveFileCheckBox);
//基因维数显示控制
// 基因维数显示控制
for (int i = 0; i < Genes.GENE_NUMBERS; i++) {
JRadioButton geneRadio = new JRadioButton();
geneRadio.setBounds(buttonXpos + 300 + i * 16, Env.ENV_HEIGHT + 8, 20, 22);
@ -145,7 +157,7 @@ public class Application {
mainFrame.add(geneRadio);
}
//是否显示分裂过程
// 是否显示分裂过程
JCheckBox showSplitDetailCheckBox = new JCheckBox("Show split detail");
showSplitDetailCheckBox.setBounds(buttonXpos + 300, Env.ENV_HEIGHT + 40, 120, 22);
ActionListener detailAction = new ActionListener() {

View File

@ -5,8 +5,6 @@ import java.awt.Graphics;
import java.awt.Image;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.swing.JPanel;
@ -15,7 +13,7 @@ import com.gitee.drinkjava2.frog.egg.FrogEggTool;
import com.gitee.drinkjava2.frog.objects.EnvObject;
import com.gitee.drinkjava2.frog.objects.Food;
import com.gitee.drinkjava2.frog.objects.Material;
import com.gitee.drinkjava2.frog.objects.OneDotEye;
import com.gitee.drinkjava2.frog.objects.OneInputJudge;
import com.gitee.drinkjava2.frog.util.Logger;
import com.gitee.drinkjava2.frog.util.RandomUtils;
@ -32,11 +30,11 @@ public class Env extends JPanel {
/** Speed of test */
public static int SHOW_SPEED = 1000; // 测试速度-1000~1000,可调, 数值越小速度越慢
public static final int FROG_EGG_QTY = 300; // 每轮下n个青蛙蛋可调只有最优秀的前n个青蛙们才允许下蛋
public static final int FROG_EGG_QTY = 1000; // 每轮下n个青蛙蛋可调只有最优秀的前n个青蛙们才允许下蛋
public static final int FROG_PER_EGG = 4; // 每个青蛙蛋可以孵出几个青蛙
public static final int FROG_PER_EGG = 6; // 每个青蛙蛋可以孵出几个青蛙
public static final int SCREEN = 4; // 分几屏测完
public static final int SCREEN = 3; // 分几屏测完
/** Delete eggs at beginning of each run */
public static final boolean DELETE_FROG_EGGS = true;// 每次运行是否先删除以前保存的青蛙蛋文件如果为false将加载旧蛋文件继续运行
@ -46,7 +44,7 @@ public class Env extends JPanel {
public static final boolean BORN_AT_RANDOM_PLACE = true;// 孵出青蛙落在地图上随机位置而不是在蛋所在地
/** Frog's brain size */ // 脑细胞位于脑范围内是个三维结构在animal中用三维数组来表示
public static final int BRAIN_SIZE =4; //脑立方边长大小必须是2的幂数如4,8,16...原因参见8叉树算法
public static final int BRAIN_SIZE = 4; //脑立方边长大小必须是2的幂数如4,8,16...原因参见8叉树算法
/** SHOW first animal's brain structure */
public static boolean SHOW_FIRST_ANIMAL_BRAIN = true; // 是否显示脑图在Env区的右侧
@ -64,7 +62,7 @@ public class Env extends JPanel {
public static final int FROG_BRAIN_DISP_WIDTH = 400; // Frog的脑图在屏幕上的显示大小,可调
/** Steps of one test round */
public static final int STEPS_PER_ROUND = 800;// 每轮测试步数,可调
public static final int STEPS_PER_ROUND = 200;// 每轮测试步数,可调
public static int step;// 当前测试步数
public static final int FOOD_QTY = 3000; // 食物数量, 可调
@ -80,11 +78,11 @@ public class Env extends JPanel {
public static int[][] bricks = new int[ENV_WIDTH][ENV_HEIGHT];// 组成环境的材料见Material.java
public static List<Frog> frogs = new ArrayList<>(); // 这里存放所有待测的青蛙可能分几次测完由FROG_PER_SCREEN大小来决定
public static ArrayList<Frog> frogs = new ArrayList<>(); // 这里存放所有待测的青蛙可能分几次测完由FROG_PER_SCREEN大小来决定
public static List<Egg> frog_eggs = new ArrayList<>(); // 这里存放新建或从磁盘载入上轮下的蛋每个蛋可能生成几个青蛙
public static ArrayList<Egg> frog_eggs = new ArrayList<>(); // 这里存放新建或从磁盘载入上轮下的蛋每个蛋可能生成几个青蛙
public static EnvObject[] things = new EnvObject[]{new OneDotEye()};// 所有外界物体如食物字母测试工具都放在这个things里面
public static EnvObject[] things = new EnvObject[] { new OneInputJudge() };// 所有外界物体如食物测试工具都放在这个things里面
public static boolean show_split_detail = false; //是否显示脑分裂的细节过程即从一个细胞开始分裂分裂而不是只显示分裂的最终结果
@ -200,8 +198,7 @@ public class Env extends JPanel {
for (Frog f : frogs)
if (f.ateFood > maxFound)
maxFound = f.ateFood;
return new StringBuilder("吃食率:").append(format100.format(Food.food_ated * 1.00 / FOOD_QTY)).append(", 平均: ").append(Food.food_ated * 1.0f / FROG_PER_SCREEN).append(",最多:").append(maxFound)
.toString();
return new StringBuilder("吃食率:").append(format100.format(Food.food_ated * 1.00 / FOOD_QTY)).append(", 平均: ").append(Food.food_ated * 1.0f / FROG_PER_SCREEN).append(",最多:").append(maxFound).toString();
}
public static void checkIfPause(int step) {
@ -221,32 +218,39 @@ public class Env extends JPanel {
}
}
public static Animal getShowAnimal() {
public static Animal getShowAnimal() { //取当前屏第一个青蛙
return frogs.get(current_screen * FROG_PER_SCREEN);
}
public static int round = 1;
public void run() {
FrogEggTool.loadFrogEggs(); // 从磁盘加载蛙egg新建一批egg
FrogEggTool.loadFrogEggs(); // 首次运行时从磁盘加载蛙egg如加载失败就新建一批egg
Image buffImg = createImage(this.getWidth(), this.getHeight());
Graphics g = buffImg.getGraphics();
long time0;// 计时用
int round = 1;
round = 1;
do {
rebuildFrogs(); // 根据蛙蛋重新孵化出蛙注意基因变异有可能在孵化过程中发生
for (current_screen = 0; current_screen < SCREEN; current_screen++) {// 分屏测试每屏FROG_PER_SCREEN个蛙
time0 = System.currentTimeMillis();
g.setColor(Color.white);
g.fillRect(0, 0, this.getWidth(), this.getHeight()); // 先清空虚拟环境
g.setColor(Color.BLACK);
for (EnvObject thing : things) // 创建食物陷阱等物体
thing.build();
thing.build(g);
boolean allDead = false;
for (int j = 0; j < FROG_PER_SCREEN; j++) {
Frog f = frogs.get(current_screen * FROG_PER_SCREEN + j);
f.initAnimal(); // 初始化器官延迟到这一步是因为脑细胞太占内存而且当前屏测完后会清空
}
for (step = 0; step < STEPS_PER_ROUND; step++) {
for (EnvObject thing : things)// 调用食物陷阱等物体的动作
thing.active(current_screen, step);
if (allDead)
break; // 青蛙全死光了就直接跳到下一轮,以节省时间
for (EnvObject thing : things)// 调用食物陷阱等物体的动作
thing.active(current_screen, step, g);
allDead = true;
for (int j = 0; j < FROG_PER_SCREEN; j++) {
Frog f = frogs.get(current_screen * FROG_PER_SCREEN + j);
@ -255,28 +259,18 @@ public class Env extends JPanel {
}
if (SHOW_SPEED == 1) // 如果speed为1人为加入延迟
sleep((100));
sleep(400);
else if (step % SHOW_SPEED != 0)// 用是否跳帧画图的方式来控制速度
continue;
// 开始画虚拟环境和青蛙
g.setColor(Color.white);
g.fillRect(0, 0, this.getWidth(), this.getHeight()); // 先清空虚拟环境
g.setColor(Color.BLACK);
drawWorld(g);// 画整个虚拟环境
// 开始画things和青蛙
drawWorld(g);// 画整个虚拟环境中的material
for (int j = 0; j < FROG_PER_SCREEN; j++) { // 显示青蛙
Frog f = frogs.get(current_screen * FROG_PER_SCREEN + j);
f.show(g);
f.showInEnv(g);
}
if (SHOW_FIRST_ANIMAL_BRAIN) {// 在showAnimal上画一个红圈
Animal showAnimal = getShowAnimal();
if (showAnimal != null) {
g.setColor(Color.red);
g.drawArc(showAnimal.xPos - 15, showAnimal.yPos - 15, 30, 30, 0, 360);
g.setColor(Color.BLACK);
}
}
if (DRAW_BRAIN_AFTER_STEPS > 0 && step % DRAW_BRAIN_AFTER_STEPS == 0) //显示脑图是耗时操作这个开关可以跳过一些脑图显示
Application.brainPic.drawBrainPicture(step);
if (SHOW_SPEED == 1 && SHOW_FIRST_ANIMAL_BRAIN) //如果速度为1强制每步都显示脑图
@ -287,17 +281,14 @@ public class Env extends JPanel {
if (SHOW_FIRST_ANIMAL_BRAIN) //一轮结束后再强制再显示脑图一次
Application.brainPic.drawBrainPicture(step);
checkIfPause(step);
for (int j = 0; j < FROG_PER_SCREEN; j++) {
Frog f = frogs.get(current_screen * FROG_PER_SCREEN + j);
}
StringBuilder sb = new StringBuilder("Round: ");
sb.append(round).append(", screen:").append(current_screen).append(", speed:").append(Env.SHOW_SPEED).append(", ").append(", 用时: ").append(System.currentTimeMillis() - time0)
.append("ms, ");
sb.append(round).append(", screen:").append(current_screen).append(", speed:").append(Env.SHOW_SPEED).append(", ").append(", 用时: ").append(System.currentTimeMillis() - time0).append("ms, ");
sb.append(foodAtedCount());
Application.mainFrame.setTitle(sb.toString());
for (EnvObject thing : things)// 去除食物陷阱等物体
thing.destory();
thing.destory(g);
}
round++;
FrogEggTool.layEggs(); //能量高的青蛙才有权下蛋

View File

@ -35,445 +35,462 @@ import com.gitee.drinkjava2.frog.util.Tree8Util;
*/
@SuppressWarnings("all")
public class BrainPicture extends JPanel {
private static final long serialVersionUID = 1L;
private static final long serialVersionUID = 1L;
private static final float D90 = (float) (Math.PI / 2);
private static final float D90 = (float) (Math.PI / 2);
Color picColor = RED;
int brainDispWidth; // screen display piexls width
float scale; // brain scale
int xOffset = 0; // brain display x offset compare to screen
int yOffset = 0; // brain display y offset compare to screen
float xAngle = D90 * .8f; // brain rotate on x axis
float yAngle = D90 / 4; // brain rotate on y axis
float zAngle = 0;// brain rotate on z axis
int xMask = -1;// x Mask
int yMask = -1;// y Mask
BufferedImage buffImg;
Graphics g;
String note;
public KeyAdapter keyAdapter;
Color picColor = RED;
int brainDispWidth; // screen display piexls width
float scale; // brain scale
int xOffset = 0; // brain display x offset compare to screen
int yOffset = 0; // brain display y offset compare to screen
float xAngle = D90 * .8f; // brain rotate on x axis
float yAngle = D90 / 4; // brain rotate on y axis
float zAngle = 0;// brain rotate on z axis
int xMask = -1;// x Mask
int yMask = -1;// y Mask
BufferedImage buffImg;
Graphics g;
String note;
public KeyAdapter keyAdapter;
public BrainPicture(int x, int y, float brainWidth, int brainDispWidth) {
super();
this.setLayout(null);// 空布局
this.brainDispWidth = brainDispWidth;
scale = 0.5f * brainDispWidth / brainWidth;
this.setBounds(x, y, brainDispWidth + 1, brainDispWidth + 1);
buffImg = new BufferedImage(Env.FROG_BRAIN_DISP_WIDTH, Env.FROG_BRAIN_DISP_WIDTH, BufferedImage.TYPE_INT_RGB);
g = buffImg.getGraphics();
MouseAction act = new MouseAction(this);
this.addMouseListener(act); // 添加鼠标动作监听
this.addMouseWheelListener(act);// 添加鼠标滚轮动作监听
this.addMouseMotionListener(act);// 添加鼠标移动动作监听
public BrainPicture(int x, int y, float brainWidth, int brainDispWidth) {
super();
this.setLayout(null);// 空布局
this.brainDispWidth = brainDispWidth;
scale = 0.5f * brainDispWidth / brainWidth;
this.setBounds(x, y, brainDispWidth + 1, brainDispWidth + 1);
buffImg = new BufferedImage(Env.FROG_BRAIN_DISP_WIDTH, Env.FROG_BRAIN_DISP_WIDTH, BufferedImage.TYPE_INT_RGB);
g = buffImg.getGraphics();
MouseAction act = new MouseAction(this);
this.addMouseListener(act); // 添加鼠标动作监听
this.addMouseWheelListener(act);// 添加鼠标滚轮动作监听
this.addMouseMotionListener(act);// 添加鼠标移动动作监听
keyAdapter = new KeyAdapter() {// 处理t,f,l,rx键盘命令
@Override
public void keyPressed(KeyEvent e) {
switch (e.getKeyCode()){
case KeyEvent.VK_UP:// Y切面向上
yMask++;
if (yMask > Env.BRAIN_SIZE)
yMask = Env.BRAIN_SIZE;
break;
case KeyEvent.VK_DOWN:// Y切面向下
yMask--;
if (yMask < 0)
yMask = 0;
break;
case KeyEvent.VK_LEFT:// x切面向左
xMask--;
if (xMask < 0)
xMask = 0;
break;
case KeyEvent.VK_RIGHT:// x切面向右
xMask++;
if (xMask > Env.BRAIN_SIZE)
xMask = Env.BRAIN_SIZE;
break;
case ' ':// 暂停及继续
Application.pauseAction.actionPerformed(null);
break;
case 'T':// 顶视
xAngle = 0;
yAngle = 0;
zAngle = 0;
break;
case 'F':// 前视
xAngle = D90;
yAngle = 0;
zAngle = 0;
break;
case 'L':// 左视
xAngle = D90;
yAngle = D90;
zAngle = 0;
break;
case 'R':// 右视
xAngle = D90;
yAngle = -D90;
zAngle = 0;
break;
case 'X':// 斜视
xAngle = D90 * .8f;
yAngle = D90 / 4;
zAngle = 0;
break;
default:
}
}
};
addKeyListener(keyAdapter);
this.setFocusable(true);
}
keyAdapter = new KeyAdapter() {// 处理t,f,l,rx键盘命令
@Override
public void keyPressed(KeyEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_UP:// Y切面向上
yMask++;
if (yMask > Env.BRAIN_SIZE)
yMask = Env.BRAIN_SIZE;
break;
case KeyEvent.VK_DOWN:// Y切面向下
yMask--;
if (yMask < 0)
yMask = 0;
break;
case KeyEvent.VK_LEFT:// x切面向左
xMask--;
if (xMask < 0)
xMask = 0;
break;
case KeyEvent.VK_RIGHT:// x切面向右
xMask++;
if (xMask > Env.BRAIN_SIZE)
xMask = Env.BRAIN_SIZE;
break;
case ' ':// 暂停及继续
Application.pauseAction.actionPerformed(null);
break;
case 'T':// 顶视
xAngle = 0;
yAngle = 0;
zAngle = 0;
break;
case 'F':// 前视
xAngle = D90;
yAngle = 0;
zAngle = 0;
break;
case 'L':// 左视
xAngle = D90;
yAngle = D90;
zAngle = 0;
break;
case 'R':// 右视
xAngle = D90;
yAngle = -D90;
zAngle = 0;
break;
case 'X':// 斜视
xAngle = D90 * .8f;
yAngle = D90 / 4;
zAngle = 0;
break;
default:
}
}
};
addKeyListener(keyAdapter);
this.setFocusable(true);
}
public void drawCuboid(float x, float y, float z, float xe, float ye, float ze) {// 在脑图上画一个长立方体框架视角是TopView
drawLine(x, y, z, x + xe, y, z);// 画立方体的下面边
drawLine(x + xe, y, z, x + xe, y + ye, z);
drawLine(x + xe, y + ye, z, x, y + ye, z);
drawLine(x, y + ye, z, x, y, z);
public void drawCuboid(float x, float y, float z, float xe, float ye, float ze) {// 在脑图上画一个长立方体框架视角是TopView
drawLine(x, y, z, x + xe, y, z);// 画立方体的下面边
drawLine(x + xe, y, z, x + xe, y + ye, z);
drawLine(x + xe, y + ye, z, x, y + ye, z);
drawLine(x, y + ye, z, x, y, z);
drawLine(x, y, z, x, y, z + ze);// 画立方体的中间边
drawLine(x + xe, y, z, x + xe, y, z + ze);
drawLine(x + xe, y + ye, z, x + xe, y + ye, z + ze);
drawLine(x, y + ye, z, x, y + ye, z + ze);
drawLine(x, y, z, x, y, z + ze);// 画立方体的中间边
drawLine(x + xe, y, z, x + xe, y, z + ze);
drawLine(x + xe, y + ye, z, x + xe, y + ye, z + ze);
drawLine(x, y + ye, z, x, y + ye, z + ze);
drawLine(x, y, z + ze, x + xe, y, z + ze);// 画立方体的上面边
drawLine(x + xe, y, z + ze, x + xe, y + ye, z + ze);
drawLine(x + xe, y + ye, z + ze, x, y + ye, z + ze);
drawLine(x, y + ye, z + ze, x, y, z + ze);
}
drawLine(x, y, z + ze, x + xe, y, z + ze);// 画立方体的上面边
drawLine(x + xe, y, z + ze, x + xe, y + ye, z + ze);
drawLine(x + xe, y + ye, z + ze, x, y + ye, z + ze);
drawLine(x, y + ye, z + ze, x, y, z + ze);
}
public void drawCentLine(float px1, float py1, float pz1, float px2, float py2, float pz2) {//从细胞中点之间画一条线
drawLine(px1 + 0.5f, py1 + 0.5f, pz1 + 0.5f, px2 + 0.5f, py2 + 0.5f, pz2 + 0.5f);
}
public void drawCentLine(float px1, float py1, float pz1, float px2, float py2, float pz2) {// 从细胞中点之间画一条线
drawLine(px1 + 0.5f, py1 + 0.5f, pz1 + 0.5f, px2 + 0.5f, py2 + 0.5f, pz2 + 0.5f);
}
/*-
画线固定以top视角的角度所以只需要从x1,y1画一条到x2,y2的直线
x 轴旋转 θ
x, y.cosθ-zsinθ, y.sinθ+z.cosθ
y 轴旋转 θ
z.sinθ+x.cosθ, y, z.cosθ-x.sinθ
z 轴旋转 θ
x.cosθ-y.sinθ, x.sinθ+y.consθ, z
-*/
public void drawLine(float px1, float py1, float pz1, float px2, float py2, float pz2) {
double x1 = px1 - Env.BRAIN_SIZE / 2;
double y1 = -py1 + Env.BRAIN_SIZE / 2;// 屏幕的y坐标是反的显示时要正过来
double z1 = pz1 - Env.BRAIN_SIZE / 2;
double x2 = px2 - Env.BRAIN_SIZE / 2;
double y2 = -py2 + Env.BRAIN_SIZE / 2;// 屏幕的y坐标是反的显示时要正过来
double z2 = pz2 - Env.BRAIN_SIZE / 2;
x1 = x1 * scale;
y1 = y1 * scale;
z1 = z1 * scale;
x2 = x2 * scale;
y2 = y2 * scale;
z2 = z2 * scale;
double x, y, z;
y = y1 * cos(xAngle) - z1 * sin(xAngle);// 绕x轴转
z = y1 * sin(xAngle) + z1 * cos(xAngle);
y1 = y;
z1 = z;
/*-
画线固定以top视角的角度所以只需要从x1,y1画一条到x2,y2的直线
x 轴旋转 θ
x, y.cosθ-zsinθ, y.sinθ+z.cosθ
y 轴旋转 θ
z.sinθ+x.cosθ, y, z.cosθ-x.sinθ
z 轴旋转 θ
x.cosθ-y.sinθ, x.sinθ+y.consθ, z
-*/
public void drawLine(float px1, float py1, float pz1, float px2, float py2, float pz2) {
double x1 = px1 - Env.BRAIN_SIZE / 2;
double y1 = -py1 + Env.BRAIN_SIZE / 2;// 屏幕的y坐标是反的显示时要正过来
double z1 = pz1 - Env.BRAIN_SIZE / 2;
double x2 = px2 - Env.BRAIN_SIZE / 2;
double y2 = -py2 + Env.BRAIN_SIZE / 2;// 屏幕的y坐标是反的显示时要正过来
double z2 = pz2 - Env.BRAIN_SIZE / 2;
x1 = x1 * scale;
y1 = y1 * scale;
z1 = z1 * scale;
x2 = x2 * scale;
y2 = y2 * scale;
z2 = z2 * scale;
double x, y, z;
y = y1 * cos(xAngle) - z1 * sin(xAngle);// 绕x轴转
z = y1 * sin(xAngle) + z1 * cos(xAngle);
y1 = y;
z1 = z;
x = z1 * sin(yAngle) + x1 * cos(yAngle);// 绕y轴转
// z = z1 * cos(yAngle) - x1 * sin(yAngle);
x1 = x;
// z1 = z;
x = z1 * sin(yAngle) + x1 * cos(yAngle);// 绕y轴转
// z = z1 * cos(yAngle) - x1 * sin(yAngle);
x1 = x;
// z1 = z;
x = x1 * cos(zAngle) - y1 * sin(zAngle);// 绕z轴转
y = x1 * sin(zAngle) + y1 * cos(zAngle);
x1 = x;
y1 = y;
x = x1 * cos(zAngle) - y1 * sin(zAngle);// 绕z轴转
y = x1 * sin(zAngle) + y1 * cos(zAngle);
x1 = x;
y1 = y;
y = y2 * cos(xAngle) - z2 * sin(xAngle);// 绕x轴转
z = y2 * sin(xAngle) + z2 * cos(xAngle);
y2 = y;
z2 = z;
y = y2 * cos(xAngle) - z2 * sin(xAngle);// 绕x轴转
z = y2 * sin(xAngle) + z2 * cos(xAngle);
y2 = y;
z2 = z;
x = z2 * sin(yAngle) + x2 * cos(yAngle);// 绕y轴转
// z = z2 * cos(yAngle) - x2 * sin(yAngle);
x2 = x;
// z2 = z;
x = z2 * sin(yAngle) + x2 * cos(yAngle);// 绕y轴转
// z = z2 * cos(yAngle) - x2 * sin(yAngle);
x2 = x;
// z2 = z;
x = x2 * cos(zAngle) - y2 * sin(zAngle);// 绕z轴转
y = x2 * sin(zAngle) + y2 * cos(zAngle);
x2 = x;
y2 = y;
x = x2 * cos(zAngle) - y2 * sin(zAngle);// 绕z轴转
y = x2 * sin(zAngle) + y2 * cos(zAngle);
x2 = x;
y2 = y;
g.setColor(picColor);
g.drawLine((int) round(x1) + Env.FROG_BRAIN_DISP_WIDTH / 2 + xOffset, (int) round(y1) + Env.FROG_BRAIN_DISP_WIDTH / 2 + yOffset, (int) round(x2) + Env.FROG_BRAIN_DISP_WIDTH / 2 + xOffset,
(int) round(y2) + Env.FROG_BRAIN_DISP_WIDTH / 2 + yOffset);
}
g.setColor(picColor);
g.drawLine((int) round(x1) + Env.FROG_BRAIN_DISP_WIDTH / 2 + xOffset,
(int) round(y1) + Env.FROG_BRAIN_DISP_WIDTH / 2 + yOffset,
(int) round(x2) + Env.FROG_BRAIN_DISP_WIDTH / 2 + xOffset,
(int) round(y2) + Env.FROG_BRAIN_DISP_WIDTH / 2 + yOffset);
}
/** 画点固定以top视角的角度所以只需要在x1,y1位置画一个点 */
public void drawPoint(float px1, float py1, float pz1, float r) {
double x1 = px1 - Env.BRAIN_SIZE / 2;
double y1 = -py1 + Env.BRAIN_SIZE / 2;// 屏幕的y坐标是反的显示时要正过来
double z1 = pz1 - Env.BRAIN_SIZE / 2;
x1 = x1 * scale;
y1 = y1 * scale;
z1 = z1 * scale;
double x, y, z;
y = y1 * cos(xAngle) - z1 * sin(xAngle);// 绕x轴转
z = y1 * sin(xAngle) + z1 * cos(xAngle);
y1 = y;
z1 = z;
public void drawPointCent(float px1, float py1, float pz1, float r) {
drawPoint(px1+0.5f, py1+0.5f, pz1+0.5f, r);
}
/** 画点固定以top视角的角度所以只需要在x1,y1位置画一个点 */
public void drawPoint(float px1, float py1, float pz1, float r) {
double x1 = px1 - Env.BRAIN_SIZE / 2;
double y1 = -py1 + Env.BRAIN_SIZE / 2;// 屏幕的y坐标是反的显示时要正过来
double z1 = pz1 - Env.BRAIN_SIZE / 2;
x1 = x1 * scale;
y1 = y1 * scale;
z1 = z1 * scale;
double x, y, z;
y = y1 * cos(xAngle) - z1 * sin(xAngle);// 绕x轴转
z = y1 * sin(xAngle) + z1 * cos(xAngle);
y1 = y;
z1 = z;
x = z1 * sin(yAngle) + x1 * cos(yAngle);// 绕y轴转
// z = z1 * cos(yAngle) - x1 * sin(yAngle);
x1 = x;
// z1 = z;
x = z1 * sin(yAngle) + x1 * cos(yAngle);// 绕y轴转
// z = z1 * cos(yAngle) - x1 * sin(yAngle);
x1 = x;
// z1 = z;
x = x1 * cos(zAngle) - y1 * sin(zAngle);// 绕z轴转
y = x1 * sin(zAngle) + y1 * cos(zAngle);
x1 = x;
y1 = y;
x = x1 * cos(zAngle) - y1 * sin(zAngle);// 绕z轴转
y = x1 * sin(zAngle) + y1 * cos(zAngle);
x1 = x;
y1 = y;
g.setColor(picColor);
g.fillOval(round((float) x1 + Env.FROG_BRAIN_DISP_WIDTH / 2 + xOffset - r * scale * .5f), round((float) y1 + Env.FROG_BRAIN_DISP_WIDTH / 2 + yOffset - r * scale * .5f), round(r * scale),
round(r * scale));
}
g.setColor(picColor);
g.fillOval(round((float) x1 + Env.FROG_BRAIN_DISP_WIDTH / 2 + xOffset - r * scale * .5f),
round((float) y1 + Env.FROG_BRAIN_DISP_WIDTH / 2 + yOffset - r * scale * .5f), round(r * scale),
round(r * scale));
}
/** 画一个圆 */
public void drawCircle(float px1, float py1, float pz1, float r) {//这个方法实际和上面的一样的只是改成了drawOval
double x1 = px1 - Env.BRAIN_SIZE / 2;
double y1 = -py1 + Env.BRAIN_SIZE / 2;// 屏幕的y坐标是反的显示时要正过来
double z1 = pz1 - Env.BRAIN_SIZE / 2;
x1 = x1 * scale;
y1 = y1 * scale;
z1 = z1 * scale;
double x, y, z;
y = y1 * cos(xAngle) - z1 * sin(xAngle);// 绕x轴转
z = y1 * sin(xAngle) + z1 * cos(xAngle);
y1 = y;
z1 = z;
/** 画一个圆 */
public void drawCircle(float px1, float py1, float pz1, float r) {// 这个方法实际和上面的一样的只是改成了drawOval
double x1 = px1 - Env.BRAIN_SIZE / 2;
double y1 = -py1 + Env.BRAIN_SIZE / 2;// 屏幕的y坐标是反的显示时要正过来
double z1 = pz1 - Env.BRAIN_SIZE / 2;
x1 = x1 * scale;
y1 = y1 * scale;
z1 = z1 * scale;
double x, y, z;
y = y1 * cos(xAngle) - z1 * sin(xAngle);// 绕x轴转
z = y1 * sin(xAngle) + z1 * cos(xAngle);
y1 = y;
z1 = z;
x = z1 * sin(yAngle) + x1 * cos(yAngle);// 绕y轴转
// z = z1 * cos(yAngle) - x1 * sin(yAngle);
x1 = x;
// z1 = z;
x = z1 * sin(yAngle) + x1 * cos(yAngle);// 绕y轴转
// z = z1 * cos(yAngle) - x1 * sin(yAngle);
x1 = x;
// z1 = z;
x = x1 * cos(zAngle) - y1 * sin(zAngle);// 绕z轴转
y = x1 * sin(zAngle) + y1 * cos(zAngle);
x1 = x;
y1 = y;
x = x1 * cos(zAngle) - y1 * sin(zAngle);// 绕z轴转
y = x1 * sin(zAngle) + y1 * cos(zAngle);
x1 = x;
y1 = y;
g.setColor(picColor);
g.drawOval(round((float) x1 + Env.FROG_BRAIN_DISP_WIDTH / 2 + xOffset - r * scale * .5f), round((float) y1 + Env.FROG_BRAIN_DISP_WIDTH / 2 + yOffset - r * scale * .5f), round(r * scale),
round(r * scale));
}
g.setColor(picColor);
g.drawOval(round((float) x1 + Env.FROG_BRAIN_DISP_WIDTH / 2 + xOffset - r * scale * .5f),
round((float) y1 + Env.FROG_BRAIN_DISP_WIDTH / 2 + yOffset - r * scale * .5f), round(r * scale),
round(r * scale));
}
public void drawText(float px1, float py1, float pz1, String text, float textSize) {
double x1 = px1 - Env.BRAIN_SIZE / 2;
double y1 = -py1 + Env.BRAIN_SIZE / 2;// 屏幕的y坐标是反的显示时要正过来
double z1 = pz1 - Env.BRAIN_SIZE / 2;
x1 = x1 * scale;
y1 = y1 * scale;
z1 = z1 * scale;
double x, y, z;
y = y1 * cos(xAngle) - z1 * sin(xAngle);// 绕x轴转
z = y1 * sin(xAngle) + z1 * cos(xAngle);
y1 = y;
z1 = z;
public void drawTextCenter(float px1, float py1, float pz1, String text, float textSize) {
drawText(px1 + 0.5f, py1 + 0.5f, pz1 + 0.5f, text, textSize);
}
x = z1 * sin(yAngle) + x1 * cos(yAngle);// 绕y轴转
// z = z1 * cos(yAngle) - x1 * sin(yAngle);
x1 = x;
// z1 = z;
public void drawText(float px1, float py1, float pz1, String text, float textSize) {
double x1 = px1 - Env.BRAIN_SIZE / 2;
double y1 = -py1 + Env.BRAIN_SIZE / 2;// 屏幕的y坐标是反的显示时要正过来
double z1 = pz1 - Env.BRAIN_SIZE / 2;
x1 = x1 * scale;
y1 = y1 * scale;
z1 = z1 * scale;
double x, y, z;
y = y1 * cos(xAngle) - z1 * sin(xAngle);// 绕x轴转
z = y1 * sin(xAngle) + z1 * cos(xAngle);
y1 = y;
z1 = z;
x = x1 * cos(zAngle) - y1 * sin(zAngle);// 绕z轴转
y = x1 * sin(zAngle) + y1 * cos(zAngle);
x1 = x;
y1 = y;
x = z1 * sin(yAngle) + x1 * cos(yAngle);// 绕y轴转
// z = z1 * cos(yAngle) - x1 * sin(yAngle);
x1 = x;
// z1 = z;
g.setColor(picColor);
g.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, (int) round(textSize * scale)));
g.drawString(text, (int) round(x1) + Env.FROG_BRAIN_DISP_WIDTH / 2 + xOffset, (int) round(y1) + Env.FROG_BRAIN_DISP_WIDTH / 2 + yOffset);
x = x1 * cos(zAngle) - y1 * sin(zAngle);// 绕z轴转
y = x1 * sin(zAngle) + y1 * cos(zAngle);
x1 = x;
y1 = y;
}
g.setColor(picColor);
g.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, (int) round(textSize * scale)));
g.drawString(text, (int) round(x1) + Env.FROG_BRAIN_DISP_WIDTH / 2 + xOffset,
(int) round(y1) + Env.FROG_BRAIN_DISP_WIDTH / 2 + yOffset);
public void drawBrainPicture(int step) {// 在这个方法里进行动物的三维脑结构的绘制,蛇是青蛙的子类所以也可以当参数传进来
if (!Env.SHOW_FIRST_ANIMAL_BRAIN)
return;
if (Env.show_split_detail)
drawSplitDetail();
else
drawBrainStructure(step);
}
}
public void drawSplitDetail() {// 在这个方法里绘制脑细胞分裂的显示步聚即从一个细胞开始分裂成最终脑结构的每一步
Animal a = Env.getShowAnimal(); // 第一个青蛙或蛇
public void drawBrainPicture(int step) {// 在这个方法里进行动物的三维脑结构的绘制,蛇是青蛙的子类所以也可以当参数传进来
if (!Env.SHOW_FIRST_ANIMAL_BRAIN)
return;
if (Env.show_split_detail)
drawSplitDetail();
else
drawBrainStructure(step);
}
for (int i = Env.BRAIN_SIZE; i >= 1; i /= 2) {
g.setColor(WHITE);// 先清空旧图, g是buffImg绘在内存中
g.fillRect(0, 0, brainDispWidth, brainDispWidth);
g.setColor(BLACK); // 画边框
g.drawRect(0, 0, brainDispWidth, brainDispWidth);
public void drawSplitDetail() {// 在这个方法里绘制脑细胞分裂的显示步聚即从一个细胞开始分裂成最终脑结构的每一步
Animal a = Env.getShowAnimal(); // 第一个青蛙或蛇
for (int geneIndex = 0; geneIndex < Genes.GENE_NUMBERS; geneIndex++) {
ArrayList<Integer> gene = a.genes.get(geneIndex);
Tree8Util.knockNodesByGene(gene);
for (int j = 0; j < Tree8Util.NODE_QTY; j++) {
if (Tree8Util.keep[j] > 0) {
int[] node = Tree8Util.TREE8[j];
int size = node[0];
if (size == i && Genes.display_gene[geneIndex]) {//如果允许显示的话, 显示当前层级的节点
setPicColor(ColorUtils.colorByCode(geneIndex));
drawPoint(node[1] + size / 2, node[2] + size / 2, node[3] + size / 2, size * (0.5f - geneIndex * 0.05f));
}
}
}
}
g.setColor(BLACK);
drawCuboid(0, 0, 0, Env.BRAIN_SIZE, Env.BRAIN_SIZE, Env.BRAIN_SIZE);// 把脑的框架画出来
this.getGraphics().drawImage(buffImg, 0, 0, this);// 利用缓存避免画面闪烁这里输出缓存图片
if (!Env.show_split_detail)
return;
try {
Thread.sleep(500);
} catch (InterruptedException e) {
}
}
}
for (int i = Env.BRAIN_SIZE; i >= 1; i /= 2) {
g.setColor(WHITE);// 先清空旧图, g是buffImg绘在内存中
g.fillRect(0, 0, brainDispWidth, brainDispWidth);
g.setColor(BLACK); // 画边框
g.drawRect(0, 0, brainDispWidth, brainDispWidth);
public void drawBrainStructure(int step) {// 在这个方法里进行动物的三维脑结构的绘制,蛇是青蛙的子类所以也可以当参数传进来
Animal a = Env.getShowAnimal(); // 显示第一个青蛙或蛇
if (a == null || !a.alive)
return;
g.setColor(WHITE);// 先清空旧图, g是buffImg绘在内存中
g.fillRect(0, 0, brainDispWidth, brainDispWidth);
g.setColor(BLACK); // 画边框
g.drawRect(0, 0, brainDispWidth, brainDispWidth);
for (int geneIndex = 0; geneIndex < Genes.GENE_NUMBERS; geneIndex++) {
ArrayList<Integer> gene = a.genes.get(geneIndex);
Tree8Util.knockNodesByGene(gene);
for (int j = 0; j < Tree8Util.NODE_QTY; j++) {
if (Tree8Util.keep[j] > 0) {
int[] node = Tree8Util.TREE8[j];
int size = node[0];
if (size == i && Genes.display_gene[geneIndex]) {// 如果允许显示的话, 显示当前层级的节点
setPicColor(ColorUtils.colorByCode(geneIndex));
drawPoint(node[1] + size / 2, node[2] + size / 2, node[3] + size / 2,
size * (0.5f - geneIndex * 0.05f));
}
}
}
}
g.setColor(BLACK);
drawCuboid(0, 0, 0, Env.BRAIN_SIZE, Env.BRAIN_SIZE, Env.BRAIN_SIZE);// 把脑的框架画出来
this.getGraphics().drawImage(buffImg, 0, 0, this);// 利用缓存避免画面闪烁这里输出缓存图片
if (!Env.show_split_detail)
return;
try {
Thread.sleep(500);
} catch (InterruptedException e) {
}
}
}
for (int z = 0; z < Env.BRAIN_SIZE; z++) {
for (int y = Env.BRAIN_SIZE - 1; y >= 0; y--) {
for (int x = Env.BRAIN_SIZE - 1; x >= 0; x--) {
long cell = a.cells[x][y][z];
// if (cell == 0) //只显示有效的细胞点
// continue;
public void drawBrainStructure(int step) {// 在这个方法里进行动物的三维脑结构的绘制,蛇是青蛙的子类所以也可以当参数传进来
Animal a = Env.getShowAnimal(); // 显示第一个青蛙或蛇
if (a == null || !a.alive)
return;
g.setColor(WHITE);// 先清空旧图, g是buffImg绘在内存中
g.fillRect(0, 0, brainDispWidth, brainDispWidth);
g.setColor(BLACK); // 画边框
g.drawRect(0, 0, brainDispWidth, brainDispWidth);
int[] holes = a.holes[x][y][z];
if (holes != null) {
setPicColor(Color.GRAY);
for (int i = 0; i < holes.length / Animal.HOLE_ARR_SIZE; i++) {//这里画出hole连线
int n = i * Animal.HOLE_ARR_SIZE;
drawCentLine(x, y, z, holes[n], holes[n + 1], holes[n + 2]);
}
}
for (int z = 0; z < Env.BRAIN_SIZE; z++) {// 画它所有的脑细胞位置和颜色
for (int y = Env.BRAIN_SIZE - 1; y >= 0; y--) {
for (int x = Env.BRAIN_SIZE - 1; x >= 0; x--) {
setPicColor(BLACK); // 画边框
drawPointCent(x, y, z, 0.03f); //画每个细胞小点
long cell = a.cells[x][y][z];
// if (cell == 0) //只显示有效的细胞点
// continue;
if (x >= xMask && y >= yMask && cell != 0)//画出细胞每个基因存在的细胞格子
for (int geneIndex = 0; geneIndex < Genes.GENE_NUMBERS; geneIndex++) {
if ((cell & (1 << geneIndex)) != 0 && Genes.display_gene[geneIndex]) {
setPicColor(ColorUtils.colorByCode(geneIndex)); //开始画出对应的细胞基因参数用不同颜色直径圆表示
//setPicColor(Color.RED);
//drawPoint(x + 0.5f, y + 0.5f, z + 0.5f, geneIndex == 0 ? 0.8f : 0.5f - geneIndex * 0.05f);
drawPoint(x + 0.5f, y + 0.5f, z + 0.5f, 0.6f);
}
}
float e = a.energys[x][y][z];
if (e > 1f || e < -1f) {
setPicColor(e > 0 ? Color.RED : Color.BLUE); //用红色小圆表示正能量蓝色表示负能量
drawPoint(x + 0.5f, y + 0.5f, z + 0.5f, 0.2f);
float size = (float) (0.5f + 0.4 * Math.log10(Math.abs(e)));//再用不同大小圆形表示不同能量值
drawCircle(x + 0.5f, y + 0.5f, z + 0.5f, size);
}
if (x >= xMask && y >= yMask && cell != 0)// 画出细胞每个基因存在的细胞格子
for (int geneIndex = 0; geneIndex < Genes.GENE_NUMBERS; geneIndex++) {
if ((cell & (1 << geneIndex)) != 0 && Genes.display_gene[geneIndex]) {
setPicColor(ColorUtils.colorByCode(geneIndex)); // 开始画出对应的细胞基因参数用不同颜色圆表示
drawPoint(x + 0.5f, y + 0.5f, z + 0.5f, 0.3f);
}
}
float e = a.energys[x][y][z];
if (e > 0.03f || e < -0.03f) {
setPicColor(e > 0 ? Color.red : Color.BLUE); // 用红色小圆表示正能量蓝色表示负能量
float size = Math.abs(e);// 再用不同大小圆形表示不同能量值
if (size > 1)
size = 1;
drawCircle(x + 0.5f, y + 0.5f, z + 0.5f, size);
}
}
}
}
}
}
}
setPicColor(Color.BLACK);
drawCuboid(0, 0, 0, Env.BRAIN_SIZE, Env.BRAIN_SIZE, Env.BRAIN_SIZE);// 把脑的框架画出来
setPicColor(Color.BLACK); //画出 a.lines里所有线条
Line.drawOnBrainPicture(a, this);
setPicColor(BLACK); //把x,y,z坐标画出来
drawText(Env.BRAIN_SIZE, 0, 0, "x", Env.BRAIN_SIZE * 0.2f);
drawText(0, Env.BRAIN_SIZE, 0, "y", Env.BRAIN_SIZE * 0.2f);
drawText(0, 0, Env.BRAIN_SIZE, "z", Env.BRAIN_SIZE * 0.2f);
setPicColor(RED);
drawLine(0, 0, 0, Env.BRAIN_SIZE, 0, 0);
drawLine(0, 0, 0, 0, Env.BRAIN_SIZE, 0);
drawLine(0, 0, 0, 0, 0, Env.BRAIN_SIZE);
for (Object[] o : Genes.dots) { // 画出所有登记的点的名字
String name = (String) o[0];
int x = (int) o[1];
int y = (int) o[2];
int z = (int) o[3];
drawTextCenter(x, y, z, name, 0.3f);
}
g.setColor(Color.black);
if (note != null) {// 全局注释
g.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 16));
g.drawString(note, 10, 20);
}
drawCuboid(0, 0, 0, Env.BRAIN_SIZE, Env.BRAIN_SIZE, Env.BRAIN_SIZE);// 把脑的框架画出来
g.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 16));
g.drawString("step:" + step + ", ate:" + a.ateFood + ", wrong:" + a.ateWrong, 10, 15);
setPicColor(BLACK); // 把x,y,z坐标画出来
drawText(Env.BRAIN_SIZE, 0, 0, "x", Env.BRAIN_SIZE * 0.2f);
drawText(0, Env.BRAIN_SIZE, 0, "y", Env.BRAIN_SIZE * 0.2f);
drawText(0, 0, Env.BRAIN_SIZE, "z", Env.BRAIN_SIZE * 0.2f);
setPicColor(RED);
drawLine(0, 0, 0, Env.BRAIN_SIZE, 0, 0);
drawLine(0, 0, 0, 0, Env.BRAIN_SIZE, 0);
drawLine(0, 0, 0, 0, 0, Env.BRAIN_SIZE);
// for (int y = 0; y < ColorUtils.rainbow.length; y += 1) {//调试彩虹色
// g.setColor(ColorUtils.rainbow[y]);
// for (int i = 0; i < 9; i++)
// g.drawLine(0, y * 9 + i, 50, y * 9 + i);
// }
g.setColor(Color.black);
if (note != null) {// 全局注释
g.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 16));
g.drawString(note, 10, 20);
}
this.getGraphics().drawImage(buffImg, 0, 0, this);// 利用缓存避免画面闪烁这里输出缓存图片
}
g.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 16));
g.drawString("step:" + step + ", ate:" + a.ateFood + ", wrong:" + a.ateWrong+", miss:" + a.ateMiss+", fat="+a.fat, 10, 15);
public static void setNote(String note) {
Application.brainPic.note = note;
}
// for (int y = 0; y < ColorUtils.rainbow.length; y += 1) {//调试彩虹色
// g.setColor(ColorUtils.rainbow[y]);
// for (int i = 0; i < 9; i++)
// g.drawLine(0, y * 9 + i, 50, y * 9 + i);
// }
// getters & setters
public float getScale() {
return scale;
}
this.getGraphics().drawImage(buffImg, 0, 0, this);// 利用缓存避免画面闪烁这里输出缓存图片
}
public void setScale(float scale) {
this.scale = scale;
}
public static void setNote(String note) {
Application.brainPic.note = note;
}
public float getxAngle() {
return xAngle;
}
// getters & setters
public float getScale() {
return scale;
}
public void setxAngle(float xAngle) {
this.xAngle = xAngle;
}
public void setScale(float scale) {
this.scale = scale;
}
public float getyAngle() {
return yAngle;
}
public float getxAngle() {
return xAngle;
}
public void setyAngle(float yAngle) {
this.yAngle = yAngle;
}
public void setxAngle(float xAngle) {
this.xAngle = xAngle;
}
public float getzAngle() {
return zAngle;
}
public float getyAngle() {
return yAngle;
}
public void setzAngle(float zAngle) {
this.zAngle = zAngle;
}
public void setyAngle(float yAngle) {
this.yAngle = yAngle;
}
public void setPicColor(Color color) {
this.picColor = color;
}
public float getzAngle() {
return zAngle;
}
public Color getPicColor() {
return picColor;
}
public void setzAngle(float zAngle) {
this.zAngle = zAngle;
}
public int getxOffset() {
return xOffset;
}
public void setPicColor(Color color) {
this.picColor = color;
}
public void setxOffset(int xOffset) {
this.xOffset = xOffset;
}
public Color getPicColor() {
return picColor;
}
public int getyOffset() {
return yOffset;
}
public int getxOffset() {
return xOffset;
}
public void setyOffset(int yOffset) {
this.yOffset = yOffset;
}
public void setxOffset(int xOffset) {
this.xOffset = xOffset;
}
public int getyOffset() {
return yOffset;
}
public void setyOffset(int yOffset) {
this.yOffset = yOffset;
}
}

View File

@ -0,0 +1,45 @@
/*
* Copyright 2018 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
* applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
* OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*/
package com.gitee.drinkjava2.frog.brain;
/**
* Cell is a basic node of brain 3d array
*
* 从效率的角度出发LineCell这两个对象将来可能都不实际创建因为Java中对象的操作比数组更占用内存和影响速度
* 所以Cell和Line里尽量只放一些文档静态方法以便以后采用数组来代替Cell对象或Line对象尤其是Line可能用计算机模拟根本不需要分配内存是个虚对象
*
*
* 关于Cell的特性
* 1.Cell相当于一个脑神经元细胞的细胞主体部分能量只保存在主体上只存贮正值 在0~1之间传入的多余的能量将削除
* 2.Cell如果有能量能量会随时间消逝随时间消逝的能量曲线是个属性不同种类的Cell这个属性是不同的
* 3.Cell激活后给每个树突轴突Line)发送等同自身能量的能量注意不是均分而是每个都拷贝一份等量的能量此处能量不守恒由它的上层计算机系统注入能量即熵
* 4.Cell一直给Line发送能量但是否能量被传送能量传送后是否扣除影响细胞主体能量这些都不由Cell考虑而是由Line的属性和行为来决定Line是复杂的
* 同一个Line接收激活的Cell能量,有一个再次传送给间隔时间, 这是由这个Line的阀值特性Cell的能量下降曲线Line的能量扣除值来决定的
*
* Cell可以看出来非常简单比树突轴突要简单的多就是一个能量暂存放大(拷贝)消逝分发节点而树突轴突(统一用Line来表示)有复杂的行为但原则上不保存能量Cell和Line分工
* 协作一个保存能量一个传输能量 Line的种类有很多今后分裂算法的主要任务就是进行Line种类的空间分配
*
* ======== Line可以是个虚对象! =========
* 为什么Line不保存能量因为如果不保存能量Line就是无状态的甚至可以不创建Line实体或数组不需要给Line分配内存将Line作为Cell的一个行为即可能量到了Cell后根据它有
* 多少个和多少种Line属性进行相应的能量分发行为影响周围或近或远的Cell能量
* 打个比方比如
* "6叉Line属性"会对四周的细胞发送能量特长上Line属性会对上方100个细胞间隔的细胞发送能量这个行为实际上并不需要创建Line对象或给它分配内存所以Line可以
* 是一种虚对象, 作为Cell的一种属性存在(即在表示Cell的长整数中占据一个字节的标志位而这个标志位又由分裂算法的基因决定)
* 世上的道理是相通的如果一个数据结据在电脑里能简化编程实际生物细胞也可能采用同样的方案
*
* 总体来说脑是简单的决定它结构的熵并不多一颗人类受精卵约包含750MB的信息所以受精卵里并不记录每个细胞每个连线的位置而是大致决定有多少种类细胞每类细胞上有相似的连线然后交给分裂算法来
* 生成海量的细胞但是这个脑的复杂性与细胞数量无关这个复杂性即熵量仅仅是由这750MB还要去除与脑结构不相关的其它部分的信息来决定的脑本身的复杂度可能定义为"约50MB信息控制的100亿细胞的分形结构"
*
* @author Yong Zhu
* @since 1.0
*/
public class Cell {
}

View File

@ -1,89 +0,0 @@
/*
* Copyright 2018 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
* applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
* OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*/
package com.gitee.drinkjava2.frog.brain;
import static com.gitee.drinkjava2.frog.util.RandomUtils.percent;
import java.lang.reflect.Field;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import com.gitee.drinkjava2.frog.Animal;
import com.gitee.drinkjava2.frog.util.Logger;
import com.gitee.drinkjava2.frog.util.RandomUtils;
/**
* Here store counts
*
* 这个类存放脑常量定义变异日志打印相关的方法
* 神经网络中有一些全局常量与结构生成无关这时可以把所有常量定义到animal和egg的constGenes常量数组中用统一的随机方法来变异这些常量
*
* @author Yong Zhu
* @since 15.0
*/
@SuppressWarnings("all")
public class Consts {
public static int CountsQTY; //总常量数量
private static int index_ = 0;
private static int index() {
return index_++;
}
public static final int ADD_EYE = index(); //用index()这种编程技巧而不是直接给定数值是为了增删常量定义时比较方便不会影响其它行
public static final int ADD_BITE = index();
public static final int REDUCE_BITE = index();
public static final int HOLE_FRESH = index();
public static final int HOLE_REDUCE = index();
private static Map<String, Integer> values = new LinkedHashMap<String, Integer>();
static {
try {
Class c = Consts.class;
Field[] fs = c.getDeclaredFields();
for (Field f : fs) { // 用反射来获取常量的名称并保存下来将来在printLog中要按顺序打印出所有常量名
if (int.class.equals(f.getType()) && !"LENGTH".equals(f.getName()) && !"index_".equals(f.getName())) {
values.put(f.getName(), f.getInt(null));
}
}
} catch (Exception e) {
e.printStackTrace();
System.exit(-1);
}
CountsQTY = values.size();
}
public static boolean[] exist = new boolean[CountsQTY]; //不是每个常量组数都用到只有被用字母代表的才会用到并在这里标记这种方法比MAP要快
static StringBuilder sb = new StringBuilder();
public static void printLog(Animal a) {
sb.setLength(0);
int i = 0;
for (Entry<String, Integer> e : values.entrySet()) {
sb.append(e.getKey()).append("=").append(a.consts[e.getValue()]).append("\t\t");
if (i++ % 6 == 5)
sb.append("\n");
}
Logger.debug(sb.toString());
}
public static void constMutation(Animal a) { //全局参数变异, 这一个方法变异动物的所有常量
for (int i = 0; i < CountsQTY; i++) {
if (percent(20))
a.consts[i] = RandomUtils.vary(a.consts[i]);
}
}
}

View File

@ -10,18 +10,15 @@
*/
package com.gitee.drinkjava2.frog.brain;
import static com.gitee.drinkjava2.frog.brain.Consts.ADD_BITE;
import static com.gitee.drinkjava2.frog.brain.Consts.REDUCE_BITE;
import java.util.ArrayList;
import com.gitee.drinkjava2.frog.Animal;
import com.gitee.drinkjava2.frog.Env;
import com.gitee.drinkjava2.frog.objects.OneDotEye;
import com.gitee.drinkjava2.frog.util.RandomUtils;
/**
* Genes代表不同的脑细胞参数对应每个参数用8叉/4叉/2叉树算法给每个细胞添加细胞参数和行为
* 每个脑细胞用一个long来存储所以目前最多允许64个基因位, 多字节参数可以由多个基因位决定每个基因位都由一个单独的阴阳8/4/2叉树控制多个基因就组成了一个8/4/2叉树阵列
* 基因+分裂算法=结构
* 每个脑细胞用一个long来存储所以目前最多允许64个基因位,
* 多字节参数可以由多个基因位决定每个基因位都由一个单独的阴阳8/4/2叉树控制多个基因就组成了一个8/4/2叉树阵列 基因+分裂算法=结构
* 基因+分裂算法+遗传算法=结构的进化
*
* 这个类里定义每个基因位的掩码以及对应基因的细胞行为, 脑结构的所有参数都要用基因来控制开始时可以有常量魔数但以后都放到基因里去自动进化
@ -30,31 +27,50 @@ import com.gitee.drinkjava2.frog.util.RandomUtils;
* @since 10.0
*/
@SuppressWarnings("all")
public class Genes { //Genes登记所有的基因 指定每个基因允许分布的空间范围注意登录完后还要并针对每个基因在active方法里写出它对应的细胞行为
public static int GENE_MAX = 64; //目前最多允许64个基因
public class Genes { // Genes登记所有的基因 指定每个基因允许分布的空间范围注意登录完后还要并针对每个基因在active方法里写出它对应的细胞行为
public static int GENE_MAX = 64; // 目前最多允许64个基因
public static int GENE_NUMBERS = 0; //这里统计定义了多少个基因
private static int zeros = 0; //当前基因位掩码0个数
public static int GENE_NUMBERS = 0; // 这里统计定义了多少个基因
private static int zeros = 0; // 当前基因位掩码0个数
public static boolean[] display_gene = new boolean[GENE_MAX]; //如果这个参数为真此基因显示在脑图上
public static boolean[] fill_gene = new boolean[GENE_MAX]; //如果这个参数为真此基因填充指定的区域而不是由分裂算法随机生成
public static boolean[] display_gene = new boolean[GENE_MAX]; // 如果这个参数为真此基因显示在脑图上
public static boolean[] fill_gene = new boolean[GENE_MAX]; // 如果这个参数为真此基因填充指定的区域而不是由分裂算法随机生成
public static int[] xLimit = new int[GENE_MAX]; //用来手工限定基因分布范围详见register方法
public static int[] xLimit = new int[GENE_MAX]; // 用来手工限定基因分布范围详见register方法
public static int[] yLimit = new int[GENE_MAX];
public static int[] zLimit = new int[GENE_MAX];
public static ArrayList<Object[]> dots = new ArrayList<>(); // 临时如果登录的范围是个三座标点把它放在这里以方便随机连线只落在登记的细胞上
/**
* Register a gene 依次从底位到高位登记所有的基因掩码及对应的相关参数
*
* @param maskBits how many mask bits 掩码位数即有几个1
* @param display whether to display the gene on the BrainPicture 是否显示在脑图
* @param fill whether to fill to specified 3D/2D/1D/1Point area 是否直接用此基因填充指定的区域区域可以是三维二维线状及一个点
* @param x_limit gene only allow on specified x layer 如x_layer大于-1且y_layer=-1, 表示只生成在指定的x层对应的yz平面上这时采用4叉树而不是8叉树以提高进化速度
* @param y_limit gene only allow on specified x, y axis 如大于-1表示只生成在指定的x,y坐标对应的z轴上这时采用2叉阴阳树算法
* @param z_limit gene only allow on specified x, y, z 点上, 表示手工指定基因位于x,y,z坐标点上
* @return a long wtih mask bits 返回基因掩码高位由maskBits个1组成低位是若干个0以后判断一个cell上是否含有这个基因只需要用cell对应的long和这个 掩码做与运算即可
* @param name
* 基因的名字这个可选 缺省为null
* @param maskBits
* how many mask bits 掩码位数即有几个1
* @param display
* whether to display the gene on the BrainPicture 是否显示在脑图
* @param fill
* whether to fill to specified 3D/2D/1D/1Point area
* 是否直接用此基因填充指定的区域区域可以是三维二维线状及一个点
* @param x_limit
* gene only allow on specified x layer 如x_layer大于-1且y_layer=-1,
* 表示只生成在指定的x层对应的yz平面上这时采用4叉树而不是8叉树以提高进化速度
* @param y_limit
* gene only allow on specified x, y axis
* 如大于-1表示只生成在指定的x,y坐标对应的z轴上这时采用2叉阴阳树算法
* @param z_limit
* gene only allow on specified x, y, z 点上, 表示手工指定基因位于x,y,z坐标点上
* @return a long wtih mask bits
* 返回基因掩码高位由maskBits个1组成低位是若干个0以后判断一个cell上是否含有这个基因只需要用cell对应的long和这个
* 掩码做与运算即可
*/
public static long register(int maskBits, boolean display, boolean fill, int x_limit, int y_limit, int z_limit) {
public static long register(String name, int maskBits, boolean display, boolean fill, int x_limit, int y_limit, int z_limit) {
if (x_limit > -1 && y_limit > -1 && z_limit > -1) {// 临时如果登录的范围是个三座标点把它放在这里以方便随机连线只落在登记的细胞上
dots.add(new Object[] {name, x_limit, y_limit, z_limit });
}
for (int i = GENE_NUMBERS; i < GENE_NUMBERS + maskBits; i++) {
display_gene[i] = display;
fill_gene[i] = fill;
@ -75,60 +91,63 @@ public class Genes { //Genes登记所有的基因 指定每个基因允许分
System.out.println("目前基因位数不能超过" + GENE_MAX);
System.exit(-1);
}
return Long.parseLong(one + zero, 2); //将类似"111000"这种字符串转换为长整
return Long.parseLong(one + zero, 2); // 将类似"111000"这种字符串转换为长整
}
public static long register(int... pos) {//登记并指定基因允许分布的位置
return register(1, true, false, pos[0], pos[1], pos[2]);
public static long register(int... pos) {// 登记并指定基因允许分布的位置
return register(null, 1, true, false, pos[0], pos[1], pos[2]);
}
public static long registerFill(int... pos) {//登记并手工指定基因填满的位置
return register(1, true, true, pos[0], pos[1], pos[2]);
public static long registerFill(int... pos) {// 登记并手工指定基因填满的位置
return register(null, 1, true, true, pos[0], pos[1], pos[2]);
}
public static boolean hasGene(long cell, long geneMask) { //判断cell是否含某个基因
public static long registerFill(String name, int... pos) {// 登记并手工指定基因填满的位置
return register(name, 1, true, true, pos[0], pos[1], pos[2]);
}
public static boolean hasGene(long cell, long geneMask) { // 判断cell是否含某个基因
return (cell & geneMask) > 0;
}
private static final int NA = -1;
private static final int CS4 = Env.BRAIN_SIZE / 4;
//============开始登记有名字的基因==========
public static int[] EYE_POS = new int[]{0, 0, 0};
public static long EYE = registerFill(EYE_POS); //视网膜细胞这个版本暂时只允许视网膜分布在x=0,y=0的z轴上即只能看到一条线状图形
// ============开始登记有名字的基因==========
public static int[] EYE1_POS = new int[] { 0, 0, 0 };
public static long EYE1 = registerFill("眼1", EYE1_POS); // 视网膜细胞
//
// public static int[] EYE2_POS = new int[] { 0, 0, 1 };
// public static long EYE2 = registerFill("眼2", EYE2_POS); // 视网膜细胞
public static int[] BITE_POS = new int[]{2, 0, 0};
public static long BITE = registerFill(BITE_POS); //咬动作细胞定义在一个点上, 这个细胞如激活就咬食物
public static int[] SWEET_POS = new int[] { 0, 2, 0 };
public static long SWEET = registerFill("", SWEET_POS); // 甜味细胞
//========开始登记无名字的基因 =========
public static int[] BITTER_POS = new int[] { 0, 2, 1 };
public static long BITTER = registerFill("", BITTER_POS); // 苦味细胞
public static int[] BITE_POS = new int[] { 0, 3, 3 };
public static long BITE = registerFill("", BITE_POS); // 咬动作细胞
public static int[] ACT_POS = new int[] { 0, 1, 3 };
public static long ACT = registerFill("", ACT_POS); // 活跃细胞这个始终激活 `
// 登记细胞间关联(触突树突)
static {
registerFill("空1", 1,1,0); //随便登记几个空细胞万一能用上呢
registerFill("空2", 1,1,1); //
registerFill("空3", 1,1,2); //
registerFill("空4", 1,1,3); //
// register(8, true, false, 0, NA, NA); //8个方向的信号发送联接
}
//========= active方法在每个主循环都会调用用来存放细胞的行为这是个重要方法 ===========
// ========= active方法在每个主循环都会调用用来存放细胞的行为这是个重要方法 ===========
//但是本版本因为没用到分裂算法所以这个方法体为空
public static void active(Animal a, int step) {
for (int z = Env.BRAIN_SIZE - 1; z >= 0; z--)
for (int x = Env.BRAIN_SIZE - 1; x >= 0; x--) {
int y = 0;
int[] src = new int[]{x, y, z};
long cell = a.cells[x][y][z];
float energy = a.energys[x][y][z];
if (energy >= 1f) { //如果细胞激活了
a.setEng1(x, y, z); //细胞强度不允许超过1见TestInput3
if (hasGene(cell, BITE)) { //TODO:如果是咬细胞
if (OneDotEye.foodSweet(step)) { //如食物是甜的
a.awardAAAA(); //奖励
a.ateFood++;
} else {
a.penaltyAA(); //其它时间是咬错了
a.ateWrong++;
}
a.setEng0(x, y, z); //咬完了后细胞能量归0
}
}
}
}
}

View File

@ -0,0 +1,132 @@
/*
* Copyright 2018 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
* applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
* OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*/
package com.gitee.drinkjava2.frog.brain;
import java.io.Serializable;
import com.gitee.drinkjava2.frog.Animal;
import com.gitee.drinkjava2.frog.util.RandomUtils;
/**
* Line is line connect 2 cells, may have many attributes
* Line是一个随机连接两个脑细胞的连线连线有多种不同属性不同的属性有不同的静态动态参数以及不同的行为
* 因为Line数量太多了对象的创建消毁太消耗资源为了节约内存和提高速度把Line设计成一个虚拟逻辑对象不会实际创建它的实例所以目前这个Line里只保留静态方法
* Line的动态参数保存在Animal.lines数列中每个元素是一个整数数组分别为 type,x1,y1,z1,x2,y2,z2,dres解释见下
* type: 线的类型同样类型的线它的常量参数是相同的常量参数保存在Animal.consts数组中起点为type*countsQTY
* x1,y1,z1为源x2,y2,z2为目的能量永远是从源到目流动与能量的正负无关要实现双向能量流动必须建立两根线
* dres dynamic resistance 动态电阻值动态电阻率敏感度大于0时电阻在重复信号刺激下会变小这样就可用电阻来作为记忆元件这个值是个有状态的值每根线条都要单独记录不像其它参数是个常量
* Line的常量参数都取值在0~1之间保存在Animal.consts数组中起点为type*countsQTY通过这种方法可以保证相同type的线它的常量参数相同(以便于将来移植到分裂算法上)参数依次为
* res: resistance, 常量电阻即通常说的权重, 0时为断路1时为全通因为no相同的line常量相同所以一条线只需要记录no不需要记录每个常量参数
* drs: dynamic resistance sensitivity 动态电阻率敏感度0时电阻率不会变动 1时电阻率会随信号重复立即变到1(全通其它值处于两者之间
* ovfoverflow 常量溢流阀值 当传递的能量大于阀值时高于阀值的部分能量传递
* ovb: overflow break 常量溃坝阀值 当传递的能量大于阀值时, 能量全部传递
* not: not logic 反相器>0.5, 将会对通过的能量反相即乘以-1
* mul: multiple 乘法器会对通过的能量值乘以一个倍数0为11时为10倍
* min: minus 扣能器 会对细胞的能量扣除一部分0为01时为1直到细胞能量为0
* add: add 加能器会对细胞能量增加一部分0为01时为1,直到细胞能量为1
* 常量参数是叠加的一个Line可以同时具有常量电阻动态电阻溢流阀反相器扣能器等属性优先级暂以上面顺序为准
* 题外话:
* 目前版本Line的数量少可以为每个Line的参数在Animal里分配一个数组空间当数量多后将来打算可能用分裂算法来代替随机连线算法分裂算法主要优点一是压缩率高二是
* 树形结构比较合理节点的等级不同一个节点的变异可能造成整个子树的增删随机连线算法做不到这一点所有节点都是同等级的无法应对环境突变情况
* 如果将来采用分裂算法Line的所有动态静态参数都要想办法挂靠在Cell上比如源目的坐标要改成相对当前细胞的偏移量这样就把同一类细胞的绝对坐标动态参数变成了相对坐标的静态常量
* Line至少会有个动态电阻当前dres值这是个有状态的值无法变成常量将来也要想办法把每根线的这个值挂靠在Cell上反正目标就是消除Line这个对象的生成让它变成一个虚拟对象以节约内存和提高速度
*
* @author Yong Zhu
* @since 1.0
*/
public class Line implements Serializable {
private static final long serialVersionUID = 1L;
public static int countsQTY = 8; // res, drs, ovf , ovb, not ...等常量参数
public static void randomAddorRemoveLine(Animal a) {//对animal的线条个数进行随机增减线条参数的变异不在这个方法中而是在常量变异见anmial的constMutate方法
if (RandomUtils.percent(10f)) { // 这两行随机加减Line
int i = RandomUtils.nextInt(Genes.dots.size());
Object[] d1 = Genes.dots.get(i);
i = RandomUtils.nextInt(Genes.dots.size());
Object[] d2 = Genes.dots.get(i);
a.lines.add(new int[] { RandomUtils.nextInt(Animal.CountsQTY / countsQTY), (int) d1[1], (int) d1[2], (int) d1[3], (int) d2[1], (int) d2[2], (int) d2[3], 5000 });
}
if (!a.lines.isEmpty() && RandomUtils.percent(10f))
a.lines.remove(RandomUtils.nextInt(a.lines.size()));
}
// ========= active方法在每个主循环都会调用调用animal所有Line的行为这是个重要方法 ===========
public static void active(Animal a, int step) {//
for (int[] l : a.lines) {
int start = l[0] * countsQTY; //start是line在常数数组的起始点
int x1 = l[1];
int y1 = l[2];
int z1 = l[3];
int x2 = l[4];
int y2 = l[5];
int z2 = l[6];
// int dres = l[7]; //dres取值0~10000对应电阻率0~1
float res = a.consts[start++]; //resistance, 常量电阻即通常说的权重, 0时为断路1时为全通
// float drs = a.consts[start++]; //dynamic resistance sensitivity 动态电阻率敏感度0时电阻率不会变动 1时电阻率会随信号重复立即变到1(全通其它值处于两者之间
//float ovf = a.consts[start++]; //overflow 常量溢流阀值 当传递的能量大于阀值时高于阀值的部分能量传递
//float ovb = a.consts[start++]; //overflow break 常量溃坝阀值 当传递的能量大于阀值时, 能量全部传递
float not = a.consts[start++]; //not logic 反相器>0.5, 将会对通过的能量反相即乘以-1
// float mul = a.consts[start++]; //乘法器会对通过的能量值乘以一个倍数0为11时为10倍
// float min = a.consts[start++]; //扣能器 会对细胞的能量扣除一部分0为01时为1直到细胞能量为0
// float add = a.consts[start++]; //加能器会对细胞能量增加一部分0为01时为1
float e = a.getEng(x1, y1, z1);
if (e < 0.1f)
continue;
if (e > 0.3f) {
// if (min > 0.1) { //扣能器
// float newSrcEng = a.getEng(x1, y1, z1) - min;
// a.setEng(x1, y1, z1, newSrcEng);
// }
// if (add > 0.1) { //加能器
// float newSrcEng = a.getEng(x1, y1, z1) + add;
// a.setEng(x1, y1, z1, newSrcEng);
// }
}
// if (e < ovb) //溃坝阀值
// continue;
// if (ovf > 0.1) { //溢流阀
// e = e - ovf;
// }
e = e * res; //静态电阻
// e = e * dres * .0001f; //动态电阻0~10000之间
// if (drs > 0.1) { //如果动态电阻敏感系统存在则每次传送一次能量电阻就变小一次
// dres += drs;
// if (dres > 10000)
// dres = 10000;
// }
if (not > 0.5) //反相器
e = -e;
// if (mul > 0.1) {
// e = e * 10 * mul; //mul是乘法器mul在0~1之间但是它控制的倍率在0~10倍之间
// }
a.addEng(x2, y2, z2, e); //能量传到target cell
}
}
//TODO 1 模式识别的环境模拟和判定 2.静态参数的初值变异 上面这个方法只是随便写的很可能跑不出结果要调整为大多数参数不起作为,即大多数情况下设为0
public static void drawOnBrainPicture(Animal a, BrainPicture pic) {// 画出所有细胞间连线
for (int[] l : a.lines) {
float f1 = RandomUtils.nextFloat() * .1f;//稍微调整下位置好看出来是否有多条线在同一个位置
float f2 = RandomUtils.nextFloat() * .1f;
pic.drawCentLine(l[1] + f1, l[2] + f2, l[3] + f2, l[4] + f1, l[5], l[6]);
pic.drawTextCenter(0.5f * (l[1] + l[4]), 0.5f * (l[2] + l[5]), 0.5f * (l[3] + l[6]), Integer.toString(l[0]), 0.15f);
pic.drawPointCent(l[4] + f1, l[5], l[6], .1f);
}
}
}

View File

@ -15,7 +15,6 @@ import java.util.ArrayList;
import com.gitee.drinkjava2.frog.Animal;
import com.gitee.drinkjava2.frog.Env;
import com.gitee.drinkjava2.frog.brain.Consts;
import com.gitee.drinkjava2.frog.util.RandomUtils;
/**
@ -37,7 +36,8 @@ public class Egg implements Serializable {
// gene record the 8-tree structure of brain cells
// 基因是随机生成的8叉树数据结构和实际生物每个细胞都要保存一份基因不同程序中每个脑细胞并不需要保存基因的副本这样可以极大地减少内存占用
public ArrayList<ArrayList<Integer>> genes = new ArrayList<>();
public int[] constGenes = new int[Consts.CountsQTY]; //animal中的全局常量基因全放在这里用随机数来生成用遗传算法筛选
public float[] consts = new float[Animal.CountsQTY]; //全局常量全放在这里用随机数来生成用遗传算法筛选
public ArrayList<int[]> lines = new ArrayList<>();//随机连线是可以遗传的
public Egg() {// 无中生有创建一个蛋先有蛋后有蛙
x = RandomUtils.nextInt(Env.ENV_WIDTH);
@ -53,7 +53,8 @@ public class Egg implements Serializable {
g.addAll(gene);
genes.add(g);
}
System.arraycopy(a.consts, 0, this.constGenes, 0, constGenes.length);
System.arraycopy(a.consts, 0, this.consts, 0, consts.length); //把a的常量数组拷到蛋里
lines.addAll(a.lines); //把a的连线拷到蛋里
}
/**
@ -63,7 +64,14 @@ public class Egg implements Serializable {
x = a.x;
y = a.y;
genes.addAll(a.genes);
System.arraycopy(a.constGenes, 0, this.constGenes, 0, constGenes.length);
System.arraycopy(a.consts, 0, this.consts, 0, consts.length);
lines.addAll(a.lines); //把a的连线拷到蛋里
if (RandomUtils.percent(3)) { //小概率插入b的连线到a蛋中
if (!b.lines.isEmpty())
a.lines.add(b.lines.get(RandomUtils.nextInt(b.lines.size())));
}
if (RandomUtils.percent(20)) //插入蛋B的基因到A蛋中
for (int i = 0; i < b.genes.size(); i++) {
if (RandomUtils.percent(2)) {
@ -75,8 +83,8 @@ public class Egg implements Serializable {
}
}
if (RandomUtils.percent(20)) {//交换蛋B的常量基因到A蛋中, 不重要先写上
int n = RandomUtils.nextInt(this.constGenes.length);
this.constGenes[n] = b.constGenes[n];
int n = RandomUtils.nextInt(this.consts.length);
this.consts[n] = b.consts[n];
}
}
}

View File

@ -15,15 +15,15 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import com.gitee.drinkjava2.frog.Animal;
import com.gitee.drinkjava2.frog.Application;
import com.gitee.drinkjava2.frog.Env;
import com.gitee.drinkjava2.frog.Frog;
import com.gitee.drinkjava2.frog.brain.Consts;
import com.gitee.drinkjava2.frog.util.LocalFileUtils;
import com.gitee.drinkjava2.frog.util.Logger;
@ -50,22 +50,8 @@ public class FrogEggTool {
Env.frog_eggs.clear();
for (int i = 0; i < Env.FROG_EGG_QTY; i++) //每次下蛋数量固定为EGG_QTY个
Env.frog_eggs.add(new Egg(Env.frogs.get(i)));
Logger.info("Fist frog fat={}, gene size={}, Last frog fat={}", first.fat, getGeneSize(first), last.fat);
//debug
for (int x = 0; x < Env.BRAIN_SIZE; x++) {
StringBuilder s = new StringBuilder();
for (int z = 0; z < Env.BRAIN_SIZE; z++) {
int[] holes = first.holes[x][0][z];
if (holes == null)
s.append("0,");
else
s.append(first.holes[x][0][z].length / 4).append(",");
}
Logger.debug("x=" + x + ", holes:" + s);//打印出每个细胞的洞数量
}
Consts.printLog(first);//debug; 打印出每个细胞的常量
Logger.info(Env.round+" Frist frog fat={}, gene size={}, lines size={}, Last frog fat={}", first.fat, getGeneSize(first), first.lines.size(), last.fat);
//Logger.info("Frist frog consts="+Arrays.toString(first.consts)); debug
if (Env.SAVE_EGGS_FILE) {
FileOutputStream fo = new FileOutputStream(Application.CLASSPATH + "frog_eggs.ser");
@ -116,8 +102,9 @@ public class FrogEggTool {
try {
FileInputStream eggsFile = new FileInputStream(Application.CLASSPATH + "frog_eggs.ser");
ObjectInputStream eggsInputStream = new ObjectInputStream(eggsFile);
Env.frog_eggs = (List<Egg>) eggsInputStream.readObject();
Env.frog_eggs = (ArrayList<Egg>) eggsInputStream.readObject();
Logger.info("Loaded " + Env.frog_eggs.size() + " eggs from file '" + Application.CLASSPATH + "frog_eggs.ser" + "'.\n");
//System.out.println(Env.frog_eggs.get(0).lines.size()); //debug
eggsInputStream.close();
} catch (Exception e) {
errorfound = true;

View File

@ -10,6 +10,8 @@
*/
package com.gitee.drinkjava2.frog.objects;
import java.awt.Graphics;
/**
* EnvObject means some virtual object in Env
*
@ -18,21 +20,21 @@ package com.gitee.drinkjava2.frog.objects;
*/
public interface EnvObject {//EnvObject接口代表虚拟环境中的一种物体每定义一个物体要在Env的things变量中添加它
public void build();// 在Env中创建本身物体只在每屏测试前调用一次
public void build(Graphics g);// 在Env中创建本身物体只在每屏测试前调用一次
public void destory();// 从Env中清除本身物体只在每屏测试完成后调用一次
public void destory(Graphics g);// 从Env中清除本身物体只在每屏测试完成后调用一次
public void active(int screen, int step);// 每个步长即时间最小单位)都会调用一次这个方法, 两个参数分别是当前屏数和当前步长数
public void active(int screen, int step, Graphics g);// 每个步长即时间最小单位)都会调用一次这个方法, 两个参数分别是当前屏数和当前步长数
public static class DefaultEnvObject implements EnvObject {//EnvObject接口的缺省实现
public void build() {
public void build(Graphics g) {
}
public void destory() {
public void destory(Graphics g) {
}
public void active(int screen, int step) {
public void active(int screen, int step, Graphics g) {
}
}
}

View File

@ -1,57 +0,0 @@
package com.gitee.drinkjava2.frog.objects;
import com.gitee.drinkjava2.frog.Env;
import com.gitee.drinkjava2.frog.Frog;
import com.gitee.drinkjava2.frog.brain.Genes;
import com.gitee.drinkjava2.frog.objects.EnvObject.DefaultEnvObject;
import com.gitee.drinkjava2.frog.util.RandomUtils;
/**
* Eye的作用只有一个就是把一个随机序号的二进制条码投射到视网膜细胞上暂定于x=0的YZ平面上
*
* 当前目标是实现简单的模式识别frog要能进化出模式识别功能即看到图来预测是要咬还是不咬这就是最简单模式识别了即原来的信号顺序是
* -(随机驱动)-, -(随机驱动)-
* 最终进化为: --- --不咬-也就是说根据图案来联想到甜或苦的感觉细胞激活从而预判咬还是不咬而无需等随机信号驱动也无需等味觉信号传入实际也不可能在咬之前有味觉)
*
*
* 这个功能通过 随机饿不咬忆这几类细胞来组合完成
* 视细胞是其中一环,位于本类操作的视网膜区Eye将外界信号先简单用一个随机序号的条形码)投射成视网膜上的图像
* 饿细胞(可选)可以设定它永远活跃即青蛙永远都吃不饱饿细胞强烈激活时有可能直接驱动咬细胞
* 随机细胞(可选)有一定机率活跃作用是随机驱动咬或不咬细胞
* 咬细胞如果活跃产生实际的咬动作Env会根据咬的图案有毒(随机序号被3除余1)扣分并激活苦细胞或是无毒(随机序号被3除余2)加分并激活甜细胞
* 苦细胞和甜细胞, 苦细胞和甜细胞是感觉信号它由食物的毒性决定只有咬下后才会激活苦和甜细胞会对忆细胞的洞大小进行调节从以影响条件反射强度
* 忆细胞 这种细胞的作用是记忆当两个细胞在时间上有短期内的相关性时就会在忆细胞上打出两个洞来将来任一个细胞激活就从洞里反向发送光子从而实现信号的关联即记忆功能洞大小由信号重复数决定
* 因为洞大小可以很大也可以很小直到遗忘消失所以忆细胞接收和发送的脉冲信号可能强度有巨大差别并不是0/1这种简单信号而是有强度的信号
*
* 目前为最简化起见食物图像用随机序号的二进制条形码图案代表将来可扩展到2维食物图形或者二维文字图形逻辑不需要改动
*
*/
public class Eye extends DefaultEnvObject {
public static int code; //这里用code的二进制单维条码代表任务图形随便假设被3除后余1的有毒且苦余2的可食且甜余0的无毒无味 (debug:调试时再简化一下只要看到就意味可食
@Override
public void active(int screen, int step) {
if (step % 20 == 0) { //每隔20步在所有青蛙的视网膜上画code的二进制条形码 设code能被3除余2即为食物在Genes里去判断
code = RandomUtils.nextInt(8);
Frog f;
for (int i = screen; i < screen + Env.FROG_PER_SCREEN; i++) {
f = Env.frogs.get(i);
drawImageOnEye(f, code);
}
}
}
/**
* 根据code数字在视网膜上画图即给某些神经细胞赋能量值实验阶段先用单维的二进制条形码代替二维图像
*/
private static void drawImageOnEye(Frog f, int code) {
long i = 1;
for (int z = 0; z < Env.BRAIN_SIZE; z++) {
float engery = ((code & i) > 0) ? 5f : 0;
if (Genes.hasGene(f.cells[0][0][z], Genes.EYE))
f.energys[0][0][z] = engery;
i = i << 1;
}
}
}

View File

@ -14,6 +14,8 @@ import static com.gitee.drinkjava2.frog.Env.ENV_HEIGHT;
import static com.gitee.drinkjava2.frog.Env.ENV_WIDTH;
import static com.gitee.drinkjava2.frog.Env.FOOD_QTY;
import java.awt.Graphics;
import com.gitee.drinkjava2.frog.Env;
import com.gitee.drinkjava2.frog.objects.EnvObject.DefaultEnvObject;
import com.gitee.drinkjava2.frog.util.RandomUtils;
@ -31,7 +33,7 @@ public class Food extends DefaultEnvObject {//食物除了被吃,它自己没
public static int food_ated = 0;
@Override
public void build() {
public void build(Graphics g) {
food_ated = 0;
for (int i = 0; i < FOOD_QTY; i++) { // 随机位置生成食物
int x = RandomUtils.nextInt(ENV_WIDTH);
@ -43,7 +45,7 @@ public class Food extends DefaultEnvObject {//食物除了被吃,它自己没
}
@Override
public void destory() {
public void destory(Graphics g) {
food_ated = 0;
for (int x = 0; x < ENV_WIDTH; x++) // 清除食物
for (int y = 0; y < ENV_HEIGHT; y++) {

View File

@ -1,40 +0,0 @@
package com.gitee.drinkjava2.frog.objects;
import com.gitee.drinkjava2.frog.Env;
import com.gitee.drinkjava2.frog.Frog;
import com.gitee.drinkjava2.frog.brain.Consts;
import com.gitee.drinkjava2.frog.objects.EnvObject.DefaultEnvObject;
import com.gitee.drinkjava2.frog.util.RandomUtils;
/**
*
* OneDotEye的作用只有一个就是定期在视网膜细胞上激活一个点告知食物存在
* 当前版本中Eye还没用到目前只测试模式识别最简单的情况即只有一个感光细胞的情况下如何形成看到食物时咬下这个条件反射
*
*/
public class OneDotEye extends DefaultEnvObject {
private static int[] food = new int[Env.STEPS_PER_ROUND];
static {
//食物只会出现在15为周期但不固定的时间点上以防止细胞进化出周期进入鞍点, 食物用数字表示0为不存在1为甜2为苦
for (int i = 15; i < Env.STEPS_PER_ROUND - 15; i += 15)
food[i + RandomUtils.nextNegOrPosInt(5)] = 1 + RandomUtils.nextInt(2);
}
public static boolean foodExist(int step) {
return food[step] > 0;
}
public static boolean foodSweet(int step) {
return food[step] == 1;
}
@Override
public void active(int screen, int step) {
if (foodExist(step)) { //如食物存在, 激活所有青蛙的视网膜
for (int i = screen; i < screen + Env.FROG_PER_SCREEN; i++) {
Frog f = Env.frogs.get(i);
f.energys[0][0][0] = f.consts[Consts.ADD_EYE];
}
}
}
}

View File

@ -0,0 +1,117 @@
package com.gitee.drinkjava2.frog.objects;
import java.awt.Color;
import java.awt.Graphics;
import com.gitee.drinkjava2.frog.Env;
import com.gitee.drinkjava2.frog.Frog;
import com.gitee.drinkjava2.frog.brain.Genes;
import com.gitee.drinkjava2.frog.util.RandomUtils;
/**
*
* OneInputJudge 用来模拟只有一个信号输入时的情况下青蛙是否随信号点亮咬下去, 随信号消失不咬
* 这是最简单的模式识别即1时输出1 0时输出0
*
*/
public class OneInputJudge implements EnvObject {
private int n = 16; //n是小方块边长
private static int[] food = new int[Env.STEPS_PER_ROUND + 10];
private static int totalFood;
private static void resetFood() {
int step = 0;
int x = 2;
while (step < (Env.STEPS_PER_ROUND)) {
int firstFood = RandomUtils.nextInt(5); //以10为一组随机安排5个食物
for (int i = 0; i < 10; i++)
if (i < firstFood || i > firstFood + x)
food[step + i] = 0;
else
food[step + i] = 1;
step += 10;
if (x == 2)
x = 6;
else
x = 2;
}
for (int i : food)
if (i == 1)
totalFood++;
}
@Override
public void build(Graphics g) { //build在每屏测试前调用一次这里用随机数准备好食物出现和消失的顺序为测试作准备
if (totalFood == 0) {
resetFood();
System.out.println("totalFood=" + totalFood);
}
resetFood();
for (int i = 0; i < Env.STEPS_PER_ROUND; i++) { //画出当前食物分布图
int x = i % (Env.ENV_WIDTH / n);
int y = i / (Env.ENV_WIDTH / n);
if (food[i] == 1)
g.fillRect(x * n, y * n, n, n);
else
g.drawRect(x * n, y * n, n, n);
x++;
}
}
@Override
public void destory(Graphics g) {
}
/*
* active方法中做以下事情
* 1.如果有食物就点亮视细胞
* 2.如果视细胞点亮且咬下就奖如果视细胞没点亮且咬下就罚
* 3.在左边Env显示区画出当前food的进度条
*/
@Override
public void active(int screen, int step, Graphics g) {
Frog f;
int x, y;
boolean hasFood = food[step] == 1;
for (int i = 0; i < Env.FROG_PER_SCREEN; i++) {
f = Env.frogs.get(Env.current_screen * Env.FROG_PER_SCREEN + i);
if (hasFood)
f.setEng(Genes.EYE1_POS, 1f);
else
f.setEng(Genes.EYE1_POS, 0f);
if (f.getEng(Genes.BITE_POS) > 0.8) {
if (f.getEng(Genes.EYE1_POS) > 0.8) {
f.awardA(); //咬到了有奖
f.ateFood++;
f.setEng(Genes.SWEET_POS, 1); //咬到能感觉到甜这是大自然进化出来的功能给青蛙一个知道自己咬对的信号
f.setEng(Genes.BITTER_POS, 0);
g.setColor(Color.GREEN);
} else {
f.ateWrong++;
f.penaltyA(); //咬空了扣分
f.setEng(Genes.SWEET_POS, 0);
f.setEng(Genes.BITTER_POS, 1); //咬错了能感觉到苦味这是大自然进化出来的功能给青蛙一个知道自己咬错的信号
g.setColor(Color.RED);
}
} else { //如果都没有咬关闭甜和苦味感觉
f.setEng(Genes.SWEET_POS, 0);
f.setEng(Genes.BITTER_POS, 0);
if (hasFood) {
g.setColor(Color.RED);
f.ateMiss++;
}
else
g.setColor(Color.GREEN);
}
if (i == 0) {
x = step % (Env.ENV_WIDTH / n);//用红色标记当前走到哪一步食物位置
y = step / (Env.ENV_WIDTH / n);
g.fillRect(x * n, y * n + n / 2, n, 2);
}
}
}
}

View File

@ -0,0 +1,13 @@
package com.gitee.drinkjava2.frog.objects;
import com.gitee.drinkjava2.frog.objects.EnvObject.DefaultEnvObject;
/**
*
* ThreeInputJudge 用来模拟当有3个视细胞一个甜细胞一个咬细胞这种情况下 判断青蛙能否根据甜味细胞的训练在下次相同视信号出现时提前咬下去
* 因为有3个视细胞即青蛙要实现128种情况下的模式识别
*
*/
public class ThreeInputJudge extends DefaultEnvObject {
}

View File

@ -0,0 +1,13 @@
package com.gitee.drinkjava2.frog.objects;
import com.gitee.drinkjava2.frog.objects.EnvObject.DefaultEnvObject;
/**
*
* TwoInputJudge 用来模拟当有两个视细胞一个甜细胞一个咬细胞这种情况下 判断青蛙能否根据甜味细胞的训练在下次相同视信号出现时提前咬下去
* 因为只有两个视细胞即青蛙要实现与异或这三种模式识别
*
*/
public class TwoInputJudge extends DefaultEnvObject {
}

View File

@ -1,201 +0,0 @@
/*
* Copyright 2018 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
* applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
* OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*/
package com.gitee.drinkjava2.frog.temp;
/**
*
* 临时类待删测试一个细胞有三个输入并且每个输入是双通道奖罚情况下是否可以实现所有的模式识别
* 处理逻辑为 (信号x正权重)取1为饱和值 - 信号x负权重当结果大于0.5时输出1 这个逻辑是模拟网上找到的大脑只需单个神经元就可进行XOR异或运算一文
* 原理只不过这里扩展到三个或以上)输入的情况
*
* 三个输入有8种排列组合如一组参数能实现任意8种组合则有2的8次方=256种排列组合去除全为0的输入必须输出0只计算有1信号的输入则有2的7次方=128种组合
* 经实测有31种组合条件不不能找到符合要求的正负权重
*
* 4个通道有16种排列组合如一组参数能实现任意16种组合则有2的16次方=65536种排列组合去除全为0的输入则有2的15次方32768种组合
* 经实测32768组合中有29987种不能找到符合要求的正负权重如只测前1024个组合则有603种情况不能找到解如只测前128组合则有31种情况下找不到解
*
*
*
*/
@SuppressWarnings("all")
public class TestInput3 {
// public static void main(String[] args) {
// testInput3();
// //testInput4();
// }
public static void testInput3() { //这里测试一个细胞有三个树突输入每个输入有正负两种信号且信号以1为饱和测试结果发现有31种情况无法找到解
long notFoundCont = 0;
boolean pass = false;
float p = 0.1f;
for (int i = 0; i < 128; i++) {
found: //
for (float a = 0; a < 1.001; a += p) {
for (float b = 0; b < 1.001; b += p) {
for (float c = 0; c < 1.001; c += p) {
for (float d = 0; d < 1.001; d += p) {
for (float e = 0; e < 1.001; e += p) {
for (float f = 0; f < 1.001; f += p) {
pass = true;
int bt = 1;
int x, y, z;
for (int n = 1; n <= 7; n++) {
x = ((n & 4) > 0) ? 1 : 0;
y = ((n & 2) > 0) ? 1 : 0;
z = ((n & 1) > 0) ? 1 : 0;
int shouldbe = (i & bt) > 0 ? 1 : 0;
float v = x * a + y * b + z * c; //正信号累加
if (v > 1) //如饱和取1
v = 1f;
float v1 = x * d + y * e + z * f; //负信号累加
if (v1 > 1)
v1 = 1f;
v = v - v1;
int result = v >= 0.5 ? 1 : 0;
if (result != shouldbe) {
pass = false;
break;
}
bt = bt << 1;
}
if (pass) {
System.out.print("i=" + i + " found, i=" + bin(i));
System.out.println(" " + r(a) + ", " + r(b) + ", " + r(c) + " " + r(d) + ", " + r(e) + ", " + r(f));
bt = 1;
for (int n = 1; n <= 7 && false; n++) {
x = ((n & 4) > 0) ? 1 : 0;
y = ((n & 2) > 0) ? 1 : 0;
z = ((n & 1) > 0) ? 1 : 0;
int shouldbe = (i & bt) > 0 ? 1 : 0;
System.out.println(" " + x + "*" + r(a) + " + " + y + "*" + r(b) + " + " + z + "*" + r(c) + " - " + x + "*" + r(d) + " - " + y + "*" + r(e) + " - " + z
+ "*" + r(f) + " = " + shouldbe);
bt = bt << 1;
}
break found;
}
}
}
}
}
}
}
if (!pass) {
System.out.println("i=" + i + " not found, i=" + bin(i));
notFoundCont++;
}
}
System.out.println("notFoundCont=" + notFoundCont);
}
public static void testInput4() {//这里测试一个细胞有4个树突输入每个输入有正负两种信号且信号以1为饱和测试结果发现有603种情况无法找到解
long notFoundCont = 0;
boolean pass = false;
float p = 0.2f;
for (int i = 0; i < 1024; i++) {
found: //
for (float a = 0; a < 1.001; a += p) {
for (float b = 0; b < 1.001; b += p) {
for (float c = 0; c < 1.001; c += p) {
for (float d = 0; d < 1.001; d += p) {
for (float e = 0; e < 1.001; e += p) {
for (float f = 0; f < 1.001; f += p) {
for (float g = 0; g < 1.001; g += p) {
for (float h = 0; h < 1.001; h += p) {
pass = true;
int bt = 1;
int x, y, z, m;
for (int n = 1; n <= 15; n++) {
x = ((n & 8) > 0) ? 1 : 0;
y = ((n & 4) > 0) ? 1 : 0;
z = ((n & 2) > 0) ? 1 : 0;
m = ((n & 1) > 0) ? 1 : 0;
int shouldbe = (i & bt) > 0 ? 1 : 0;
float v = x * a + y * b + z * c + m * d; //正信号累加
if (v > 1) //如饱和取1
v = 1f;
float v1 = x * e + y * f + z * g + m * h; //负信号累加
if (v1 > 1)
v1 = 1f;
v = v - v1;
int result = v >= 0.5 ? 1 : 0;
if (result != shouldbe) {
pass = false;
break;
}
bt = bt << 1;
}
if (pass) {
System.out.print("i=" + i + " found, i=" + bin(i));
System.out.println(" " + r(a) + ", " + r(b) + ", " + r(c) + ", " + r(d) + " " + r(e) + ", " + r(f) + ", " + r(g) + ", " + r(h));
bt = 1;
for (int n = 1; n <= 15; n++) {
x = ((n & 8) > 0) ? 1 : 0;
y = ((n & 4) > 0) ? 1 : 0;
z = ((n & 2) > 0) ? 1 : 0;
m = ((n & 1) > 0) ? 1 : 0;
int shouldbe = (i & bt) > 0 ? 1 : 0;
System.out.println(" " + x + "*" + r(a) + " + " + y + "*" + r(b) + " + " + z + "*" + r(c) + " + " + m + "*" + r(d) //
+ " - " + x + "*" + r(e) + " - " + y + "*" + r(f) + " - " + z + "*" + r(g) + " - " + m + "*" + r(h) + " = " + shouldbe);
bt = bt << 1;
}
break found;
}
}
}
}
}
}
}
}
}
if (!pass) {
System.out.println("i=" + i + " not found, i=" + bin(i));
notFoundCont++;
}
}
System.out.println("notFoundCont=" + notFoundCont);
}
static float r(float f) { //取小数后2位
return Math.round(f * 100) * 1.0f / 100;
}
static String bin(int i) { //转二进制
String ibin = Integer.toBinaryString(i);
while (ibin.length() < 7)
ibin = "0" + ibin;
return ibin;
}
}

View File

@ -33,7 +33,7 @@ import java.util.concurrent.BlockingQueue;
*/
@SuppressWarnings("all")
public class Logger {
private static final int SYSTEM_OUT_PRINT = 0; //如设为1不使用log直接System.out.print输出
private static final int SYSTEM_OUT_PRINT = 1; //如设为1不使用log直接System.out.print输出
private static final int LOGGER_STYLE = 0; //风格设定 0:不输出前缀, 1:输出时间行号等前缀
private static final String LEV_EL = "DEBUG";
private static final int LEVEL_INT;

View File

@ -41,6 +41,12 @@ public class RandomUtils {
public static float nextFloat() {
return rand.nextFloat();
}
public static float nextNegOrPosFloat() {
if (percent(50))
return rand.nextFloat();
return -rand.nextFloat();
}
public static boolean percent(float percent) {// 有百分这percent的机率为true
return rand.nextFloat() * 100 < percent;

View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,19 @@
## core目录简介
core目录是当前工作目录如果跑出什么结果就会拷贝一份放到history目录里存档。
当前目标是大方向是由遗传算法来自动排列脑细胞和触突参数,以实现模式识别功能,并与上下左右运动细胞、进食奖罚感觉细胞结合起来,实现吃掉无毒蘑菇,避开有毒蘑菇这个任务。(未完成)
当前小目标:
1)是要利用阴阳8叉树分裂算法进化出第一个可以工作向食物运动的神经网络。(已完成, 见011_yinyan_eatfood)
2)利用阴阳8叉树或4叉树分裂算法(见012_tree4)来进化出具备模式识别功能的神经网络。即实现图像到声音的关联比如对应1,2,3,4 数字的图像会反同激活训练时对应的声音细胞(未完成)
3)简单时序信号的模式识别。比如AB后是C, AD后是E, ABC后是F多次重复后即可形成时序信号的预测关联。功能类似RNN但用分裂算法来实现参数自动生成。(未完成)
以上2和3识别原理类似都建立在细胞连接的遗忘曲线基础上但是一个强调细胞的空间位置关系另一个强调信号留存的时间
当前小小目标:
进化出具备简单模式识别功能的神经网络,即实现图像到咬动作的关联。
目前版本(16a)用遗传算法实现了只有一个点输入信号输入时,有信号则咬细胞激活,无信号则咬细胞停止激活,这是个最简单的逻辑。
下面的目标是二点输入、三点输入、2x2输入、3x3输入..., (当模式识别做到5x5以上后可能分辨率就会越来越难做上去了要考虑引入变焦细胞和动眼细胞了将总体轮廓识别和局部关键特征识别结合起来提高分辨能力)

View File

@ -0,0 +1 @@
mvn clean

View File

@ -0,0 +1 @@
mvn eclipse:clean

View File

@ -0,0 +1,2 @@
call mvn eclipse:eclipse
call pause

View File

@ -0,0 +1,102 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.gitee.drinkjava2</groupId>
<artifactId>frog016a</artifactId>
<packaging>jar</packaging>
<version>16a.0</version>
<name>frog</name>
<description>当前目标是用分裂算法来实现第一个具备模式识别功能的神经网络</description>
<url>https://gitee.com/drinkjava2/frog</url>
<issueManagement>
<system>gitee Issue</system>
<url>https://gitee.com/drinkjava2/frog/issues</url>
</issueManagement>
<licenses>
<license>
<name>The Apache Software License, Version 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
</license>
</licenses>
<developers>
<developer>
<name>Yong Zhu</name>
<email>yong9981@gmail.com</email>
<url>https://gitee.com/drinkjava2/</url>
</developer>
</developers>
<scm>
<connection>scm:git@gitee.com:drinkjava2/frog.git</connection>
<developerConnection>scm:git@gitee.com:drinkjava2/frog.git</developerConnection>
<url>git@gitee.com:drinkjava2/frog.git</url>
</scm>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
<!-- Release on Java8 -->
<version.java>1.8</version.java>
<version.javadoc>6</version.javadoc>
<version.compiler-plugin>3.3</version.compiler-plugin>
<version.war-plugin>2.6</version.war-plugin>
<version.clean-plugin>3.0.0</version.clean-plugin>
<version.resources-plugin>2.7</version.resources-plugin>
<version.surefire-plugin>2.19</version.surefire-plugin>
<version.jar-plugin>2.6</version.jar-plugin>
<version.source-plugin>2.4</version.source-plugin>
<version.javadoc-plugin>2.10.3</version.javadoc-plugin>
<version.gpg-plugin>1.6</version.gpg-plugin>
</properties>
<dependencies>
<!--dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency-->
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${version.compiler-plugin}</version>
<configuration>
<source>${version.java}</source>
<target>${version.java}</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.1.2</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<useUniqueVersions>false</useUniqueVersions>
<classpathPrefix>lib/</classpathPrefix>
<mainClass>com.gitee.drinkjava2.frog.Application</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
</profiles>
</project>

View File

@ -0,0 +1,3 @@
call mvn clean compile
cd target\classes
java -classpath ".;*" com.gitee.drinkjava2.frog.Application

View File

@ -0,0 +1,2 @@
mvn clean package
java -jar target/frog-*.jar

View File

@ -0,0 +1,239 @@
/*
* Copyright 2018 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
* applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
* OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*/
package com.gitee.drinkjava2.frog;
import static com.gitee.drinkjava2.frog.brain.Genes.GENE_NUMBERS;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.FileInputStream;
import java.util.ArrayList;
import javax.imageio.ImageIO;
import com.gitee.drinkjava2.frog.brain.Genes;
import com.gitee.drinkjava2.frog.brain.Line;
import com.gitee.drinkjava2.frog.egg.Egg;
import com.gitee.drinkjava2.frog.objects.Material;
import com.gitee.drinkjava2.frog.util.GeneUtils;
import com.gitee.drinkjava2.frog.util.RandomUtils;
/**
* Animal is all artificial lives' father class
* Animal only keep one copy of genes from egg, not store gene in cell
* Animal是所有动物青蛙蛇等的父类, animal是由蛋孵出来的蛋里保存着脑细胞结构生成的基因, Animal只保存一份基因而不是每个细胞都保存一份基因这是人工生命与实际生物的最大不同
* 基因是一个list<list>结构, 每一条list代表一条由深度树方式存储的基因树分表控制细胞的一个参数当cell用长整数表示时最多可以表达支持64个参数
*
*
* @author Yong Zhu
* @since 1.0
*/
public abstract class Animal {// 这个程序大量用到public变量而不是getter/setter主要是为了编程方便和简洁但缺点是编程者需要小心维护各个变量
public static BufferedImage FROG_IMAGE;
static {
try {
FROG_IMAGE = ImageIO.read(new FileInputStream(Application.CLASSPATH + "frog.png"));
} catch (Exception e) {
e.printStackTrace();
}
}
public ArrayList<ArrayList<Integer>> genes = new ArrayList<>(); // 基因是多个数列有点象多条染色体每个数列都代表一个基因的分裂次序(8叉/4叉/2叉)
public static int CountsQTY = 100; //常量总数多少
public float[] consts = new float[CountsQTY]; // 常量范围0~1之间这些常量并不常会参与遗传算法筛选规则是有大概率小变异小概率大变异
public ArrayList<int[]> lines = new ArrayList<>();
/** brain cells每个细胞对应一个神经元long是64位所以目前一个细胞只能允许最多64个基因64个基因有些是8叉分裂有些是4叉分裂
* 如果今后要扩充到超过64个基因限制可以定义多个三维数组同一个细胞由多个三维数组相同坐标位置的基因共同表达
*/
public long[][][] cells = new long[Env.BRAIN_SIZE][Env.BRAIN_SIZE][Env.BRAIN_SIZE]; //所有脑细胞
public float[][][] energys = new float[Env.BRAIN_SIZE][Env.BRAIN_SIZE][Env.BRAIN_SIZE]; //每个细胞的能量值细胞能量不参与打分打分是由fat变量承担
public int xPos; // animal在Env中的x坐标
public int yPos; // animal在Env中的y坐标
public long fat = 1000000000; // 青蛙的肥胖度, 只有胖的青蛙才允许下蛋, 以前版本这个变量名为energy为了不和脑细胞的能量重名从这个版本起改名为fat
public boolean alive = true; // 设为false表示青蛙死掉了将不参与计算和显示以节省时间
public int ateFood = 0; // 青蛙曾吃过的食物总数
public int ateWrong = 0; // 青蛙咬了个空气的次数
public int ateMiss = 0; // 该咬却没咬下去的次数
public int no; // 青蛙在Env.animals中的序号从1开始 会在运行期写到当前brick的最低位可利用Env.animals.get(no-1)快速定位青蛙
public int animalMaterial;
public Image animalImage;
public Animal(Egg egg) {//构造方法Animal从蛋中诞生
System.arraycopy(egg.consts, 0, this.consts, 0, consts.length);//从蛋中拷一份全局参数
for (int i = 0; i < GENE_NUMBERS; i++) {
genes.add(new ArrayList<>());
}
int i = 0;
for (ArrayList<Integer> gene : egg.genes)//动物的基因是蛋的基因的拷贝
genes.get(i++).addAll(gene);
lines.addAll(egg.lines); //复制蛋的所有线条
i = 0;
if (Env.BORN_AT_RANDOM_PLACE) { //是否随机出生在地图上?
xPos = RandomUtils.nextInt(Env.ENV_WIDTH);
yPos = RandomUtils.nextInt(Env.ENV_HEIGHT);
} else {//否则出生成指定区域
this.xPos = egg.x + RandomUtils.nextInt(80) - 40;
this.yPos = egg.y + RandomUtils.nextInt(80) - 40;
if (this.xPos < 0)
this.xPos = 0;
if (this.yPos < 0)
this.yPos = 0;
if (this.xPos >= (Env.ENV_WIDTH - 1))
this.xPos = Env.ENV_WIDTH - 1;
if (this.yPos >= (Env.ENV_HEIGHT - 1))
this.yPos = Env.ENV_HEIGHT - 1;
}
}
public void initAnimal() { // 初始化animal,生成脑细胞是在这一步这个方法是在当前屏animal生成之后调用比方说有一千个青蛙分为500屏测试每屏只生成2个青蛙的脑细胞可以节约内存
constMutate();//常量基因突变, 线条的参数都在常量里
Line.randomAddorRemoveLine(this);// //Line的随机增删变异
GeneUtils.geneMutation(this); //分裂算法控制的基因突变
if (RandomUtils.percent(5))
for (ArrayList<Integer> gene : genes) //基因多也要适当小扣点分防止基因无限增长
fat -= gene.size();
if (RandomUtils.percent(3)) //线条多也小扣点分防止线条无限增长
fat -= lines.size() / 10;
GeneUtils.createCellsFromGene(this); //根据基因分裂生成脑细胞
}
public boolean active(int step) {// 这个active方法在每一步循环都会被调用是脑思考的最小帧step是当前屏的帧数
// 如果fat小于0出界与非食物的点重合则判死
if (!alive) {
return false;
}
if (fat <= 0 || Env.outsideEnv(xPos, yPos) || Env.bricks[xPos][yPos] >= Material.KILL_ANIMAL) {
kill();
return false;
}
this.setEng(Genes.ACT_POS, 1f); //ACT这个细胞就象太阳永远保持激活某些情况下当无外界信号时它是驱动系统运行的能量来源
Genes.active(this, step); //调用每个细胞的活动重要
Line.active(this, step); //调用每个连线(触突)的活动重要
return alive;
}
public void constMutate() { // 全局参数变异, 这一个方法此动物个体的所有常量
if (RandomUtils.percent(30)) //这个30%机率的变异方法让所有常量都有3%的机率随机在0~1之间重新取值
for (int i = 0; i < CountsQTY; i++) {
if (RandomUtils.percent(3))
consts[i] = RandomUtils.nextFloat(); //debug
}
if (RandomUtils.percent(10)) //这个10%机率的变异方法让所有常量都有5%的机率随机在原基础上变异即大概率有小变异小概率有大变异
for (int i = 0; i < CountsQTY; i++) {
if (RandomUtils.percent(5))
consts[i] = RandomUtils.vary(consts[i]);
}
}
private static final int MIN_FAT_LIMIT = Integer.MIN_VALUE + 5000;
private static final int MAX_FAT_LIMIT = Integer.MAX_VALUE - 5000;
//@formatter:off 下面几行是重要的奖罚方法会经常调整或注释掉集中放在一起不要格式化为多行
public void changeFat(int fat_) {//正数为奖励负数为惩罚 fat值是环境对animal唯一的奖罚也是animal唯一的下蛋竞争标准
fat += fat_;
if (fat > MAX_FAT_LIMIT)
fat = MAX_FAT_LIMIT;
if (fat < MIN_FAT_LIMIT)
fat = MIN_FAT_LIMIT;
}
//没定各个等级的奖罚值目前是手工设定的常数
public void awardAAAA() { changeFat(2000);}
public void awardAAA() { changeFat(1000);}
public void awardAA() { changeFat(100);}
public void awardA() { changeFat(10);}
public void penaltyAAAA() { changeFat(-2000);}
public void penaltyAAA() { changeFat(-1000);}
public void penaltyAA() { changeFat(-100);}
public void penaltyA() { changeFat(-10);}
public void kill() { this.alive = false; changeFat(-5000000); Env.clearMaterial(xPos, yPos, animalMaterial); } //kill是最大的惩罚
//@formatter:on
public void showInEnv(Graphics g) {// 在虚拟环境显示当前动物这个方法直接调用Env的Graphics对象
if (g != null) //这个版本借用环境区测试模式功能不需要显示青蛙所以直接跳出
return;
if (no == (Env.current_screen * Env.FROG_PER_SCREEN + 1)) { //如果是当前第一个青蛙给它画个红圈
Color c = g.getColor();
g.setColor(Color.red);
g.drawArc(xPos - 15, yPos - 15, 30, 30, 0, 360);
g.setColor(c);
}
if (!alive)
return;
g.drawImage(animalImage, xPos - 8, yPos - 8, 16, 16, null);// 减去坐标保证嘴巴显示在当前x,y处
}
/** Check if x,y,z out of animal's brain range */
public static boolean outBrainRange(int x, int y, int z) {// 检查指定坐标是否超出animal脑空间界限
return x < 0 || x >= Env.BRAIN_SIZE || y < 0 || y >= Env.BRAIN_SIZE || z < 0 || z >= Env.BRAIN_SIZE;
}
public boolean hasGene(int x, int y, int z, long geneMask) { //判断cell是否含某个基因
return (cells[x][y][z] & geneMask) > 0;
}
public boolean hasGene(int x, int y, int z) { //判断cell是否含任一基因
return cells[x][y][z] > 0;
}
public void setEng(int x, int y, int z, float e) { //打开指定的xyz坐标对应的cell能量值为极大
if (e > 1)
e = 1;
if (e < 0)
e = 0;
energys[x][y][z] = e;
}
public void setEng(int[] a, float e) { //打开指定的xyz坐标对应的cell能量值为极大
if (e > 1)
e = 1;
if (e < 0)
e = 0;
energys[a[0]][a[1]][a[2]] = e;
}
public void addEng(int[] a, float e) {//指定的a坐标对应的cell能量值加e
addEng(a[0], a[1], a[2], e);
}
public void addEng(int x, int y, int z, float e) {//指定的a坐标对应的cell能量值加e
if (cells[x][y][z] == 0)
return;
float eng = energys[x][y][z] + e;
if (eng > 1) //如果饱和不再增加通过这个方法可以实现异或逻辑或更复杂的模式识别详见TestInput3测试
eng = 1;
if (eng < 0) //回到传统方式细胞不允许出现负能量但是能量可以出现负值这个与实际细胞的抑制信号相似
eng = 0;
energys[x][y][z] = eng;
}
public float getEng(int[] a) {//返回指定的a坐标对应的cell能量值
return energys[a[0]][a[1]][a[2]];
}
public float getEng(int x, int y, int z) {//返回指定的a坐标对应的cell能量值
return energys[x][y][z];
}
}

View File

@ -0,0 +1,178 @@
package com.gitee.drinkjava2.frog;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JRadioButton;
import javax.swing.JSlider;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import com.gitee.drinkjava2.frog.brain.BrainPicture;
import com.gitee.drinkjava2.frog.brain.Genes;
/**
* Application's main method start the program
* Application是程序入口
*
* 关于本项目代码格式化工具的约定:
* 1.不使用tab而是使用空白符
* 2.源码采用UTF-8编码
* 3.源码换行设定为UNIX风格单个LF字符结尾 git里设定git config --global core.autocrlf input即提交时把CRLF改成单个LF字符签出时不改
* 4.所有注释内容不允许被格式化
* Never join lines 设为true不允许自动合并注释行
* Enable Javadoc comment formatting 设为false
* Enable block comment formatting 设为false
* Enable line comment formatting 设为false
* 5.其他人提交时只能修改自已修改的部分不要随便使用代码格式化工具如果要使用代码格式化工具也必须参照以上内容设置成不能变动未修改的其它行
*
* @author Yong Zhu
* @since 1.0
*/
@SuppressWarnings("all")
public class Application {
public static final String CLASSPATH;
static {
String classpath = new File("").getAbsolutePath();
int i = classpath.lastIndexOf("\\frog\\");
if (i > 0)
CLASSPATH = classpath.substring(0, i) + "\\frog\\";// windows
else
CLASSPATH = classpath + "/"; // UNIX
}
public static JFrame mainFrame = new JFrame();
public static Env env = new Env();
public static BrainPicture brainPic = new BrainPicture(Env.ENV_WIDTH + 5, 0, Env.BRAIN_SIZE, Env.FROG_BRAIN_DISP_WIDTH);
public static ActionListener pauseAction;
public static boolean selectFrog = true;
private static void checkIfShowBrainPicture(JButton button) {
int y = Env.ENV_HEIGHT + 150;
if (Env.SHOW_FIRST_ANIMAL_BRAIN) {
button.setText("Hide brain");
if (Env.FROG_BRAIN_DISP_WIDTH + 41 > y)
y = Env.FROG_BRAIN_DISP_WIDTH + 41;
mainFrame.setSize(Env.ENV_WIDTH + Env.FROG_BRAIN_DISP_WIDTH + 25, y);
brainPic.requestFocus();
} else {
button.setText("Show brain");
mainFrame.setSize(Env.ENV_WIDTH + 20, y);
}
}
public static void main(String[] args) {
mainFrame.setLayout(null);
mainFrame.setSize(Env.ENV_WIDTH + 200, Env.ENV_HEIGHT + 150); // 窗口大小
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 关闭时退出程序
mainFrame.add(env); // 添加虚拟环境Panel
mainFrame.add(brainPic); // 添加脑图Panel
JButton button = new JButton("Show brain");// 按钮显示或隐藏脑图
int buttonWidth = 100;
int buttonHeight = 22;
int buttonXpos = Env.ENV_WIDTH / 2 - buttonWidth / 2;
button.setBounds(buttonXpos, Env.ENV_HEIGHT + 8, buttonWidth, buttonHeight);
ActionListener al = new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {// 显示或隐藏脑图
Env.SHOW_FIRST_ANIMAL_BRAIN = !Env.SHOW_FIRST_ANIMAL_BRAIN;
checkIfShowBrainPicture(button);
}
};
checkIfShowBrainPicture(button);
button.addActionListener(al);
mainFrame.add(button);
JButton stopButton = new JButton("Pause");// 暂停或继续按钮
stopButton.setBounds(buttonXpos, Env.ENV_HEIGHT + 35, buttonWidth, buttonHeight);
pauseAction = new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
Env.pause = !Env.pause;
if (Env.pause) {
stopButton.setText("Resume");
} else {
stopButton.setText("Pause");
brainPic.requestFocus();
}
}
};
stopButton.addActionListener(pauseAction);
mainFrame.add(stopButton);
// 速度条
final JSlider speedSlider = new JSlider(1, 10, (int) Math.round(Math.pow(Env.SHOW_SPEED, 1.0 / 3)));
speedSlider.setBounds(buttonXpos - 50, stopButton.getY() + 25, buttonWidth + 100, buttonHeight);
ChangeListener slideAction = new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
Env.SHOW_SPEED = speedSlider.getValue() * speedSlider.getValue() * speedSlider.getValue();
brainPic.requestFocus();
}
};
speedSlider.addChangeListener(slideAction);
mainFrame.add(speedSlider);
final JLabel label = new JLabel("Speed:");
label.setBounds(buttonXpos - 90, stopButton.getY() + 23, 100, buttonHeight);
mainFrame.add(label);
// 是否把egg文件存盘
JCheckBox saveFileCheckBox = new JCheckBox("Save egg file");
saveFileCheckBox.setBounds(buttonXpos, Env.ENV_HEIGHT + 80, 120, 22);
ActionListener saveAction = new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (saveFileCheckBox.isSelected())
Env.SAVE_EGGS_FILE = true;
else
Env.SAVE_EGGS_FILE = false;
}
};
saveFileCheckBox.addActionListener(saveAction);
mainFrame.add(saveFileCheckBox);
// 基因维数显示控制
for (int i = 0; i < Genes.GENE_NUMBERS; i++) {
JRadioButton geneRadio = new JRadioButton();
geneRadio.setBounds(buttonXpos + 300 + i * 16, Env.ENV_HEIGHT + 8, 20, 22);
geneRadio.setSelected(Genes.display_gene[i]);
geneRadio.setName("" + i);
ActionListener geneRadioAction = new ActionListener() {
public void actionPerformed(ActionEvent e) {
int i = Integer.parseInt(geneRadio.getName());
if (geneRadio.isSelected())
Genes.display_gene[i] = true;
else
Genes.display_gene[i] = false;
}
};
geneRadio.addActionListener(geneRadioAction);
mainFrame.add(geneRadio);
}
// 是否显示分裂过程
JCheckBox showSplitDetailCheckBox = new JCheckBox("Show split detail");
showSplitDetailCheckBox.setBounds(buttonXpos + 300, Env.ENV_HEIGHT + 40, 120, 22);
ActionListener detailAction = new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (showSplitDetailCheckBox.isSelected())
Env.show_split_detail = true;
else
Env.show_split_detail = false;
}
};
showSplitDetailCheckBox.addActionListener(detailAction);
mainFrame.add(showSplitDetailCheckBox);
//mainFrame.setBounds(0, 400, 5, 5);
mainFrame.setVisible(true);
env.run();
}
}

View File

@ -0,0 +1,298 @@
package com.gitee.drinkjava2.frog;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.text.NumberFormat;
import java.util.ArrayList;
import javax.swing.JPanel;
import com.gitee.drinkjava2.frog.egg.Egg;
import com.gitee.drinkjava2.frog.egg.FrogEggTool;
import com.gitee.drinkjava2.frog.objects.EnvObject;
import com.gitee.drinkjava2.frog.objects.Food;
import com.gitee.drinkjava2.frog.objects.Material;
import com.gitee.drinkjava2.frog.objects.OneInputJudge;
import com.gitee.drinkjava2.frog.util.Logger;
import com.gitee.drinkjava2.frog.util.RandomUtils;
/**
* Env is the living space of frog. draw it on JPanel
*
* @author Yong Zhu
* @since 1.0
*/
@SuppressWarnings("all")
public class Env extends JPanel {
private static final long serialVersionUID = 1L;
/** Speed of test */
public static int SHOW_SPEED = 1000; // 测试速度-1000~1000,可调, 数值越小速度越慢
public static final int FROG_EGG_QTY = 1000; // 每轮下n个青蛙蛋可调只有最优秀的前n个青蛙们才允许下蛋
public static final int FROG_PER_EGG = 6; // 每个青蛙蛋可以孵出几个青蛙
public static final int SCREEN = 3; // 分几屏测完
/** Delete eggs at beginning of each run */
public static final boolean DELETE_FROG_EGGS = true;// 每次运行是否先删除以前保存的青蛙蛋文件如果为false将加载旧蛋文件继续运行
public static boolean SAVE_EGGS_FILE = false; //从2021-11-23起添加这个选项允许不输出蛋文件到磁盘上
public static final boolean BORN_AT_RANDOM_PLACE = true;// 孵出青蛙落在地图上随机位置而不是在蛋所在地
/** Frog's brain size */ // 脑细胞位于脑范围内是个三维结构在animal中用三维数组来表示
public static final int BRAIN_SIZE = 4; //脑立方边长大小必须是2的幂数如4,8,16...原因参见8叉树算法
/** SHOW first animal's brain structure */
public static boolean SHOW_FIRST_ANIMAL_BRAIN = true; // 是否显示脑图在Env区的右侧
/** Draw first frog's brain after some steps */
public static int DRAW_BRAIN_AFTER_STEPS = 0; // 以此值为间隔动态画出脑图设为0则关闭这个动态脑图功能只显示一个静态不闪烁的脑图
/** Environment x width, unit: pixels */
public static final int ENV_WIDTH = 400; // 虚拟环境的宽度, 可调
/** Environment y height, unit: pixels */
public static final int ENV_HEIGHT = ENV_WIDTH; // 虚拟环境高度, 可调通常取正方形
/** Frog's brain display width on screen, not important */
public static final int FROG_BRAIN_DISP_WIDTH = 400; // Frog的脑图在屏幕上的显示大小,可调
/** Steps of one test round */
public static final int STEPS_PER_ROUND = 200;// 每轮测试步数,可调
public static int step;// 当前测试步数
public static final int FOOD_QTY = 3000; // 食物数量, 可调
// 以下是程序内部变量不要手工修改它们
public static final int TOTAL_FROG_QTY = FROG_EGG_QTY * FROG_PER_EGG; // 蛇的总数
public static final int FROG_PER_SCREEN = TOTAL_FROG_QTY / SCREEN; // 每屏显示几个青蛙这个数值由其它常量计算得来
public static int current_screen = 0;
public static boolean pause = false; // 暂停按钮按下将暂停测试
public static int[][] bricks = new int[ENV_WIDTH][ENV_HEIGHT];// 组成环境的材料见Material.java
public static ArrayList<Frog> frogs = new ArrayList<>(); // 这里存放所有待测的青蛙可能分几次测完由FROG_PER_SCREEN大小来决定
public static ArrayList<Egg> frog_eggs = new ArrayList<>(); // 这里存放新建或从磁盘载入上轮下的蛋每个蛋可能生成几个青蛙
public static EnvObject[] things = new EnvObject[] { new OneInputJudge() };// 所有外界物体如食物测试工具都放在这个things里面
public static boolean show_split_detail = false; //是否显示脑分裂的细节过程即从一个细胞开始分裂分裂而不是只显示分裂的最终结果
static {
Logger.info("唵缚悉波罗摩尼莎诃!"); // 杀生前先打印往生咒因为遗传算法建立在杀生选优的基础上用这个方式表示一下对生命的尊重智能研究不光是技术还涉及到伦理对虚拟生命的尊重也是对人类自身的尊重
// 意识不是一种实体存在只是一种表象但正因为此我们才要尊重所有表现出或低级或高级的意识现象的虚拟智能系统包括避免制造不必要的虚拟生命的痛苦感觉现象己所不欲勿施于人
Logger.info("脑图快捷键: T:顶视 F前视 L:左视 R:右视 X:斜视 方向键:剖视 空格:暂停 鼠标:缩放旋转平移");
if (DELETE_FROG_EGGS)
FrogEggTool.deleteEggs();
}
public Env() {
super();
this.setLayout(null);// 空布局
this.setBounds(1, 1, ENV_WIDTH, ENV_HEIGHT);
}
public static boolean insideBrain(int x, int y) {// 如果指定点在边界内
return !(x < 0 || y < 0 || x >= BRAIN_SIZE || y >= BRAIN_SIZE);
}
public static boolean insideBrain(int x, int y, int z) {// 如果指定点在边界内
return !(x < 0 || y < 0 || z < 0 || x >= BRAIN_SIZE || y >= BRAIN_SIZE || z >= BRAIN_SIZE);
}
public static boolean insideBrain(float x, float y, float z) {// 如果指定点在边界内
return !(x < 0 || y < 0 || z < 0 || x >= BRAIN_SIZE || y >= BRAIN_SIZE || z >= BRAIN_SIZE);
}
public static boolean insideEnv(int x, int y) {// 如果指定点在边界内
return !(x < 0 || y < 0 || x >= ENV_WIDTH || y >= ENV_HEIGHT);
}
public static boolean outsideEnv(int x, int y) {// 如果指定点超出边界
return x < 0 || y < 0 || x >= ENV_WIDTH || y >= ENV_HEIGHT;
}
public static boolean closeToEdge(Animal a) {// 靠近边界? 离死不远了
return a.xPos < 20 || a.yPos < 20 || a.xPos > (Env.ENV_WIDTH - 20) || a.yPos > (Env.ENV_HEIGHT - 20);
}
public static boolean foundAnyThingOrOutEdge(int x, int y) {// 如果指定点看到任意东西或超出边界返回true
return x < 0 || y < 0 || x >= ENV_WIDTH || y >= ENV_HEIGHT || Env.bricks[x][y] != 0;
}
public static boolean foundFrogOrOutEdge(int x, int y) {// 如果指定点看到青蛙或超出边界返回true
if (x < 0 || y < 0 || x >= ENV_WIDTH || y >= ENV_HEIGHT)
return true;// 如果出界返回true
if ((Env.bricks[x][y] & Material.FROG_TAG) > 0)
return true;
else
return false;
}
public static void setMaterial(int x, int y, int material) {
if (Env.insideEnv(x, y))
Env.bricks[x][y] = Env.bricks[x][y] | material;
}
public static boolean hasMaterial(int x, int y, int material) {
if (!Env.insideEnv(x, y))
return false;
return (Env.bricks[x][y] & material) > 0;
}
public static void clearMaterial(int x, int y, int material) {
if (Env.insideEnv(x, y))
Env.bricks[x][y] = Env.bricks[x][y] & ~material;
}
private void rebuildFrogs() {// 根据蛙蛋重新孵化出蛙
frogs.clear();
for (int i = 0; i < frog_eggs.size(); i++) {// 创建青蛙每个蛋生成n个蛙并随机取一个别的蛋作为精子
int loop = FROG_PER_EGG;
if (frog_eggs.size() > 20) { // 如果数量多进行一些优化让排名靠前的Egg多孵出青蛙
if (i < FROG_PER_EGG)// 0,1,2,3
loop = FROG_PER_EGG + 1;
if (i >= (frog_eggs.size() - FROG_PER_EGG))
loop = FROG_PER_EGG - 1;
}
for (int j = 0; j < loop; j++) {
Egg zygote = new Egg(frog_eggs.get(i), frog_eggs.get(RandomUtils.nextInt(frog_eggs.size())));
Frog f = new Frog(zygote);
frogs.add(f);
f.no = frogs.size();
}
}
}
private void drawWorld(Graphics g) {
int brick;
for (int x = 0; x < ENV_WIDTH; x++)
for (int y = 0; y < ENV_HEIGHT; y++) {
brick = bricks[x][y];
if (brick != 0) {
g.setColor(Material.color(brick));
if ((brick & Material.FOOD) > 0) {
g.fillRoundRect(x, y, 4, 4, 2, 2); //食物只有一个点太小画大一点
} else
g.drawLine(x, y, x, y); // only 1 point
}
}
g.setColor(Color.BLACK);
}
static final NumberFormat format100 = NumberFormat.getPercentInstance();
static {
format100.setMaximumFractionDigits(2);
}
private String foodAtedCount() {// 统计吃食总数等
int maxFound = 0;
for (Frog f : frogs)
if (f.ateFood > maxFound)
maxFound = f.ateFood;
return new StringBuilder("吃食率:").append(format100.format(Food.food_ated * 1.00 / FOOD_QTY)).append(", 平均: ").append(Food.food_ated * 1.0f / FROG_PER_SCREEN).append(",最多:").append(maxFound).toString();
}
public static void checkIfPause(int step) {
if (pause)
do {
Application.brainPic.drawBrainPicture(step);
Application.brainPic.requestFocus();
sleep(100);
} while (pause);
}
public static void sleep(long millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static Animal getShowAnimal() { //取当前屏第一个青蛙
return frogs.get(current_screen * FROG_PER_SCREEN);
}
public static int round = 1;
public void run() {
FrogEggTool.loadFrogEggs(); // 首次运行时从磁盘加载蛙egg如加载失败就新建一批egg
Image buffImg = createImage(this.getWidth(), this.getHeight());
Graphics g = buffImg.getGraphics();
long time0;// 计时用
round = 1;
do {
rebuildFrogs(); // 根据蛙蛋重新孵化出蛙注意基因变异有可能在孵化过程中发生
for (current_screen = 0; current_screen < SCREEN; current_screen++) {// 分屏测试每屏FROG_PER_SCREEN个蛙
time0 = System.currentTimeMillis();
g.setColor(Color.white);
g.fillRect(0, 0, this.getWidth(), this.getHeight()); // 先清空虚拟环境
g.setColor(Color.BLACK);
for (EnvObject thing : things) // 创建食物陷阱等物体
thing.build(g);
boolean allDead = false;
for (int j = 0; j < FROG_PER_SCREEN; j++) {
Frog f = frogs.get(current_screen * FROG_PER_SCREEN + j);
f.initAnimal(); // 初始化器官延迟到这一步是因为脑细胞太占内存而且当前屏测完后会清空
}
for (step = 0; step < STEPS_PER_ROUND; step++) {
if (allDead)
break; // 青蛙全死光了就直接跳到下一轮,以节省时间
for (EnvObject thing : things)// 调用食物陷阱等物体的动作
thing.active(current_screen, step, g);
allDead = true;
for (int j = 0; j < FROG_PER_SCREEN; j++) {
Frog f = frogs.get(current_screen * FROG_PER_SCREEN + j);
if (f.active(step))// 调用青蛙的Active方法并返回是否还活着
allDead = false;
}
if (SHOW_SPEED == 1) // 如果speed为1人为加入延迟
sleep(400);
else if (step % SHOW_SPEED != 0)// 用是否跳帧画图的方式来控制速度
continue;
// 开始画things和青蛙
drawWorld(g);// 画整个虚拟环境中的material
for (int j = 0; j < FROG_PER_SCREEN; j++) { // 显示青蛙
Frog f = frogs.get(current_screen * FROG_PER_SCREEN + j);
f.showInEnv(g);
}
if (DRAW_BRAIN_AFTER_STEPS > 0 && step % DRAW_BRAIN_AFTER_STEPS == 0) //显示脑图是耗时操作这个开关可以跳过一些脑图显示
Application.brainPic.drawBrainPicture(step);
if (SHOW_SPEED == 1 && SHOW_FIRST_ANIMAL_BRAIN) //如果速度为1强制每步都显示脑图
Application.brainPic.drawBrainPicture(step);
Graphics g2 = this.getGraphics();
g2.drawImage(buffImg, 0, 0, this);
}
if (SHOW_FIRST_ANIMAL_BRAIN) //一轮结束后再强制再显示脑图一次
Application.brainPic.drawBrainPicture(step);
checkIfPause(step);
StringBuilder sb = new StringBuilder("Round: ");
sb.append(round).append(", screen:").append(current_screen).append(", speed:").append(Env.SHOW_SPEED).append(", ").append(", 用时: ").append(System.currentTimeMillis() - time0).append("ms, ");
sb.append(foodAtedCount());
Application.mainFrame.setTitle(sb.toString());
for (EnvObject thing : things)// 去除食物陷阱等物体
thing.destory(g);
}
round++;
FrogEggTool.layEggs(); //能量高的青蛙才有权下蛋
} while (true);
}
}

View File

@ -0,0 +1,29 @@
/*
* Copyright 2018 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
* applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
* OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*/
package com.gitee.drinkjava2.frog;
import com.gitee.drinkjava2.frog.egg.Egg;
import com.gitee.drinkjava2.frog.objects.Material;
/**
* Frog is child class of Animal, Frog's name is Sam
* Frog是Animal的一个子类
*
* @since 1.0
*/
public class Frog extends Animal {
public Frog(Egg egg) {
super(egg);
animalMaterial = Material.FROG_TAG;
animalImage = Animal.FROG_IMAGE;
}
}

View File

@ -0,0 +1,496 @@
package com.gitee.drinkjava2.frog.brain;
import static java.awt.Color.BLACK;
import static java.awt.Color.RED;
import static java.awt.Color.WHITE;
import static java.lang.Math.cos;
import static java.lang.Math.round;
import static java.lang.Math.sin;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import javax.swing.JPanel;
import com.gitee.drinkjava2.frog.Animal;
import com.gitee.drinkjava2.frog.Application;
import com.gitee.drinkjava2.frog.Env;
import com.gitee.drinkjava2.frog.util.ColorUtils;
import com.gitee.drinkjava2.frog.util.Tree8Util;
/**
* BrainPicture show first frog's brain structure, for debug purpose only
*
* 这个类用来画出脑图这不是一个关键类对脑的运行逻辑无影响但有了脑图后可以直观地看出脑的3维结构进行有针对性的改进
* 可以用鼠标进行平移缩放旋转以及tflr,x五个键来选择顶视前视左视右视斜视这5个方向的视图以及空格暂停方向键调整切面
* 鼠标的动作定义在MouseAction类中
*
* @author Yong Zhu
* @since 1.0
*/
@SuppressWarnings("all")
public class BrainPicture extends JPanel {
private static final long serialVersionUID = 1L;
private static final float D90 = (float) (Math.PI / 2);
Color picColor = RED;
int brainDispWidth; // screen display piexls width
float scale; // brain scale
int xOffset = 0; // brain display x offset compare to screen
int yOffset = 0; // brain display y offset compare to screen
float xAngle = D90 * .8f; // brain rotate on x axis
float yAngle = D90 / 4; // brain rotate on y axis
float zAngle = 0;// brain rotate on z axis
int xMask = -1;// x Mask
int yMask = -1;// y Mask
BufferedImage buffImg;
Graphics g;
String note;
public KeyAdapter keyAdapter;
public BrainPicture(int x, int y, float brainWidth, int brainDispWidth) {
super();
this.setLayout(null);// 空布局
this.brainDispWidth = brainDispWidth;
scale = 0.5f * brainDispWidth / brainWidth;
this.setBounds(x, y, brainDispWidth + 1, brainDispWidth + 1);
buffImg = new BufferedImage(Env.FROG_BRAIN_DISP_WIDTH, Env.FROG_BRAIN_DISP_WIDTH, BufferedImage.TYPE_INT_RGB);
g = buffImg.getGraphics();
MouseAction act = new MouseAction(this);
this.addMouseListener(act); // 添加鼠标动作监听
this.addMouseWheelListener(act);// 添加鼠标滚轮动作监听
this.addMouseMotionListener(act);// 添加鼠标移动动作监听
keyAdapter = new KeyAdapter() {// 处理t,f,l,rx键盘命令
@Override
public void keyPressed(KeyEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_UP:// Y切面向上
yMask++;
if (yMask > Env.BRAIN_SIZE)
yMask = Env.BRAIN_SIZE;
break;
case KeyEvent.VK_DOWN:// Y切面向下
yMask--;
if (yMask < 0)
yMask = 0;
break;
case KeyEvent.VK_LEFT:// x切面向左
xMask--;
if (xMask < 0)
xMask = 0;
break;
case KeyEvent.VK_RIGHT:// x切面向右
xMask++;
if (xMask > Env.BRAIN_SIZE)
xMask = Env.BRAIN_SIZE;
break;
case ' ':// 暂停及继续
Application.pauseAction.actionPerformed(null);
break;
case 'T':// 顶视
xAngle = 0;
yAngle = 0;
zAngle = 0;
break;
case 'F':// 前视
xAngle = D90;
yAngle = 0;
zAngle = 0;
break;
case 'L':// 左视
xAngle = D90;
yAngle = D90;
zAngle = 0;
break;
case 'R':// 右视
xAngle = D90;
yAngle = -D90;
zAngle = 0;
break;
case 'X':// 斜视
xAngle = D90 * .8f;
yAngle = D90 / 4;
zAngle = 0;
break;
default:
}
}
};
addKeyListener(keyAdapter);
this.setFocusable(true);
}
public void drawCuboid(float x, float y, float z, float xe, float ye, float ze) {// 在脑图上画一个长立方体框架视角是TopView
drawLine(x, y, z, x + xe, y, z);// 画立方体的下面边
drawLine(x + xe, y, z, x + xe, y + ye, z);
drawLine(x + xe, y + ye, z, x, y + ye, z);
drawLine(x, y + ye, z, x, y, z);
drawLine(x, y, z, x, y, z + ze);// 画立方体的中间边
drawLine(x + xe, y, z, x + xe, y, z + ze);
drawLine(x + xe, y + ye, z, x + xe, y + ye, z + ze);
drawLine(x, y + ye, z, x, y + ye, z + ze);
drawLine(x, y, z + ze, x + xe, y, z + ze);// 画立方体的上面边
drawLine(x + xe, y, z + ze, x + xe, y + ye, z + ze);
drawLine(x + xe, y + ye, z + ze, x, y + ye, z + ze);
drawLine(x, y + ye, z + ze, x, y, z + ze);
}
public void drawCentLine(float px1, float py1, float pz1, float px2, float py2, float pz2) {// 从细胞中点之间画一条线
drawLine(px1 + 0.5f, py1 + 0.5f, pz1 + 0.5f, px2 + 0.5f, py2 + 0.5f, pz2 + 0.5f);
}
/*-
画线固定以top视角的角度所以只需要从x1,y1画一条到x2,y2的直线
x 轴旋转 θ
x, y.cosθ-zsinθ, y.sinθ+z.cosθ
y 轴旋转 θ
z.sinθ+x.cosθ, y, z.cosθ-x.sinθ
z 轴旋转 θ
x.cosθ-y.sinθ, x.sinθ+y.consθ, z
-*/
public void drawLine(float px1, float py1, float pz1, float px2, float py2, float pz2) {
double x1 = px1 - Env.BRAIN_SIZE / 2;
double y1 = -py1 + Env.BRAIN_SIZE / 2;// 屏幕的y坐标是反的显示时要正过来
double z1 = pz1 - Env.BRAIN_SIZE / 2;
double x2 = px2 - Env.BRAIN_SIZE / 2;
double y2 = -py2 + Env.BRAIN_SIZE / 2;// 屏幕的y坐标是反的显示时要正过来
double z2 = pz2 - Env.BRAIN_SIZE / 2;
x1 = x1 * scale;
y1 = y1 * scale;
z1 = z1 * scale;
x2 = x2 * scale;
y2 = y2 * scale;
z2 = z2 * scale;
double x, y, z;
y = y1 * cos(xAngle) - z1 * sin(xAngle);// 绕x轴转
z = y1 * sin(xAngle) + z1 * cos(xAngle);
y1 = y;
z1 = z;
x = z1 * sin(yAngle) + x1 * cos(yAngle);// 绕y轴转
// z = z1 * cos(yAngle) - x1 * sin(yAngle);
x1 = x;
// z1 = z;
x = x1 * cos(zAngle) - y1 * sin(zAngle);// 绕z轴转
y = x1 * sin(zAngle) + y1 * cos(zAngle);
x1 = x;
y1 = y;
y = y2 * cos(xAngle) - z2 * sin(xAngle);// 绕x轴转
z = y2 * sin(xAngle) + z2 * cos(xAngle);
y2 = y;
z2 = z;
x = z2 * sin(yAngle) + x2 * cos(yAngle);// 绕y轴转
// z = z2 * cos(yAngle) - x2 * sin(yAngle);
x2 = x;
// z2 = z;
x = x2 * cos(zAngle) - y2 * sin(zAngle);// 绕z轴转
y = x2 * sin(zAngle) + y2 * cos(zAngle);
x2 = x;
y2 = y;
g.setColor(picColor);
g.drawLine((int) round(x1) + Env.FROG_BRAIN_DISP_WIDTH / 2 + xOffset,
(int) round(y1) + Env.FROG_BRAIN_DISP_WIDTH / 2 + yOffset,
(int) round(x2) + Env.FROG_BRAIN_DISP_WIDTH / 2 + xOffset,
(int) round(y2) + Env.FROG_BRAIN_DISP_WIDTH / 2 + yOffset);
}
public void drawPointCent(float px1, float py1, float pz1, float r) {
drawPoint(px1+0.5f, py1+0.5f, pz1+0.5f, r);
}
/** 画点固定以top视角的角度所以只需要在x1,y1位置画一个点 */
public void drawPoint(float px1, float py1, float pz1, float r) {
double x1 = px1 - Env.BRAIN_SIZE / 2;
double y1 = -py1 + Env.BRAIN_SIZE / 2;// 屏幕的y坐标是反的显示时要正过来
double z1 = pz1 - Env.BRAIN_SIZE / 2;
x1 = x1 * scale;
y1 = y1 * scale;
z1 = z1 * scale;
double x, y, z;
y = y1 * cos(xAngle) - z1 * sin(xAngle);// 绕x轴转
z = y1 * sin(xAngle) + z1 * cos(xAngle);
y1 = y;
z1 = z;
x = z1 * sin(yAngle) + x1 * cos(yAngle);// 绕y轴转
// z = z1 * cos(yAngle) - x1 * sin(yAngle);
x1 = x;
// z1 = z;
x = x1 * cos(zAngle) - y1 * sin(zAngle);// 绕z轴转
y = x1 * sin(zAngle) + y1 * cos(zAngle);
x1 = x;
y1 = y;
g.setColor(picColor);
g.fillOval(round((float) x1 + Env.FROG_BRAIN_DISP_WIDTH / 2 + xOffset - r * scale * .5f),
round((float) y1 + Env.FROG_BRAIN_DISP_WIDTH / 2 + yOffset - r * scale * .5f), round(r * scale),
round(r * scale));
}
/** 画一个圆 */
public void drawCircle(float px1, float py1, float pz1, float r) {// 这个方法实际和上面的一样的只是改成了drawOval
double x1 = px1 - Env.BRAIN_SIZE / 2;
double y1 = -py1 + Env.BRAIN_SIZE / 2;// 屏幕的y坐标是反的显示时要正过来
double z1 = pz1 - Env.BRAIN_SIZE / 2;
x1 = x1 * scale;
y1 = y1 * scale;
z1 = z1 * scale;
double x, y, z;
y = y1 * cos(xAngle) - z1 * sin(xAngle);// 绕x轴转
z = y1 * sin(xAngle) + z1 * cos(xAngle);
y1 = y;
z1 = z;
x = z1 * sin(yAngle) + x1 * cos(yAngle);// 绕y轴转
// z = z1 * cos(yAngle) - x1 * sin(yAngle);
x1 = x;
// z1 = z;
x = x1 * cos(zAngle) - y1 * sin(zAngle);// 绕z轴转
y = x1 * sin(zAngle) + y1 * cos(zAngle);
x1 = x;
y1 = y;
g.setColor(picColor);
g.drawOval(round((float) x1 + Env.FROG_BRAIN_DISP_WIDTH / 2 + xOffset - r * scale * .5f),
round((float) y1 + Env.FROG_BRAIN_DISP_WIDTH / 2 + yOffset - r * scale * .5f), round(r * scale),
round(r * scale));
}
public void drawTextCenter(float px1, float py1, float pz1, String text, float textSize) {
drawText(px1 + 0.5f, py1 + 0.5f, pz1 + 0.5f, text, textSize);
}
public void drawText(float px1, float py1, float pz1, String text, float textSize) {
double x1 = px1 - Env.BRAIN_SIZE / 2;
double y1 = -py1 + Env.BRAIN_SIZE / 2;// 屏幕的y坐标是反的显示时要正过来
double z1 = pz1 - Env.BRAIN_SIZE / 2;
x1 = x1 * scale;
y1 = y1 * scale;
z1 = z1 * scale;
double x, y, z;
y = y1 * cos(xAngle) - z1 * sin(xAngle);// 绕x轴转
z = y1 * sin(xAngle) + z1 * cos(xAngle);
y1 = y;
z1 = z;
x = z1 * sin(yAngle) + x1 * cos(yAngle);// 绕y轴转
// z = z1 * cos(yAngle) - x1 * sin(yAngle);
x1 = x;
// z1 = z;
x = x1 * cos(zAngle) - y1 * sin(zAngle);// 绕z轴转
y = x1 * sin(zAngle) + y1 * cos(zAngle);
x1 = x;
y1 = y;
g.setColor(picColor);
g.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, (int) round(textSize * scale)));
g.drawString(text, (int) round(x1) + Env.FROG_BRAIN_DISP_WIDTH / 2 + xOffset,
(int) round(y1) + Env.FROG_BRAIN_DISP_WIDTH / 2 + yOffset);
}
public void drawBrainPicture(int step) {// 在这个方法里进行动物的三维脑结构的绘制,蛇是青蛙的子类所以也可以当参数传进来
if (!Env.SHOW_FIRST_ANIMAL_BRAIN)
return;
if (Env.show_split_detail)
drawSplitDetail();
else
drawBrainStructure(step);
}
public void drawSplitDetail() {// 在这个方法里绘制脑细胞分裂的显示步聚即从一个细胞开始分裂成最终脑结构的每一步
Animal a = Env.getShowAnimal(); // 第一个青蛙或蛇
for (int i = Env.BRAIN_SIZE; i >= 1; i /= 2) {
g.setColor(WHITE);// 先清空旧图, g是buffImg绘在内存中
g.fillRect(0, 0, brainDispWidth, brainDispWidth);
g.setColor(BLACK); // 画边框
g.drawRect(0, 0, brainDispWidth, brainDispWidth);
for (int geneIndex = 0; geneIndex < Genes.GENE_NUMBERS; geneIndex++) {
ArrayList<Integer> gene = a.genes.get(geneIndex);
Tree8Util.knockNodesByGene(gene);
for (int j = 0; j < Tree8Util.NODE_QTY; j++) {
if (Tree8Util.keep[j] > 0) {
int[] node = Tree8Util.TREE8[j];
int size = node[0];
if (size == i && Genes.display_gene[geneIndex]) {// 如果允许显示的话, 显示当前层级的节点
setPicColor(ColorUtils.colorByCode(geneIndex));
drawPoint(node[1] + size / 2, node[2] + size / 2, node[3] + size / 2,
size * (0.5f - geneIndex * 0.05f));
}
}
}
}
g.setColor(BLACK);
drawCuboid(0, 0, 0, Env.BRAIN_SIZE, Env.BRAIN_SIZE, Env.BRAIN_SIZE);// 把脑的框架画出来
this.getGraphics().drawImage(buffImg, 0, 0, this);// 利用缓存避免画面闪烁这里输出缓存图片
if (!Env.show_split_detail)
return;
try {
Thread.sleep(500);
} catch (InterruptedException e) {
}
}
}
public void drawBrainStructure(int step) {// 在这个方法里进行动物的三维脑结构的绘制,蛇是青蛙的子类所以也可以当参数传进来
Animal a = Env.getShowAnimal(); // 显示第一个青蛙或蛇
if (a == null || !a.alive)
return;
g.setColor(WHITE);// 先清空旧图, g是buffImg绘在内存中
g.fillRect(0, 0, brainDispWidth, brainDispWidth);
g.setColor(BLACK); // 画边框
g.drawRect(0, 0, brainDispWidth, brainDispWidth);
for (int z = 0; z < Env.BRAIN_SIZE; z++) {// 画它所有的脑细胞位置和颜色
for (int y = Env.BRAIN_SIZE - 1; y >= 0; y--) {
for (int x = Env.BRAIN_SIZE - 1; x >= 0; x--) {
setPicColor(BLACK); // 画边框
drawPointCent(x, y, z, 0.03f); //画每个细胞小点
long cell = a.cells[x][y][z];
// if (cell == 0) //只显示有效的细胞点
// continue;
if (x >= xMask && y >= yMask && cell != 0)// 画出细胞每个基因存在的细胞格子
for (int geneIndex = 0; geneIndex < Genes.GENE_NUMBERS; geneIndex++) {
if ((cell & (1 << geneIndex)) != 0 && Genes.display_gene[geneIndex]) {
setPicColor(ColorUtils.colorByCode(geneIndex)); // 开始画出对应的细胞基因参数用不同颜色圆表示
drawPoint(x + 0.5f, y + 0.5f, z + 0.5f, 0.3f);
}
}
float e = a.energys[x][y][z];
if (e > 0.03f || e < -0.03f) {
setPicColor(e > 0 ? Color.red : Color.BLUE); // 用红色小圆表示正能量蓝色表示负能量
float size = Math.abs(e);// 再用不同大小圆形表示不同能量值
if (size > 1)
size = 1;
drawCircle(x + 0.5f, y + 0.5f, z + 0.5f, size);
}
}
}
}
setPicColor(Color.BLACK); //画出 a.lines里所有线条
Line.drawOnBrainPicture(a, this);
for (Object[] o : Genes.dots) { // 画出所有登记的点的名字
String name = (String) o[0];
int x = (int) o[1];
int y = (int) o[2];
int z = (int) o[3];
drawTextCenter(x, y, z, name, 0.3f);
}
drawCuboid(0, 0, 0, Env.BRAIN_SIZE, Env.BRAIN_SIZE, Env.BRAIN_SIZE);// 把脑的框架画出来
setPicColor(BLACK); // 把x,y,z坐标画出来
drawText(Env.BRAIN_SIZE, 0, 0, "x", Env.BRAIN_SIZE * 0.2f);
drawText(0, Env.BRAIN_SIZE, 0, "y", Env.BRAIN_SIZE * 0.2f);
drawText(0, 0, Env.BRAIN_SIZE, "z", Env.BRAIN_SIZE * 0.2f);
setPicColor(RED);
drawLine(0, 0, 0, Env.BRAIN_SIZE, 0, 0);
drawLine(0, 0, 0, 0, Env.BRAIN_SIZE, 0);
drawLine(0, 0, 0, 0, 0, Env.BRAIN_SIZE);
g.setColor(Color.black);
if (note != null) {// 全局注释
g.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 16));
g.drawString(note, 10, 20);
}
g.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 16));
g.drawString("step:" + step + ", ate:" + a.ateFood + ", wrong:" + a.ateWrong+", fat="+a.fat, 10, 15);
// for (int y = 0; y < ColorUtils.rainbow.length; y += 1) {//调试彩虹色
// g.setColor(ColorUtils.rainbow[y]);
// for (int i = 0; i < 9; i++)
// g.drawLine(0, y * 9 + i, 50, y * 9 + i);
// }
this.getGraphics().drawImage(buffImg, 0, 0, this);// 利用缓存避免画面闪烁这里输出缓存图片
}
public static void setNote(String note) {
Application.brainPic.note = note;
}
// getters & setters
public float getScale() {
return scale;
}
public void setScale(float scale) {
this.scale = scale;
}
public float getxAngle() {
return xAngle;
}
public void setxAngle(float xAngle) {
this.xAngle = xAngle;
}
public float getyAngle() {
return yAngle;
}
public void setyAngle(float yAngle) {
this.yAngle = yAngle;
}
public float getzAngle() {
return zAngle;
}
public void setzAngle(float zAngle) {
this.zAngle = zAngle;
}
public void setPicColor(Color color) {
this.picColor = color;
}
public Color getPicColor() {
return picColor;
}
public int getxOffset() {
return xOffset;
}
public void setxOffset(int xOffset) {
this.xOffset = xOffset;
}
public int getyOffset() {
return yOffset;
}
public void setyOffset(int yOffset) {
this.yOffset = yOffset;
}
}

View File

@ -0,0 +1,45 @@
/*
* Copyright 2018 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
* applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
* OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*/
package com.gitee.drinkjava2.frog.brain;
/**
* Cell is a basic node of brain 3d array
*
* 从效率的角度出发LineCell这两个对象将来可能都不实际创建因为Java中对象的操作比数组更占用内存和影响速度
* 所以Cell和Line里尽量只放一些文档静态方法以便以后采用数组来代替Cell对象或Line对象尤其是Line可能用计算机模拟根本不需要分配内存是个虚对象
*
*
* 关于Cell的特性
* 1.Cell相当于一个脑神经元细胞的细胞主体部分能量只保存在主体上只存贮正值 在0~1之间传入的多余的能量将削除
* 2.Cell如果有能量能量会随时间消逝随时间消逝的能量曲线是个属性不同种类的Cell这个属性是不同的
* 3.Cell激活后给每个树突轴突Line)发送等同自身能量的能量注意不是均分而是每个都拷贝一份等量的能量此处能量不守恒由它的上层计算机系统注入能量即熵
* 4.Cell一直给Line发送能量但是否能量被传送能量传送后是否扣除影响细胞主体能量这些都不由Cell考虑而是由Line的属性和行为来决定Line是复杂的
* 同一个Line接收激活的Cell能量,有一个再次传送给间隔时间, 这是由这个Line的阀值特性Cell的能量下降曲线Line的能量扣除值来决定的
*
* Cell可以看出来非常简单比树突轴突要简单的多就是一个能量暂存放大(拷贝)消逝分发节点而树突轴突(统一用Line来表示)有复杂的行为但原则上不保存能量Cell和Line分工
* 协作一个保存能量一个传输能量 Line的种类有很多今后分裂算法的主要任务就是进行Line种类的空间分配
*
* ======== Line可以是个虚对象! =========
* 为什么Line不保存能量因为如果不保存能量Line就是无状态的甚至可以不创建Line实体或数组不需要给Line分配内存将Line作为Cell的一个行为即可能量到了Cell后根据它有
* 多少个和多少种Line属性进行相应的能量分发行为影响周围或近或远的Cell能量
* 打个比方比如
* "6叉Line属性"会对四周的细胞发送能量特长上Line属性会对上方100个细胞间隔的细胞发送能量这个行为实际上并不需要创建Line对象或给它分配内存所以Line可以
* 是一种虚对象, 作为Cell的一种属性存在(即在表示Cell的长整数中占据一个字节的标志位而这个标志位又由分裂算法的基因决定)
* 世上的道理是相通的如果一个数据结据在电脑里能简化编程实际生物细胞也可能采用同样的方案
*
* 总体来说脑是简单的决定它结构的熵并不多一颗人类受精卵约包含750MB的信息所以受精卵里并不记录每个细胞每个连线的位置而是大致决定有多少种类细胞每类细胞上有相似的连线然后交给分裂算法来
* 生成海量的细胞但是这个脑的复杂性与细胞数量无关这个复杂性即熵量仅仅是由这750MB还要去除与脑结构不相关的其它部分的信息来决定的脑本身的复杂度可能定义为"约50MB信息控制的100亿细胞的分形结构"
*
* @author Yong Zhu
* @since 1.0
*/
public class Cell {
}

View File

@ -0,0 +1,153 @@
/*
* Copyright 2018 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
* applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
* OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*/
package com.gitee.drinkjava2.frog.brain;
import java.util.ArrayList;
import com.gitee.drinkjava2.frog.Animal;
import com.gitee.drinkjava2.frog.Env;
/**
* Genes代表不同的脑细胞参数对应每个参数用8叉/4叉/2叉树算法给每个细胞添加细胞参数和行为
* 每个脑细胞用一个long来存储所以目前最多允许64个基因位,
* 多字节参数可以由多个基因位决定每个基因位都由一个单独的阴阳8/4/2叉树控制多个基因就组成了一个8/4/2叉树阵列 基因+分裂算法=结构
* 基因+分裂算法+遗传算法=结构的进化
*
* 这个类里定义每个基因位的掩码以及对应基因的细胞行为, 脑结构的所有参数都要用基因来控制开始时可以有常量魔数但以后都放到基因里去自动进化
*
* @author Yong Zhu
* @since 10.0
*/
@SuppressWarnings("all")
public class Genes { // Genes登记所有的基因 指定每个基因允许分布的空间范围注意登录完后还要并针对每个基因在active方法里写出它对应的细胞行为
public static int GENE_MAX = 64; // 目前最多允许64个基因
public static int GENE_NUMBERS = 0; // 这里统计定义了多少个基因
private static int zeros = 0; // 当前基因位掩码0个数
public static boolean[] display_gene = new boolean[GENE_MAX]; // 如果这个参数为真此基因显示在脑图上
public static boolean[] fill_gene = new boolean[GENE_MAX]; // 如果这个参数为真此基因填充指定的区域而不是由分裂算法随机生成
public static int[] xLimit = new int[GENE_MAX]; // 用来手工限定基因分布范围详见register方法
public static int[] yLimit = new int[GENE_MAX];
public static int[] zLimit = new int[GENE_MAX];
public static ArrayList<Object[]> dots = new ArrayList<>(); // 临时如果登录的范围是个三座标点把它放在这里以方便随机连线只落在登记的细胞上
/**
* Register a gene 依次从底位到高位登记所有的基因掩码及对应的相关参数
*
* @param name
* 基因的名字这个可选 缺省为null
* @param maskBits
* how many mask bits 掩码位数即有几个1
* @param display
* whether to display the gene on the BrainPicture 是否显示在脑图
* @param fill
* whether to fill to specified 3D/2D/1D/1Point area
* 是否直接用此基因填充指定的区域区域可以是三维二维线状及一个点
* @param x_limit
* gene only allow on specified x layer 如x_layer大于-1且y_layer=-1,
* 表示只生成在指定的x层对应的yz平面上这时采用4叉树而不是8叉树以提高进化速度
* @param y_limit
* gene only allow on specified x, y axis
* 如大于-1表示只生成在指定的x,y坐标对应的z轴上这时采用2叉阴阳树算法
* @param z_limit
* gene only allow on specified x, y, z 点上, 表示手工指定基因位于x,y,z坐标点上
* @return a long wtih mask bits
* 返回基因掩码高位由maskBits个1组成低位是若干个0以后判断一个cell上是否含有这个基因只需要用cell对应的long和这个
* 掩码做与运算即可
*/
public static long register(String name, int maskBits, boolean display, boolean fill, int x_limit, int y_limit, int z_limit) {
if (x_limit > -1 && y_limit > -1 && z_limit > -1) {// 临时如果登录的范围是个三座标点把它放在这里以方便随机连线只落在登记的细胞上
dots.add(new Object[] {name, x_limit, y_limit, z_limit });
}
for (int i = GENE_NUMBERS; i < GENE_NUMBERS + maskBits; i++) {
display_gene[i] = display;
fill_gene[i] = fill;
xLimit[i] = x_limit;
yLimit[i] = y_limit;
zLimit[i] = z_limit;
}
String one = "";
String zero = "";
for (int i = 1; i <= maskBits; i++)
one += "1";
for (int i = 1; i <= GENE_NUMBERS; i++)
zero += "0";
zeros = GENE_NUMBERS;
GENE_NUMBERS += maskBits;
if (GENE_NUMBERS >= GENE_MAX) {//
System.out.println("目前基因位数不能超过" + GENE_MAX);
System.exit(-1);
}
return Long.parseLong(one + zero, 2); // 将类似"111000"这种字符串转换为长整
}
public static long register(int... pos) {// 登记并指定基因允许分布的位置
return register(null, 1, true, false, pos[0], pos[1], pos[2]);
}
public static long registerFill(int... pos) {// 登记并手工指定基因填满的位置
return register(null, 1, true, true, pos[0], pos[1], pos[2]);
}
public static long registerFill(String name, int... pos) {// 登记并手工指定基因填满的位置
return register(name, 1, true, true, pos[0], pos[1], pos[2]);
}
public static boolean hasGene(long cell, long geneMask) { // 判断cell是否含某个基因
return (cell & geneMask) > 0;
}
private static final int NA = -1;
private static final int CS4 = Env.BRAIN_SIZE / 4;
// ============开始登记有名字的基因==========
public static int[] EYE1_POS = new int[] { 0, 0, 0 };
public static long EYE1 = registerFill("眼1", EYE1_POS); // 视网膜细胞
//
// public static int[] EYE2_POS = new int[] { 0, 0, 1 };
// public static long EYE2 = registerFill("眼2", EYE2_POS); // 视网膜细胞
public static int[] SWEET_POS = new int[] { 0, 2, 0 };
public static long SWEET = registerFill("", SWEET_POS); // 甜味细胞
public static int[] BITTER_POS = new int[] { 0, 2, 1 };
public static long BITTER = registerFill("", BITTER_POS); // 苦味细胞
public static int[] BITE_POS = new int[] { 0, 3, 3 };
public static long BITE = registerFill("", BITE_POS); // 咬动作细胞
public static int[] ACT_POS = new int[] { 0, 1, 3 };
public static long ACT = registerFill("", ACT_POS); // 活跃细胞这个始终激活 `
// 登记细胞间关联(触突树突)
static {
registerFill("空1", 1,1,0); //随便登记几个空细胞万一能用上呢
registerFill("空2", 1,1,1); //
registerFill("空3", 1,1,2); //
registerFill("空4", 1,1,3); //
// register(8, true, false, 0, NA, NA); //8个方向的信号发送联接
}
// ========= active方法在每个主循环都会调用用来存放细胞的行为这是个重要方法 ===========
//但是本版本因为没用到分裂算法所以这个方法体为空
public static void active(Animal a, int step) {
}
}

View File

@ -0,0 +1,132 @@
/*
* Copyright 2018 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
* applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
* OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*/
package com.gitee.drinkjava2.frog.brain;
import java.io.Serializable;
import com.gitee.drinkjava2.frog.Animal;
import com.gitee.drinkjava2.frog.util.RandomUtils;
/**
* Line is line connect 2 cells, may have many attributes
* Line是一个随机连接两个脑细胞的连线连线有多种不同属性不同的属性有不同的静态动态参数以及不同的行为
* 因为Line数量太多了对象的创建消毁太消耗资源为了节约内存和提高速度把Line设计成一个虚拟逻辑对象不会实际创建它的实例所以目前这个Line里只保留静态方法
* Line的动态参数保存在Animal.lines数列中每个元素是一个整数数组分别为 type,x1,y1,z1,x2,y2,z2,dres解释见下
* type: 线的类型同样类型的线它的常量参数是相同的常量参数保存在Animal.consts数组中起点为type*countsQTY
* x1,y1,z1为源x2,y2,z2为目的能量永远是从源到目流动与能量的正负无关要实现双向能量流动必须建立两根线
* dres dynamic resistance 动态电阻值动态电阻率敏感度大于0时电阻在重复信号刺激下会变小这样就可用电阻来作为记忆元件这个值是个有状态的值每根线条都要单独记录不像其它参数是个常量
* Line的常量参数都取值在0~1之间保存在Animal.consts数组中起点为type*countsQTY通过这种方法可以保证相同type的线它的常量参数相同(以便于将来移植到分裂算法上)参数依次为
* res: resistance, 常量电阻即通常说的权重, 0时为断路1时为全通因为no相同的line常量相同所以一条线只需要记录no不需要记录每个常量参数
* drs: dynamic resistance sensitivity 动态电阻率敏感度0时电阻率不会变动 1时电阻率会随信号重复立即变到1(全通其它值处于两者之间
* ovfoverflow 常量溢流阀值 当传递的能量大于阀值时高于阀值的部分能量传递
* ovb: overflow break 常量溃坝阀值 当传递的能量大于阀值时, 能量全部传递
* not: not logic 反相器>0.5, 将会对通过的能量反相即乘以-1
* mul: multiple 乘法器会对通过的能量值乘以一个倍数0为11时为10倍
* min: minus 扣能器 会对细胞的能量扣除一部分0为01时为1直到细胞能量为0
* add: add 加能器会对细胞能量增加一部分0为01时为1,直到细胞能量为1
* 常量参数是叠加的一个Line可以同时具有常量电阻动态电阻溢流阀反相器扣能器等属性优先级暂以上面顺序为准
* 题外话:
* 目前版本Line的数量少可以为每个Line的参数在Animal里分配一个数组空间当数量多后将来打算可能用分裂算法来代替随机连线算法分裂算法主要优点一是压缩率高二是
* 树形结构比较合理节点的等级不同一个节点的变异可能造成整个子树的增删随机连线算法做不到这一点所有节点都是同等级的无法应对环境突变情况
* 如果将来采用分裂算法Line的所有动态静态参数都要想办法挂靠在Cell上比如源目的坐标要改成相对当前细胞的偏移量这样就把同一类细胞的绝对坐标动态参数变成了相对坐标的静态常量
* Line至少会有个动态电阻当前dres值这是个有状态的值无法变成常量将来也要想办法把每根线的这个值挂靠在Cell上反正目标就是消除Line这个对象的生成让它变成一个虚拟对象以节约内存和提高速度
*
* @author Yong Zhu
* @since 1.0
*/
public class Line implements Serializable {
private static final long serialVersionUID = 1L;
public static int countsQTY = 8; // res, drs, ovf , ovb, not ...等常量参数
public static void randomAddorRemoveLine(Animal a) {//对animal的线条个数进行随机增减线条参数的变异不在这个方法中而是在常量变异见anmial的constMutate方法
if (RandomUtils.percent(10f)) { // 这两行随机加减Line
int i = RandomUtils.nextInt(Genes.dots.size());
Object[] d1 = Genes.dots.get(i);
i = RandomUtils.nextInt(Genes.dots.size());
Object[] d2 = Genes.dots.get(i);
a.lines.add(new int[] { RandomUtils.nextInt(Animal.CountsQTY / countsQTY), (int) d1[1], (int) d1[2], (int) d1[3], (int) d2[1], (int) d2[2], (int) d2[3], 5000 });
}
if (!a.lines.isEmpty() && RandomUtils.percent(10f))
a.lines.remove(RandomUtils.nextInt(a.lines.size()));
}
// ========= active方法在每个主循环都会调用调用animal所有Line的行为这是个重要方法 ===========
public static void active(Animal a, int step) {//
for (int[] l : a.lines) {
int start = l[0] * countsQTY; //start是line在常数数组的起始点
int x1 = l[1];
int y1 = l[2];
int z1 = l[3];
int x2 = l[4];
int y2 = l[5];
int z2 = l[6];
// int dres = l[7]; //dres取值0~10000对应电阻率0~1
float res = a.consts[start++]; //resistance, 常量电阻即通常说的权重, 0时为断路1时为全通
// float drs = a.consts[start++]; //dynamic resistance sensitivity 动态电阻率敏感度0时电阻率不会变动 1时电阻率会随信号重复立即变到1(全通其它值处于两者之间
//float ovf = a.consts[start++]; //overflow 常量溢流阀值 当传递的能量大于阀值时高于阀值的部分能量传递
//float ovb = a.consts[start++]; //overflow break 常量溃坝阀值 当传递的能量大于阀值时, 能量全部传递
float not = a.consts[start++]; //not logic 反相器>0.5, 将会对通过的能量反相即乘以-1
// float mul = a.consts[start++]; //乘法器会对通过的能量值乘以一个倍数0为11时为10倍
// float min = a.consts[start++]; //扣能器 会对细胞的能量扣除一部分0为01时为1直到细胞能量为0
// float add = a.consts[start++]; //加能器会对细胞能量增加一部分0为01时为1
float e = a.getEng(x1, y1, z1);
if (e < 0.1f)
continue;
if (e > 0.3f) {
// if (min > 0.1) { //扣能器
// float newSrcEng = a.getEng(x1, y1, z1) - min;
// a.setEng(x1, y1, z1, newSrcEng);
// }
// if (add > 0.1) { //加能器
// float newSrcEng = a.getEng(x1, y1, z1) + add;
// a.setEng(x1, y1, z1, newSrcEng);
// }
}
// if (e < ovb) //溃坝阀值
// continue;
// if (ovf > 0.1) { //溢流阀
// e = e - ovf;
// }
e = e * res; //静态电阻
// e = e * dres * .0001f; //动态电阻0~10000之间
// if (drs > 0.1) { //如果动态电阻敏感系统存在则每次传送一次能量电阻就变小一次
// dres += drs;
// if (dres > 10000)
// dres = 10000;
// }
if (not > 0.5) //反相器
e = -e;
// if (mul > 0.1) {
// e = e * 10 * mul; //mul是乘法器mul在0~1之间但是它控制的倍率在0~10倍之间
// }
a.addEng(x2, y2, z2, e); //能量传到target cell
}
}
//TODO 1 模式识别的环境模拟和判定 2.静态参数的初值变异 上面这个方法只是随便写的很可能跑不出结果要调整为大多数参数不起作为,即大多数情况下设为0
public static void drawOnBrainPicture(Animal a, BrainPicture pic) {// 画出所有细胞间连线
for (int[] l : a.lines) {
float f1 = RandomUtils.nextFloat() * .1f;//稍微调整下位置好看出来是否有多条线在同一个位置
float f2 = RandomUtils.nextFloat() * .1f;
pic.drawCentLine(l[1] + f1, l[2] + f2, l[3] + f2, l[4] + f1, l[5], l[6]);
pic.drawTextCenter(0.5f * (l[1] + l[4]), 0.5f * (l[2] + l[5]), 0.5f * (l[3] + l[6]), Integer.toString(l[0]), 0.15f);
pic.drawPointCent(l[4] + f1, l[5], l[6], .1f);
}
}
}

View File

@ -0,0 +1,107 @@
package com.gitee.drinkjava2.frog.brain;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
/**
* MouseAction
*
* 这个类用来处理脑图BrainPicture上的鼠标动作有平移旋转缩放三种
*
* @author Yong Zhu
* @since 2.0.2
*/
public class MouseAction implements MouseListener, MouseWheelListener, MouseMotionListener {
private BrainPicture brainPic;
private int buttonPressed = 0;
private int x;
private int y;
public MouseAction(BrainPicture brainPic) {
this.brainPic = brainPic;
}
@Override
public void mousePressed(MouseEvent e) {// 记录当前鼠标点
if (e.getButton() == 1)// 旋转
buttonPressed = 1;
else if (e.getButton() == 2)// 缩放
buttonPressed = 2;
else
buttonPressed = 0;
x = e.getPoint().x;
y = e.getPoint().y;
brainPic.requestFocus();
}
@Override
public void mouseReleased(MouseEvent e) {
buttonPressed = 0;
}
@Override
public void mouseWheelMoved(MouseWheelEvent e) {// 缩放
if (e.getWheelRotation() < 0) {
brainPic.scale *= 1.1;
brainPic.xOffset *= 1.1;
brainPic.yOffset *= 1.1;
} else {
brainPic.scale /= 1.1;
brainPic.xOffset /= 1.1;
brainPic.yOffset /= 1.1;
}
}
@Override
public void mouseDragged(MouseEvent e) {// 旋转
if (buttonPressed == 1) {
if (e.getX() > x && e.getY() > y)
brainPic.zAngle -= .00f;
else if (e.getX() < x && e.getY() < y)
brainPic.zAngle += .00f;
else {
if (e.getX() > x)
brainPic.yAngle += .02f;
if (e.getX() < x)
brainPic.yAngle -= .02f;
if (e.getY() > y)
brainPic.xAngle -= .02f;
if (e.getY() < y)
brainPic.xAngle += .02f;
}
x = e.getX();
y = e.getY();
}
if (buttonPressed == 2) {// 平移
if (e.getX() > x)
brainPic.xOffset += 6;
if (e.getX() < x)
brainPic.xOffset -= 6;
if (e.getY() > y)
brainPic.yOffset += 6;
if (e.getY() < y)
brainPic.yOffset -= 6;
x = e.getX();
y = e.getY();
}
}
@Override
public void mouseClicked(MouseEvent e) {// do nothing
}
@Override
public void mouseEntered(MouseEvent e) {// do nothing
}
@Override
public void mouseExited(MouseEvent e) {// do nothing
}
@Override
public void mouseMoved(MouseEvent e) { // do nothing
}
}

View File

@ -0,0 +1,90 @@
/*
* Copyright 2018 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
* applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
* OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*/
package com.gitee.drinkjava2.frog.egg;
import java.io.Serializable;
import java.util.ArrayList;
import com.gitee.drinkjava2.frog.Animal;
import com.gitee.drinkjava2.frog.Env;
import com.gitee.drinkjava2.frog.util.RandomUtils;
/**
* Egg is the static structure description of brain cells
*
* 蛋存在的目的是为了以最小的字节数串行化存储脑细胞,它是海量脑细胞的生成算法描述而不是脑细胞本身
* 蛋和基因的关系基因是一种语言相当于染色体不存在坐标位置蛋则是基因的载体有x,y坐标表示在虚拟环境中蛋存在的位置
*
* 另外青蛙本身也是基因的载体所以青蛙里有一个gene属性
*
* @author Yong Zhu
* @since 1.0
*/
public class Egg implements Serializable {
private static final long serialVersionUID = 1L;
public int x; // 蛋的x位置
public int y; // 蛋的y位置
// gene record the 8-tree structure of brain cells
// 基因是随机生成的8叉树数据结构和实际生物每个细胞都要保存一份基因不同程序中每个脑细胞并不需要保存基因的副本这样可以极大地减少内存占用
public ArrayList<ArrayList<Integer>> genes = new ArrayList<>();
public float[] consts = new float[Animal.CountsQTY]; //全局常量全放在这里用随机数来生成用遗传算法筛选
public ArrayList<int[]> lines = new ArrayList<>();//随机连线是可以遗传的
public Egg() {// 无中生有创建一个蛋先有蛋后有蛙
x = RandomUtils.nextInt(Env.ENV_WIDTH);
y = RandomUtils.nextInt(Env.ENV_HEIGHT);
}
/** Create egg from animal */
public Egg(Animal a) { // 下蛋每个器官会创建自已的副本或变异可以是0或多个
x = a.xPos;
y = a.yPos;
for (ArrayList<Integer> gene : a.genes) {//下蛋就是把动物的基因拷贝到新蛋里并有可能变异
ArrayList<Integer> g = new ArrayList<>();
g.addAll(gene);
genes.add(g);
}
System.arraycopy(a.consts, 0, this.consts, 0, consts.length); //把a的常量数组拷到蛋里
lines.addAll(a.lines); //把a的连线拷到蛋里
}
/**
* Create a egg by join 2 eggs, x+y=zygote 模拟XY 染色体合并两个蛋生成一个新的蛋
*/
public Egg(Egg a, Egg b) {//两个蛋的基因混合, 生成一个新蛋
x = a.x;
y = a.y;
genes.addAll(a.genes);
System.arraycopy(a.consts, 0, this.consts, 0, consts.length);
lines.addAll(a.lines); //把a的连线拷到蛋里
if (RandomUtils.percent(3)) { //小概率插入b的连线到a蛋中
if (!b.lines.isEmpty())
a.lines.add(b.lines.get(RandomUtils.nextInt(b.lines.size())));
}
if (RandomUtils.percent(20)) //插入蛋B的基因到A蛋中
for (int i = 0; i < b.genes.size(); i++) {
if (RandomUtils.percent(2)) {
ArrayList<Integer> aGene = a.genes.get(i);
ArrayList<Integer> bGene = b.genes.get(i);
if (bGene.size() > 1) {//随机插入一个B的基因不用担心基因越来越多因为随机删除的速度大于增长的
aGene.add(bGene.get(RandomUtils.nextInt(bGene.size())));
}
}
}
if (RandomUtils.percent(20)) {//交换蛋B的常量基因到A蛋中, 不重要先写上
int n = RandomUtils.nextInt(this.consts.length);
this.consts[n] = b.consts[n];
}
}
}

View File

@ -0,0 +1,123 @@
/*
* Copyright 2018 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
* applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
* OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*/
package com.gitee.drinkjava2.frog.egg;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import com.gitee.drinkjava2.frog.Animal;
import com.gitee.drinkjava2.frog.Application;
import com.gitee.drinkjava2.frog.Env;
import com.gitee.drinkjava2.frog.Frog;
import com.gitee.drinkjava2.frog.util.LocalFileUtils;
import com.gitee.drinkjava2.frog.util.Logger;
/**
* FrogEggTool save/load frog eggs to file
*
* @author Yong Zhu
* @since 1.0
*/
@SuppressWarnings("all")
public class FrogEggTool {
/**
* Frogs which have higher fat value lay eggs
*
* 利用Java串行机制存盘 更肥的(也就是吃的更多)的Frog下蛋并存盘, 以进行下一轮测试瘦的Frog被淘汰没有下蛋的资格
* 用fat值来作为唯一的生存竟争标准
*/
public static void layEggs() {
sortFrogsOrderByFat();
Frog first = Env.frogs.get(0);
Frog last = Env.frogs.get(Env.frogs.size() - 1);
try {
Env.frog_eggs.clear();
for (int i = 0; i < Env.FROG_EGG_QTY; i++) //每次下蛋数量固定为EGG_QTY个
Env.frog_eggs.add(new Egg(Env.frogs.get(i)));
Logger.info(Env.round+" Frist frog fat={}, gene size={}, lines size={}, Last frog fat={}", first.fat, getGeneSize(first), first.lines.size(), last.fat);
//Logger.info("Frist frog consts="+Arrays.toString(first.consts)); debug
if (Env.SAVE_EGGS_FILE) {
FileOutputStream fo = new FileOutputStream(Application.CLASSPATH + "frog_eggs.ser");
ObjectOutputStream so = new ObjectOutputStream(fo);
so.writeObject(Env.frog_eggs);
so.close();
Logger.info(". Saved {} eggs to file '{}frog_eggs.ser'", Env.frog_eggs.size(), Application.CLASSPATH);
}
} catch (IOException e) {
Logger.error(e);
}
}
private static String getGeneSize(Frog f) {//按genes类型汇总每种基因的个数
StringBuilder sb = new StringBuilder("[");
for (int i = 0; i < f.genes.size(); i++)
sb.append(f.genes.get(i).size()).append(",");
if (sb.length() > 1)
sb.setLength(sb.length() - 1);
sb.append("]");
return sb.toString();
}
private static void sortFrogsOrderByFat() {// 给青蛙从肥到瘦排个序
Collections.sort(Env.frogs, new Comparator<Animal>() {
public int compare(Animal a, Animal b) {
if (a.fat > b.fat)
return -1;
else if (a.fat == b.fat)
return 0;
else
return 1;
}
});
}
public static void deleteEggs() {
Logger.info("Delete exist egg file: '{}frog_eggs.ser'", Application.CLASSPATH);
LocalFileUtils.deleteFile(Application.CLASSPATH + "frog_eggs.ser");
}
/**
* 从磁盘读入一批frog Egg
*/
@SuppressWarnings("unchecked")
public static void loadFrogEggs() {
boolean errorfound = false;
try {
FileInputStream eggsFile = new FileInputStream(Application.CLASSPATH + "frog_eggs.ser");
ObjectInputStream eggsInputStream = new ObjectInputStream(eggsFile);
Env.frog_eggs = (ArrayList<Egg>) eggsInputStream.readObject();
Logger.info("Loaded " + Env.frog_eggs.size() + " eggs from file '" + Application.CLASSPATH + "frog_eggs.ser" + "'.\n");
//System.out.println(Env.frog_eggs.get(0).lines.size()); //debug
eggsInputStream.close();
} catch (Exception e) {
errorfound = true;
}
if (errorfound) {
Env.frog_eggs.clear();
for (int j = 0; j < Env.FROG_EGG_QTY; j++) {
Egg egg = new Egg();
Env.frog_eggs.add(egg);
}
Logger.info("Fail to load frog egg file '" + Application.CLASSPATH + "frog_eggs.ser" + "', created " + Env.frog_eggs.size() + " eggs to do test.");
}
}
}

View File

@ -0,0 +1,40 @@
/* Copyright 2018-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
* applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
* OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*/
package com.gitee.drinkjava2.frog.objects;
import java.awt.Graphics;
/**
* EnvObject means some virtual object in Env
*
* @author Yong Zhu
* @since 1.0
*/
public interface EnvObject {//EnvObject接口代表虚拟环境中的一种物体每定义一个物体要在Env的things变量中添加它
public void build(Graphics g);// 在Env中创建本身物体只在每屏测试前调用一次
public void destory(Graphics g);// 从Env中清除本身物体只在每屏测试完成后调用一次
public void active(int screen, int step, Graphics g);// 每个步长即时间最小单位)都会调用一次这个方法, 前两个参数分别是当前屏数和当前步长数
public static class DefaultEnvObject implements EnvObject {//EnvObject接口的缺省实现
public void build(Graphics g) {
}
public void destory(Graphics g) {
}
public void active(int screen, int step, Graphics g) {
}
}
}

View File

@ -0,0 +1,64 @@
/* Copyright 2018-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
* applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
* OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*/
package com.gitee.drinkjava2.frog.objects;
import static com.gitee.drinkjava2.frog.Env.ENV_HEIGHT;
import static com.gitee.drinkjava2.frog.Env.ENV_WIDTH;
import static com.gitee.drinkjava2.frog.Env.FOOD_QTY;
import java.awt.Graphics;
import com.gitee.drinkjava2.frog.Env;
import com.gitee.drinkjava2.frog.objects.EnvObject.DefaultEnvObject;
import com.gitee.drinkjava2.frog.util.RandomUtils;
/**
* Food randomly scatter on Env
* 食物
*
* @author Yong Zhu
* @since 1.0
*/
public class Food extends DefaultEnvObject {//食物除了被吃它自己没有什么活动,所以没有重写active方法
static Food FOOD = new Food(); //FOOD是一个单例整个环境只允许有一个FOOD实例
public static int food_ated = 0;
@Override
public void build(Graphics g) {
food_ated = 0;
for (int i = 0; i < FOOD_QTY; i++) { // 随机位置生成食物
int x = RandomUtils.nextInt(ENV_WIDTH);
int y = RandomUtils.nextInt(ENV_HEIGHT);
if (!Env.hasMaterial(x, y, Material.FOOD)) {
Env.setMaterial(x, y, Material.FOOD); //在环境里标记上FOOD
}
}
}
@Override
public void destory(Graphics g) {
food_ated = 0;
for (int x = 0; x < ENV_WIDTH; x++) // 清除食物
for (int y = 0; y < ENV_HEIGHT; y++) {
Env.clearMaterial(x, y, Material.FOOD);
}
}
public static boolean foundAndAteFood(int x, int y) {// 如果x,y有食物将其清0返回true
if (Env.hasMaterial(x, y, Material.FOOD)) {
food_ated++;
Env.clearMaterial(x, y, Material.FOOD);//在环境里清除FOOD
return true;
}
return false;
}
}

View File

@ -0,0 +1,50 @@
/* Copyright 2018-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
* applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
* OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*/
package com.gitee.drinkjava2.frog.objects;
import java.awt.Color;
/**
* Material store material types
*
* 虚拟环境中每个点由一个int代表多个材料可以同时出现在同一个点每种材料用int中的一个bit位来表示
* 小于等于16384的位数用来标记青蛙序号可利用Env.frogs.get(no-1)获取青蛙对象其它各种材料用整数中其它位来表示
*
* @author Yong Zhu
* @since 1.0
*/
public class Material {// NOSONAR
public static final int FROG_TAG = 0b11111111111111; // 16383 小于等于16384的位数用来标记青蛙序号可利用Env.frogs.get(no-1)快速定位青蛙
private static int material = FROG_TAG + 1; // 大于16384用来作为各种材料的标记
public static final int FOOD = nextMaterial();
public static final int SNAKE = nextMaterial(); // 蛇的图形
public static final int KILL_ANIMAL = nextMaterial(); // if>=KILLFROG will kill animal
public static final int BRICK = nextMaterial();// brick will kill frog
public static final int TRAP = nextMaterial(); // trap will kill frog
private static int nextMaterial() {// 每次将material左移1位
material = material << 1;
if (material < 0)
throw new IllegalArgumentException("Material out of maximum range");
return material;
}
public static Color color(int material) {
if ((material & TRAP) > 0)
return Color.LIGHT_GRAY;
else
return Color.BLACK;
}
}

View File

@ -0,0 +1,117 @@
package com.gitee.drinkjava2.frog.objects;
import java.awt.Color;
import java.awt.Graphics;
import com.gitee.drinkjava2.frog.Env;
import com.gitee.drinkjava2.frog.Frog;
import com.gitee.drinkjava2.frog.brain.Genes;
import com.gitee.drinkjava2.frog.util.RandomUtils;
/**
*
* OneInputJudge 用来模拟只有一个信号输入时的情况下青蛙是否随信号点亮咬下去, 随信号消失不咬
* 这是最简单的模式识别即1时输出1 0时输出0
*
*/
public class OneInputJudge implements EnvObject {
private int n = 16; //n是小方块边长
private static int[] food = new int[Env.STEPS_PER_ROUND + 10];
private static int totalFood;
private static void resetFood() {
int step = 0;
int x = 2;
while (step < (Env.STEPS_PER_ROUND)) {
int firstFood = RandomUtils.nextInt(5); //以10为一组随机安排5个食物
for (int i = 0; i < 10; i++)
if (i < firstFood || i > firstFood + x)
food[step + i] = 0;
else
food[step + i] = 1;
step += 10;
if (x == 2)
x = 6;
else
x = 2;
}
for (int i : food)
if (i == 1)
totalFood++;
}
@Override
public void build(Graphics g) { //build在每屏测试前调用一次这里用随机数准备好食物出现和消失的顺序为测试作准备
if (totalFood == 0) {
resetFood();
System.out.println("totalFood=" + totalFood);
}
resetFood();
for (int i = 0; i < Env.STEPS_PER_ROUND; i++) { //画出当前食物分布图
int x = i % (Env.ENV_WIDTH / n);
int y = i / (Env.ENV_WIDTH / n);
if (food[i] == 1)
g.fillRect(x * n, y * n, n, n);
else
g.drawRect(x * n, y * n, n, n);
x++;
}
}
@Override
public void destory(Graphics g) {
}
/*
* active方法中做以下事情
* 1.如果有食物就点亮视细胞
* 2.如果视细胞点亮且咬下就奖如果视细胞没点亮且咬下就罚
* 3.在左边Env显示区画出当前food的进度条
*/
@Override
public void active(int screen, int step, Graphics g) {
Frog f;
int x, y;
boolean hasFood = food[step] == 1;
for (int i = 0; i < Env.FROG_PER_SCREEN; i++) {
f = Env.frogs.get(Env.current_screen * Env.FROG_PER_SCREEN + i);
if (hasFood)
f.setEng(Genes.EYE1_POS, 1f);
else
f.setEng(Genes.EYE1_POS, 0f);
if (f.getEng(Genes.BITE_POS) > 0.8) {
if (f.getEng(Genes.EYE1_POS) > 0.8) {
f.awardA(); //咬到了有奖
f.ateFood++;
f.setEng(Genes.SWEET_POS, 1); //咬到能感觉到甜这是大自然进化出来的功能给青蛙一个知道自己咬对的信号
f.setEng(Genes.BITTER_POS, 0);
g.setColor(Color.GREEN);
} else {
f.ateWrong++;
f.penaltyA(); //咬空了扣分
f.setEng(Genes.SWEET_POS, 0);
f.setEng(Genes.BITTER_POS, 1); //咬错了能感觉到苦味这是大自然进化出来的功能给青蛙一个知道自己咬错的信号
g.setColor(Color.RED);
}
} else { //如果都没有咬关闭甜和苦味感觉
f.setEng(Genes.SWEET_POS, 0);
f.setEng(Genes.BITTER_POS, 0);
if (hasFood) {
g.setColor(Color.RED);
f.ateMiss++;
}
else
g.setColor(Color.GREEN);
}
if (i == 0) {
x = step % (Env.ENV_WIDTH / n);//用红色标记当前走到哪一步食物位置
y = step / (Env.ENV_WIDTH / n);
g.fillRect(x * n, y * n + n / 2, n, 2);
}
}
}
}

View File

@ -0,0 +1,13 @@
package com.gitee.drinkjava2.frog.objects;
import com.gitee.drinkjava2.frog.objects.EnvObject.DefaultEnvObject;
/**
*
* ThreeInputJudge 用来模拟当有3个视细胞一个甜细胞一个咬细胞这种情况下 判断青蛙能否根据甜味细胞的训练在下次相同视信号出现时提前咬下去
* 因为有3个视细胞即青蛙要实现128种情况下的模式识别
*
*/
public class ThreeInputJudge extends DefaultEnvObject {
}

View File

@ -0,0 +1,13 @@
package com.gitee.drinkjava2.frog.objects;
import com.gitee.drinkjava2.frog.objects.EnvObject.DefaultEnvObject;
/**
*
* TwoInputJudge 用来模拟当有两个视细胞一个甜细胞一个咬细胞这种情况下 判断青蛙能否根据甜味细胞的训练在下次相同视信号出现时提前咬下去
* 因为只有两个视细胞即青蛙要实现与异或这三种模式识别
*
*/
public class TwoInputJudge extends DefaultEnvObject {
}

View File

@ -0,0 +1,83 @@
/* Copyright 2018-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
* applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
* OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*/
package com.gitee.drinkjava2.frog.util;
import java.awt.Color;
/**
* Color Utilities used in this project
*
* @author Yong Zhu
* @since 1.0
*/
public class ColorUtils {
public static Color[] rainbow;
static {
rainbow = new Color[125];
int i = 0;
for (int r = 0; r < 3; r++)
for (int b = 0; b < 3; b++)
for (int g = 0; g < 3; g++) {
{
{
if (!(r == b && r == g)) {
rainbow[i] = new Color(((r + 2) % 3) * 122, g * 100, ((b + 1) % 3) * 88);
i++;
}
}
}
}
Color[] t = new Color[i];
System.arraycopy(rainbow, 0, t, 0, i);
rainbow = t;
}
private static int nextColor = 0;
private ColorUtils() {// default private constr
}
public static int nextColorCode() {
return nextColor++;
}
public static Color nextRainbowColor() {// 返回下一个彩虹色
if (nextColor >= rainbow.length)
nextColor = 0;
return rainbow[nextColor++];
}
public static Color colorByCode(int i) {// 数值取模后返回一个固定彩虹色
return rainbow[i % rainbow.length];
}
public static Color rainbowColor(float i) { // 根据数值大小范围在8种彩虹色中取值
if (i <= 20)
return Color.GRAY;
if (i <= 30)
return Color.BLACK;
if (i <= 50)
return Color.RED;
return Color.MAGENTA;
}
public static Color grayColor(float f) { // 根据数值大小范围0~1000返回一个灰度色越大越黑
if (f > 1000)
f = 1000;
int i1 = 255 - (int) Math.round(f * .255);
int i2 = 200 - (int) Math.round(f * .200);
int i3 = 150 - (int) Math.round(f * .150);
return new Color(i1, i2, i3);
}
}

View File

@ -0,0 +1,138 @@
/*
* Copyright 2018 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
* applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
* OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*/
package com.gitee.drinkjava2.frog.util;
import static com.gitee.drinkjava2.frog.brain.Genes.GENE_NUMBERS;
import static com.gitee.drinkjava2.frog.util.RandomUtils.percent;
import java.util.ArrayList;
import java.util.Arrays;
import com.gitee.drinkjava2.frog.Animal;
import com.gitee.drinkjava2.frog.Env;
import com.gitee.drinkjava2.frog.brain.Genes;
/**
* 与Gene相关的工具方法
*
* @author Yong Zhu
* @since 10.0
*/
@SuppressWarnings("all")
public class GeneUtils {
public static void createCellsFromGene(Animal a) {//根据基因生成细胞参数
for (int g = 0; g < GENE_NUMBERS; g++) {//动物有多条基因一条基因控制一维细胞参数目前最多有64维也就是最多有64条基因
long geneMask = 1l << g;
ArrayList<Integer> gene = a.genes.get(g);
int xLimit = Genes.xLimit[g];
int yLimit = Genes.yLimit[g];
int zLimit = Genes.zLimit[g];
boolean fill = Genes.fill_gene[g];
if (fill) { //如果这个基因是fill型的会填充在指定区域的所有细胞中不需要使用分裂算法来生成细胞
if (xLimit < 0) { //如坐标一个也没有给出, 填充整个三维脑细胞空间
for (int x = 0; x < Env.BRAIN_SIZE; x++)
for (int y = 0; y < Env.BRAIN_SIZE; y++)
for (int z = 0; z < Env.BRAIN_SIZE; z++)
a.cells[x][y][z] = a.cells[x][y][z] | geneMask;
} else if (yLimit < 0) { // 如果只给出了x坐标, 填充此基因在脑坐标为x的yz平面上
for (int y = 0; y < Env.BRAIN_SIZE; y++)
for (int z = 0; z < Env.BRAIN_SIZE; z++)
a.cells[xLimit][y][z] = a.cells[xLimit][y][z] | geneMask;
} else if (zLimit < 0) { // 如果只给出了x,y坐标填充此基因在x,y指定的z轴上
for (int z = 0; z < Env.BRAIN_SIZE; z++)
a.cells[xLimit][yLimit][z] = a.cells[xLimit][yLimit][z] | geneMask;
} else { //如果x,y,z都给出了填充此基因在x,y,z指定的点上
a.cells[xLimit][yLimit][zLimit] = a.cells[xLimit][yLimit][zLimit] | geneMask;
}
continue;
}
//以下开始使用分裂和随机算法来从基因链生成脑细胞
if (xLimit < 0) { //如坐标一个也没有定义,使用阴阳8叉树分裂算法在三维脑细胞空间分裂,这个最慢但分布范围大
Tree8Util.knockNodesByGene(gene);//根据基因把要敲除的8叉树节点作个标记
for (int i = 0; i < Tree8Util.NODE_QTY; i++) {//再根据敲剩下的8叉树keep标记生成细胞参数
if (Tree8Util.keep[i] > 0) {
int[] node = Tree8Util.TREE8[i];
if (node[0] == 1) {//如果node边长为1即不可以再分裂了就在三维空间对应数组的位置把当前基因geneMask置1
a.cells[node[1]][node[2]][node[3]] = a.cells[node[1]][node[2]][node[3]] | geneMask; //在相应的细胞处把细胞参数位置1
}
}
}
} else if (yLimit < 0) { // 如果只定义了x坐标, 表示此基因分布在脑坐标x的yz平面上此时使用阴阳4叉树分裂算法在此平面上分裂以加快速度
Tree4Util.knockNodesByGene(gene);//根据基因把要敲除的4叉树节点作个标记
for (int i = 0; i < Tree4Util.NODE_QTY; i++) {//再根据敲剩下的4叉树keep标记生成细胞参数
if (Tree4Util.keep[i] > 0) {
int[] node = Tree4Util.TREE4[i];
if (node[0] == 1) {//如果node边长为1即不可以再分裂了就在2维空间对间数组的位置把当前基因geneMask置1
a.cells[xLimit][node[1]][node[2]] = a.cells[xLimit][node[1]][node[2]] | geneMask; //在相应的细胞处把细胞参数位置1
}
}
}
} else if (zLimit < 0) { // 如果只定义了x,y坐标这时基因只能分布在x,y指定的z轴上此时使用阴阳2叉树分裂算法
Tree2Util.knockNodesByGene(gene);//根据基因把要敲除的4叉树节点作个标记
for (int i = 0; i < Tree2Util.NODE_QTY; i++) {//再根据敲剩下的4叉树keep标记生成细胞参数
if (Tree2Util.keep[i] > 0) {
int[] node = Tree2Util.TREE2[i];
if (node[0] == 1) {//如果node边长为1即不可以再分裂了就在2维空间对间数组的位置把当前基因geneMask置1
a.cells[xLimit][yLimit][node[1]] = a.cells[xLimit][yLimit][node[1]] | geneMask; //在相应的细胞处把细胞参数位置1
}
}
}
} else { //如果x,y,z都指定了表示这个基因只能分布在一个点上, 这时只有0或1两种可能,如果基因不为空就认为它有
if (!gene.isEmpty())
a.cells[xLimit][yLimit][zLimit] = a.cells[xLimit][yLimit][zLimit] | geneMask;
}
}
}
public static void geneMutation(Animal a) { //基因变异,注意这一个方法同时变异所有条基因
for (int g = 0; g < GENE_NUMBERS; g++)
if (percent(50)) {
if (Genes.fill_gene[g]) //如果这个基因是fill型的永远会存在指定区域的所有细胞中所以不需要参与变异
continue;
int n = 5; //这是个魔数今后可以考虑放在基因里去变异8\4\2\1叉树的变异率可以不一样
//随机新增阴节点基因注意只是简单地随机新增所以可能有重复基因
ArrayList<Integer> gene = a.genes.get(g);
int geneMaxLength; //8叉4叉树2叉树的节点最大序号不同基因随机生成时要限制它不能大于最大序号
if (Genes.xLimit[g] < 0) { //如x没定义,使用阴阳8叉树分裂算法
geneMaxLength = Tree8Util.NODE_QTY;
} else if (Genes.yLimit[g] < 0) { // 如果x>=0, y没定义, 表示此基因分布在坐标x的yz平面上此时使用阴阳4叉树分裂算法
geneMaxLength = Tree4Util.NODE_QTY;
} else if (Genes.zLimit[g] < 0) { // 如果x>=0, y>=0z没定义这时基因只能分布在x,y指定的z轴上此时使用阴阳2叉树分裂算法
geneMaxLength = Tree2Util.NODE_QTY;
} else { //如果x,y,z都指定了表示这个基因只能分布在一个点上, 这时只有0或1两种可能, 如果基因不为空就认为它有用随机算法
if (percent(n)) {
if (gene.isEmpty())
gene.add(1);
else
gene.clear();
}
continue;
}
if (percent(n)) //随机生成负节点号对应阴节点,
gene.add(-RandomUtils.nextInt(geneMaxLength));
if (percent(n)) //生成随机负正节点号对应阳节点
gene.add(RandomUtils.nextInt(geneMaxLength));
if (percent(n * 2 + 2)) //随机删除一个节点用这种方式来清除节点防止节点无限增长如果删对了就不会再回来如果删错了系统就会把这个青蛙整个淘汰这就是遗传算法的好处
if (!gene.isEmpty())
gene.remove(RandomUtils.nextInt(gene.size()));
}
}
}

View File

@ -0,0 +1,141 @@
/* Copyright 2018-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
* applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
* OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*/
package com.gitee.drinkjava2.frog.util;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* Local File Utilities used in this project
*
* @author Yong Zhu
* @since 1.0
*/
public class LocalFileUtils {
private LocalFileUtils() {
// default constructor
}
public static boolean deleteFile(String fileFullPath) {
File file = new File(fileFullPath);
return file.delete(); // NOSONAR
}
public static void writeFile(String fileFullPath, byte[] byteArry) {
File file = new File(fileFullPath);
if (!file.getParentFile().exists())
file.getParentFile().mkdirs();
FileOutputStream fos = null;
try {
fos = new FileOutputStream(file);
fos.write(byteArry);
fos.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (fos != null) {
try {
try {
fos.flush();
} catch (Exception e) {
}
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public static void writeFile(String fileFullPath, String text, String encoding) {
File file = new File(fileFullPath);
if (!file.getParentFile().exists())
file.getParentFile().mkdirs();
FileOutputStream fos = null;
try {
fos = new FileOutputStream(file);
byte[] bytes;
bytes = text.getBytes(encoding);
fos.write(bytes);
fos.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (fos != null) {
try {
try {
fos.flush();
} catch (Exception e) {
}
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public static String readFile(String fileFullPath, String encoding) {
InputStream inputStream;
try {
inputStream = new FileInputStream(new File(fileFullPath));
} catch (FileNotFoundException e1) {
return null;
}
try {
ByteArrayOutputStream result = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int length;
while ((length = inputStream.read(buffer)) != -1)
result.write(buffer, 0, length);
String string = result.toString(encoding);
return string;
} catch (IOException e) {
e.printStackTrace();
return null;
} finally {
try {
inputStream.close();
} catch (IOException e) {
}
}
}
public static void appendFile(String fileName, String content) {
FileOutputStream fos = null;
try {
fos = new FileOutputStream(fileName, true);
fos.write(content.getBytes());
fos.write("\r\n".getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fos != null) {
try {
try {
fos.flush();
} catch (Exception e) {
}
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}

View File

@ -0,0 +1,200 @@
/*
* Copyright 2021 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
* applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
* OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*/
package com.gitee.drinkjava2.frog.util;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
/**
* Usually a logger tool is used like below:
* Logger log = LoggerFactory.getLogger(Xxxx.class);
* log.info("some msg");
*
* But to simplify, in this project directly use static method:
* Logger.info("some msg");
*
* @Description: 简版控制台日志打印从码云上合并来 https://gitee.com/drinkjava2/frog/pulls/4
* @author 栾成翔
* @Date: 2021/12/07
*/
@SuppressWarnings("all")
public class Logger {
private static final int SYSTEM_OUT_PRINT = 1; //如设为1不使用log直接System.out.print输出
private static final int LOGGER_STYLE = 0; //风格设定 0:不输出前缀, 1:输出时间行号等前缀
private static final String LEV_EL = "DEBUG";
private static final int LEVEL_INT;
private static final BlockingQueue<String> LOG_LIST = new ArrayBlockingQueue<>(256);
private static final SimpleDateFormat FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss,SSS");
private static final OutputStream OUTPUT_STREAM = System.out;
private static final String LINE_SEPARATOR = System.getProperty("line.separator");
private static final String DELIM_STR = "{}";
private static final String TAB = "\tat";
private static final Map<String, Integer> LEVEL_MAP = new HashMap<>();
static {
LEVEL_MAP.put("DEBUG", 1);
LEVEL_MAP.put("INFO", 2);
LEVEL_MAP.put("WARN", 3);
LEVEL_MAP.put("ERROR", 4);
LEVEL_INT = LEVEL_MAP.get(LEV_EL.toUpperCase());
new Thread(() -> {
while (true) {
try {
outPutConsole(LOG_LIST.take());
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}).start();
}
public static void debug(String msg) {
printLog(LEVEL_MAP.get("DEBUG"), msg);
}
public static void debug(String msg, Object params) {
printLog(LEVEL_MAP.get("DEBUG"), msg, params);
}
public static void debug(String msg, Object... params) {
printLog(LEVEL_MAP.get("DEBUG"), msg, params);
}
public static void info(String msg) {
printLog(LEVEL_MAP.get("INFO"), msg);
}
public static void info(String msg, Object params) {
printLog(LEVEL_MAP.get("INFO"), msg, params);
}
public static void info(String msg, Object... params) {
printLog(LEVEL_MAP.get("INFO"), msg, params);
}
public static void warn(String msg) {
printLog(LEVEL_MAP.get("WARN"), msg);
}
public static void warn(String msg, Object params) {
printLog(LEVEL_MAP.get("WARN"), msg, params);
}
public static void warn(String msg, Object... params) {
printLog(LEVEL_MAP.get("WARN"), msg, params);
}
public static void error(String msg) {
printLog(LEVEL_MAP.get("ERROR"), msg);
}
public static void error(String msg, Object params) {
printLog(LEVEL_MAP.get("ERROR"), msg, params);
}
public static void error(String msg, Object... params) {
printLog(LEVEL_MAP.get("ERROR"), msg, params);
}
public static void error(Object param) {
printLog(LEVEL_MAP.get("ERROR"), "", param);
}
private static void printLog(int levelInt, String msg, Object... params) {
if (levelInt < LEVEL_INT)
return;
if (SYSTEM_OUT_PRINT == 1)
System.out.print(generateMsg(getLevelStr(levelInt), msg, params));
else
try {
LOG_LIST.put(generateMsg(getLevelStr(levelInt), msg, params));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private static String generateMsg(String levelStr, String msg, Object... params) {
if (LOGGER_STYLE == 0)
return formatMsg(msg + LINE_SEPARATOR, null, params);
StackTraceElement stack = Thread.currentThread().getStackTrace()[4];
String s = "{} [{}][{}#{} {}] - " + msg + LINE_SEPARATOR;
Object[] args = new Object[5 + params.length];
args[0] = FORMAT.format(System.currentTimeMillis());
args[1] = levelStr;
args[2] = stack.getClassName();
args[3] = stack.getMethodName();
args[4] = stack.getLineNumber();
Throwable throwable = null;
if (params.length > 0) {
final Object lastEntry = params[params.length - 1];
if (lastEntry instanceof Throwable) {
throwable = (Throwable) lastEntry;
System.arraycopy(params, 0, args, 5, params.length - 1);
} else {
System.arraycopy(params, 0, args, 5, params.length);
}
}
return formatMsg(s, throwable, args);
}
private static String formatMsg(String msg, Throwable throwable, Object... params) {
StringBuilder sb = new StringBuilder();
int s;
int i = 0;
for (Object o : params) {
s = msg.indexOf(DELIM_STR, i);
if (s > -1) {
sb.append(msg, i, s).append(o);
i = s + 2;
}
}
sb.append(msg, i, msg.length());
if (null != throwable) {
sb.append(throwable).append(LINE_SEPARATOR);
StackTraceElement[] stack = throwable.getStackTrace();
for (StackTraceElement element : stack) {
sb.append(TAB).append(element).append(LINE_SEPARATOR);
}
}
return sb.toString();
}
private static void outPutConsole(String msg) {
try {
OUTPUT_STREAM.write(msg.getBytes(StandardCharsets.UTF_8));
OUTPUT_STREAM.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
private static String getLevelStr(int levelInt) {
switch (levelInt){
case 1:
return "DEBUG";
case 2:
return "INFO";
case 3:
return "WARN";
case 4:
return "ERROR";
default:
throw new IllegalStateException("Level " + levelInt + " is unknown.");
}
}
}

View File

@ -0,0 +1,126 @@
/* Copyright 2018-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
* applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
* OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*/
package com.gitee.drinkjava2.frog.util;
import java.util.Random;
/**
* Random Utilities used in this project
*
* @author Yong Zhu
* @since 1.0
*/
public class RandomUtils {
private RandomUtils() {
}
private static final Random rand = new Random();
public static int nextInt(int i) {//返回随机整数最小为0最大为n-1
if (i == 0)
return 0;
return rand.nextInt(i);
}
public static int nextNegOrPosInt(int n) {//返回随机整数最小为-(n-1)最大为n-1
int x = nextInt(n);
if (percent(50))
return x;
return -x;
}
public static float nextFloat() {
return rand.nextFloat();
}
public static float nextNegOrPosFloat() {
if (percent(50))
return rand.nextFloat();
return -rand.nextFloat();
}
public static boolean percent(float percent) {// 有百分这percent的机率为true
return rand.nextFloat() * 100 < percent;
}
public static int vary(int v, int percet) {
if (percent(percet))
return vary(v);
return v;
}
// public static int vary(int v) {// 随机有大概率小变异小概率大变异极小概率极大变异
// if (percent(50))
// v += (nextInt(3) - 0.5);
// else if (percent(50))
// v += 2 * (nextInt(2) - 0.5);
// else if (percent(50))
// v += 4 * (nextInt(2) - 0.5);
// else if (percent(50))
// v += 8 * (nextInt(2) - 0.5);
// else if (percent(50))
// v += 16 * (nextInt(2) - 0.5);
// else if (percent(50))
// v += 32 * (nextInt(2) - 0.5);
// else if (percent(50))
// v += 64 * (nextInt(2) - 0.5);
// else if (percent(50))
// v += 100 * (nextInt(2) - 0.5);
// return v;
// }
public static int vary(int v) {// 随机有大概率小变异小概率大变异极小概率极大变异 这里改进算法用正切函数曲线来实现这个概率
int n = nextNegOrPosInt(900);
return (int) (v + Math.round(2 * Math.tan(0.1f * n * 3.14159 / 180)));
}
// public static void main(String[] args) {
// int n = 0;
// for (int i = 0; i < 100; i++) {
// n = vary(n);
// System.out.println(n);
// }
// }
public static float vary(float v) {// 随机有大概率小变异小概率大变异极小概率极大变异
if (percent(40))
v += v * .04 * (nextFloat() - 0.5); // v=v+-.04
if (percent(10))
v += v * .103 * (nextFloat() - 0.5); // v=v+-0.1
else if (percent(5))
v += v * 1 * (nextFloat() - 0.5); // v=v+-0.4
else if (percent(2))
v += v * 4 * (nextFloat() - 0.5); // v=v+-2
else if (percent(1f))
v += v * 8 * (nextFloat() - 0.5); // v=v+-6
return v;
}
public static int varyInLimit(int v, int from, int to) {// 让返回值在from和to之间随机变异
int i = vary(v);
if (i < from)
i = from;
if (i > to)
i = to;
return i;
}
public static float varyInLimit(float v, float from, float to) {// 让返回值在from和to之间随机变异
float i = vary(v);
if (i < from)
i = from;
if (i > to)
i = to;
return i;
}
}

View File

@ -0,0 +1,110 @@
/* Copyright 2018-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
* applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
* OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*/
package com.gitee.drinkjava2.frog.util;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.util.HashMap;
import java.util.Map;
/**
* StringPixelUtils used to get pixel array from a given string
*
* 根据给定的字体和字符串返回它的像素点阵lettersMap[0][0]是左下角素
*
* @author Yong Zhu
* @since 2.0.2
*/
public class StringPixelUtils {
private static final Map<String, byte[][]> lettersMap = new HashMap<>();//cache
public static byte[][] getSanserif10Pixels(String s) {
return getStringPixels(Font.SANS_SERIF, Font.PLAIN, 10, s);
}
public static byte[][] getSanserif12Pixels(String s) {
return getStringPixels(Font.SANS_SERIF, Font.PLAIN, 12, s);
}
public static byte[][] getSanserifItalic10Pixels(String s) {
return getStringPixels(Font.SANS_SERIF, Font.ITALIC, 10, s);
}
/* 在内存 BufferedImage里输出文本并获取它的像素点 */
public static byte[][] getStringPixels(String fontName, int fontStyle, int fontSize, String s) {
String key = new StringBuilder(fontName).append("_").append(fontStyle).append("_").append(fontSize).append("_")
.append(s).toString();
if (lettersMap.containsKey(key))
return lettersMap.get(key);
Font font = new Font(fontName, fontStyle, fontSize);
BufferedImage bi = new BufferedImage(fontSize * 10, fontSize * 50, BufferedImage.TYPE_INT_RGB);
Graphics g = bi.getGraphics();
Graphics2D g2d = (Graphics2D) g;
g2d.setFont(font);
FontMetrics fm = g2d.getFontMetrics();
int strHeight = fm.getAscent() + fm.getDescent() - 1;
int strWidth = fm.stringWidth(s);
g2d.drawString(s, 0, fm.getAscent() - fm.getLeading() -1);
int ystart;//改进在命令行和eclipse下会有不同的空行所以要用ystart和yend来限定只获取有效象素行数
loop1: for (ystart = 0; ystart < strHeight; ystart++)
for (int x = 0; x < strWidth; x++) {
if (bi.getRGB(x, ystart) == -1)
break loop1;
}
int yend;
loop2: for (yend = strHeight; yend >= 0; yend--)
for (int x = 0; x < strWidth; x++) {
if (bi.getRGB(x, yend) == -1)
break loop2;
}
if(yend<ystart)
yend=ystart;
byte[][] b = new byte[strWidth][yend-ystart+1];
for (int y = ystart; y <= yend; y++)
for (int x = 0; x < strWidth; x++)
if (bi.getRGB(x, y ) == -1)
b[x][yend-y] = 1;
else
b[x][yend-y] = 0;
lettersMap.put(key, b);
return b;
}
public static void printPixelArray(byte[][] c) {
int w = c.length;
int h = c[0].length;
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
if (c[x][h - y - 1] > 0)
System.out.print("*");
else
System.out.print(" ");
}
System.out.println();
}
}
/*- 这个是测试输出平时不需要用
public static void main(String[] args) {
System.out.println("===============");
byte[][] c = getStringPixels(Font.SANS_SERIF, Font.PLAIN, 12, "FROG");
printPixelArray(c);
System.out.println("===============");
}
*/
}

View File

@ -0,0 +1,86 @@
/* Copyright 2018-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
* applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
* OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*/
package com.gitee.drinkjava2.frog.util;
import java.util.List;
import com.gitee.drinkjava2.frog.Env;
/**
* Tree2Util used to store a pre-order Traversal 2tree array to speed
*
* 这里缓存着一个前序排列的2叉树用来加快速度
* 这里的2叉树和4叉树的原理类似只不过4叉树生成2维平面形状 2叉树用来生成一维线状对于线状生成速度更快
*
* 另一种思路是先把基因按绝对值排序然后顺着多叉树先序排列的次序依次设每个细胞的黑白可能速度更快且可以很快发现无效和重复基因但我没时间按这个思路来优化了以后有时间做
*
* @author Yong Zhu
* @since 1.0
*/
public class Tree2Util {
//EIGHT_TREE store a pre-order Traversal tree array
public static final int NODE_QTY = calculateNodeSize(Env.BRAIN_SIZE);
public static int[][] TREE2 = new int[NODE_QTY][2]; //2叉数用数组表示第一维是深度树的行号第二维是一个整数数组,内容是深度树表示的2叉树细胞的size, x值
public static byte[] keep = new byte[NODE_QTY]; //这里临时记录树的敲除记录大于0的值表示要keep, 小于等于0表示要敲除
private static byte[] KEEP0 = new byte[NODE_QTY]; //这里保存初值为0的数组常量可以用System.arraycopy(KEEP, 0, keep, 0, NODE_QTY)快速清空enable数组
public static int keepNodeQTY = NODE_QTY; //这里临时记录需keep的节点总数好用来继续敲除初始值是全部节点
private static int index = 0;
static {
tree2Split(0, Env.BRAIN_SIZE);
}
static int calculateNodeSize(int n) {//计算2叉树全展开的总节点数
if (n == 1)
return 1;
return n + calculateNodeSize(n / 2);
}
//if cube can split, then split it to 8 small cubes
private static void tree2Split(int x, int size) {//如正方形可分裂就继续递归分裂成4个
TREE2[index++] = new int[]{size, x}; //这里size类似于深度树中的level只不过是size从大到小level是从小到大原理一样
if (size == 1)
return;
int half = size / 2;//每个细胞可以分裂成2个size为原来1/2的小细胞
tree2Split(x, half);
tree2Split(x + half, half);
}
public static void knockNodesByGene(List<Integer> gene) {//根据基因把要敲除的2叉树节点作敲除或保留标记
System.arraycopy(KEEP0, 0, keep, 0, NODE_QTY);//清空keep数组
keepNodeQTY = 0;
for (int g : gene) {//g基因用带符号的2叉数的行号表示负数表示阴节点要敲除正数表示是阳节点要保留
int gLine = Math.abs(g); //基因对应节点的行号
int size = Tree2Util.TREE2[gLine][0]; //size是基因对应节点的细胞正方形边长
for (int line = gLine; line < Tree2Util.NODE_QTY; line++) {//从这个g节点开始往下找节点
if (line > gLine && Tree2Util.TREE2[line][0] >= size) //如果除了第一个节点外边长大于等于size说明节点不是g的子节点退出
break;
else {//否则就是g的子节点
if (g < 0) { //g是阴节点
if (Tree2Util.keep[line] == 1) //如果是1表示这个节点将从保留状态转为删除状态
keepNodeQTY--;
Tree2Util.keep[line] = 0;
} else if (g > 0) { //g是阳节点
if (Tree2Util.keep[line] == 0) //如果是0表示这个节点将从删除状态转为保留状态
keepNodeQTY++;
Tree2Util.keep[line] = 1;
}
}
}
}
}
}

View File

@ -0,0 +1,88 @@
/* Copyright 2018-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
* applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
* OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*/
package com.gitee.drinkjava2.frog.util;
import java.util.List;
import com.gitee.drinkjava2.frog.Env;
/**
* Tree4Util used to store a pre-order Traversal 4tree array to speed
*
* 这里缓存着一个前序排列的4叉树用来加快速度
* 这里的4叉树和4叉树的原理类似只不过4叉树生成3维形状4叉树用来生成2维平面形状对于平面形状生成速度更快
*
* 另一种思路是先把基因按绝对值排序然后顺着多叉树先序排列的次序依次设每个细胞的黑白可能速度更快且可以很快发现无效和重复基因但我没时间按这个思路来优化了以后有时间做
*
* @author Yong Zhu
* @since 1.0
*/
public class Tree4Util {
//EIGHT_TREE store a pre-order Traversal tree array
public static final int NODE_QTY = calculateNodeSize(Env.BRAIN_SIZE);
public static int[][] TREE4 = new int[NODE_QTY][3]; //4叉数用数组表示第一维是深度树的行号第二维是一个整数数组,内容是深度树表示的4叉树细胞的size, x, y值
public static byte[] keep = new byte[NODE_QTY]; //这里临时记录树的敲除记录大于0的值表示要keep, 小于等于0表示要敲除
private static byte[] KEEP0 = new byte[NODE_QTY]; //这里保存初值为0的数组常量可以用System.arraycopy(KEEP, 0, keep, 0, NODE_QTY)快速清空enable数组
public static int keepNodeQTY = NODE_QTY; //这里临时记录需keep的节点总数好用来继续敲除初始值是全部节点
private static int index = 0;
static {
tree4Split(0, 0, Env.BRAIN_SIZE);
}
static int calculateNodeSize(int n) {//计算4叉树全展开的总节点数
if (n == 1)
return 1;
return n * n + calculateNodeSize(n / 2);
}
//if cube can split, then split it to 8 small cubes
private static void tree4Split(int x, int y, int size) {//如正方形可分裂就继续递归分裂成4个
TREE4[index++] = new int[]{size, x, y}; //这里size类似于深度树中的level只不过是size从大到小level是从小到大原理一样
if (size == 1)
return;
int half = size / 2;//每个细胞可以分裂成4个size为原来1/2的小细胞
tree4Split(x, y, half);
tree4Split(x + half, y, half);
tree4Split(x, y + half, half);
tree4Split(x + half, y + half, half);
}
public static void knockNodesByGene(List<Integer> gene) {//根据基因把要敲除的4叉树节点作敲除或保留标记
System.arraycopy(KEEP0, 0, keep, 0, NODE_QTY);//清空keep数组
keepNodeQTY = 0;
for (int g : gene) {//g基因用带符号的4叉数的行号表示负数表示阴节点要敲除正数表示是阳节点要保留
int gLine = Math.abs(g); //基因对应节点的行号
int size = Tree4Util.TREE4[gLine][0]; //size是基因对应节点的细胞正方形边长
for (int line = gLine; line < Tree4Util.NODE_QTY; line++) {//从这个g节点开始往下找节点
if (line > gLine && Tree4Util.TREE4[line][0] >= size) //如果除了第一个节点外边长大于等于size说明节点不是g的子节点退出
break;
else {//否则就是g的子节点
if (g < 0) { //g是阴节点
if (Tree4Util.keep[line] == 1) //如果是1表示这个节点将从保留状态转为删除状态
keepNodeQTY--;
Tree4Util.keep[line] = 0;
} else if (g > 0) { //g是阳节点
if (Tree4Util.keep[line] == 0) //如果是0表示这个节点将从删除状态转为保留状态
keepNodeQTY++;
Tree4Util.keep[line] = 1;
}
}
}
}
}
}

View File

@ -0,0 +1,91 @@
/* Copyright 2018-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
* applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
* OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*/
package com.gitee.drinkjava2.frog.util;
import java.util.List;
import com.gitee.drinkjava2.frog.Env;
/**
* Tree8Util used to store a pre-order Traversal tree array to speed
*
* 这里缓存着一个前序排列的八叉树用来在细胞生成时加快速度和简化运算关于树结构可用深度树数组来表达的知识可以参见这里https://my.oschina.net/drinkjava2/blog/1818631
*
* 另一种思路是先把基因按绝对值排序然后顺着多叉树先序排列的次序依次设每个细胞的黑白可能速度更快且可以很快发现无效和重复基因但我没时间按这个思路来优化了以后有时间做
*
* @author Yong Zhu
* @since 1.0
*/
public class Tree8Util {
//EIGHT_TREE store a pre-order Traversal tree array
public static final int NODE_QTY = calculateNodeSize(Env.BRAIN_SIZE);
public static int[][] TREE8 = new int[NODE_QTY][4]; //八叉数用数组表示第一维是深度树的行号第二维是一个整数数组,内容是深度树表示的八叉树细胞的size, x, y, z值
public static byte[] keep = new byte[NODE_QTY]; //这里临时记录树的敲除记录大于0的值表示要keep, 小于等于0表示要敲除
private static byte[] KEEP0 = new byte[NODE_QTY]; //这里保存初值为0的数组常量可以用System.arraycopy(KEEP, 0, keep, 0, NODE_QTY)快速清空enable数组
public static int keepNodeQTY = NODE_QTY; //这里临时记录需keep的节点总数好用来继续敲除初始值是全部节点
private static int index = 0;
static {
tree8Split(0, 0, 0, Env.BRAIN_SIZE);
}
static int calculateNodeSize(int n) {//计算8叉树全展开的总节点数
if (n == 1)
return 1;
return n * n * n + calculateNodeSize(n / 2);
}
//if cube can split, then split it to 8 small cubes
private static void tree8Split(int x, int y, int z, int size) {//如立方体可分裂就继续递归分裂成8个
TREE8[index++] = new int[]{size, x, y, z}; //这里size类似于深度树中的level只不过是size从大到小level是从小到大原理一样
if (size == 1)
return;
int half = size / 2;//每个细胞可以分裂成8个size为原来1/2的小细胞
tree8Split(x, y, z, half);
tree8Split(x + half, y, z, half);
tree8Split(x, y + half, z, half);
tree8Split(x + half, y + half, z, half);
tree8Split(x, y, z + half, half);
tree8Split(x + half, y, z + half, half);
tree8Split(x, y + half, z + half, half);
tree8Split(x + half, y + half, z + half, half);
}
public static void knockNodesByGene(List<Integer> gene) {//根据基因把要敲除的8叉树节点作敲除或保留标记
System.arraycopy(KEEP0, 0, keep, 0, NODE_QTY);//清空keep数组
keepNodeQTY = 0;
for (int g : gene) {//g基因用带符号的8叉数的行号表示负数表示阴节点要敲除正数表示是阳节点要保留
int gLine = Math.abs(g); //基因对应节点的行号
int size = Tree8Util.TREE8[gLine][0]; //size是基因对应节点的细胞立方体边长
for (int line = gLine; line < Tree8Util.NODE_QTY; line++) {//从这个g节点开始往下找节点
if (line > gLine && Tree8Util.TREE8[line][0] >= size) //如果除了第一个节点外边长大于等于size说明节点不是g的子节点退出
break;
else {//否则就是g的子节点
if (g < 0) { //g是阴节点
if (Tree8Util.keep[line] == 1) //如果是1表示这个节点将从保留状态转为删除状态
keepNodeQTY--;
Tree8Util.keep[line] = 0;
} else if (g > 0) { //g是阳节点
if (Tree8Util.keep[line] == 0) //如果是0表示这个节点将从删除状态转为保留状态
keepNodeQTY++;
Tree8Util.keep[line] = 1;
}
}
}
}
}
}

View File

@ -1,4 +1,4 @@
中国通用人工智能协会官网: http://www.agi-society.cn/index.html
相关项目:
OpenNARS开源项目https://github.com/opennars/opennars
中国通用人工智能协会官网: http://www.agi-society.cn/index.html
相关项目:
OpenNARS开源项目https://github.com/opennars/opennars