diff --git a/README.md b/README.md index fc0affd..c11fba9 100644 --- a/README.md +++ b/README.md @@ -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下有多个子目录,按版本号顺序排列,存放着这个项目演化过程中的主要历史版本供演示。 diff --git a/core/pom.xml b/core/pom.xml index 0dfce8f..002d2e9 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -2,12 +2,12 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 com.gitee.drinkjava2 - frog015 + frog016b jar - 15.0 + 16b.0 frog - 当前目标是用分裂算法来实现第一个具备模式识别功能的神经网络 + 当前目标是用分裂算法来实现模式识别,当前小目标是实现两点输入的模式识别 https://gitee.com/drinkjava2/frog diff --git a/core/src/main/java/com/gitee/drinkjava2/frog/Animal.java b/core/src/main/java/com/gitee/drinkjava2/frog/Animal.java index 9e9ab92..3fc589d 100644 --- a/core/src/main/java/com/gitee/drinkjava2/frog/Animal.java +++ b/core/src/main/java/com/gitee/drinkjava2/frog/Animal.java @@ -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> 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 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 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 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;//要改成由基因调整 - - } - } - } - } diff --git a/core/src/main/java/com/gitee/drinkjava2/frog/Application.java b/core/src/main/java/com/gitee/drinkjava2/frog/Application.java index 886ab80..f9e1622 100644 --- a/core/src/main/java/com/gitee/drinkjava2/frog/Application.java +++ b/core/src/main/java/com/gitee/drinkjava2/frog/Application.java @@ -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() { diff --git a/core/src/main/java/com/gitee/drinkjava2/frog/Env.java b/core/src/main/java/com/gitee/drinkjava2/frog/Env.java index 3de24af..7d442fc 100644 --- a/core/src/main/java/com/gitee/drinkjava2/frog/Env.java +++ b/core/src/main/java/com/gitee/drinkjava2/frog/Env.java @@ -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 frogs = new ArrayList<>(); // 这里存放所有待测的青蛙,可能分几次测完,由FROG_PER_SCREEN大小来决定 + public static ArrayList frogs = new ArrayList<>(); // 这里存放所有待测的青蛙,可能分几次测完,由FROG_PER_SCREEN大小来决定 - public static List frog_eggs = new ArrayList<>(); // 这里存放新建或从磁盘载入上轮下的蛋,每个蛋可能生成几个青蛙, + public static ArrayList 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(); //能量高的青蛙才有权下蛋 diff --git a/core/src/main/java/com/gitee/drinkjava2/frog/brain/BrainPicture.java b/core/src/main/java/com/gitee/drinkjava2/frog/brain/BrainPicture.java index 316a1b0..1c27f3f 100644 --- a/core/src/main/java/com/gitee/drinkjava2/frog/brain/BrainPicture.java +++ b/core/src/main/java/com/gitee/drinkjava2/frog/brain/BrainPicture.java @@ -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,r,x键盘命令 - @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,r,x键盘命令 + @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 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 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; + } } diff --git a/core/src/main/java/com/gitee/drinkjava2/frog/brain/Cell.java b/core/src/main/java/com/gitee/drinkjava2/frog/brain/Cell.java new file mode 100644 index 0000000..464dc11 --- /dev/null +++ b/core/src/main/java/com/gitee/drinkjava2/frog/brain/Cell.java @@ -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 + * + * 从效率的角度出发,Line、Cell这两个对象将来可能都不实际创建,因为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 { +} diff --git a/core/src/main/java/com/gitee/drinkjava2/frog/brain/Consts.java b/core/src/main/java/com/gitee/drinkjava2/frog/brain/Consts.java deleted file mode 100644 index 9a9647b..0000000 --- a/core/src/main/java/com/gitee/drinkjava2/frog/brain/Consts.java +++ /dev/null @@ -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 values = new LinkedHashMap(); - - 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 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]); - } - } - -} diff --git a/core/src/main/java/com/gitee/drinkjava2/frog/brain/Genes.java b/core/src/main/java/com/gitee/drinkjava2/frog/brain/Genes.java index 1f9bc7b..0432bd2 100644 --- a/core/src/main/java/com/gitee/drinkjava2/frog/brain/Genes.java +++ b/core/src/main/java/com/gitee/drinkjava2/frog/brain/Genes.java @@ -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 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 - } - - } - } } } diff --git a/core/src/main/java/com/gitee/drinkjava2/frog/brain/Line.java b/core/src/main/java/com/gitee/drinkjava2/frog/brain/Line.java new file mode 100644 index 0000000..5bc0787 --- /dev/null +++ b/core/src/main/java/com/gitee/drinkjava2/frog/brain/Line.java @@ -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(全通),其它值处于两者之间 + * ovf:overflow 常量溢流阀值, 当传递的能量大于阀值时,高于阀值的部分能量传递 + * ovb: overflow break 常量溃坝阀值, 当传递的能量大于阀值时, 能量全部传递 + * not: not logic 反相器,如>0.5, 将会对通过的能量反相,即乘以-1 + * mul: multiple 乘法器,会对通过的能量值乘以一个倍数,0为1,1时为10倍 + * min: minus 扣能器, 会对细胞的能量扣除一部分,0为0,1时为1,直到细胞能量为0 + * add: add 加能器,会对细胞能量增加一部分,0为0,1时为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为1,1时为10倍 +// float min = a.consts[start++]; //扣能器, 会对细胞的能量扣除一部分,0为0,1时为1,直到细胞能量为0 +// float add = a.consts[start++]; //加能器,会对细胞能量增加一部分,0为0,1时为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); + } + } +} diff --git a/core/src/main/java/com/gitee/drinkjava2/frog/egg/Egg.java b/core/src/main/java/com/gitee/drinkjava2/frog/egg/Egg.java index 223c0f8..769928a 100644 --- a/core/src/main/java/com/gitee/drinkjava2/frog/egg/Egg.java +++ b/core/src/main/java/com/gitee/drinkjava2/frog/egg/Egg.java @@ -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> genes = new ArrayList<>(); - public int[] constGenes = new int[Consts.CountsQTY]; //animal中的全局常量基因全放在这里,用随机数来生成,用遗传算法筛选 + public float[] consts = new float[Animal.CountsQTY]; //全局常量全放在这里,用随机数来生成,用遗传算法筛选 + public ArrayList 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]; } } } diff --git a/core/src/main/java/com/gitee/drinkjava2/frog/egg/FrogEggTool.java b/core/src/main/java/com/gitee/drinkjava2/frog/egg/FrogEggTool.java index f1b77f3..66af5c9 100644 --- a/core/src/main/java/com/gitee/drinkjava2/frog/egg/FrogEggTool.java +++ b/core/src/main/java/com/gitee/drinkjava2/frog/egg/FrogEggTool.java @@ -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) eggsInputStream.readObject(); + Env.frog_eggs = (ArrayList) 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; diff --git a/core/src/main/java/com/gitee/drinkjava2/frog/objects/EnvObject.java b/core/src/main/java/com/gitee/drinkjava2/frog/objects/EnvObject.java index fa69c98..5f78413 100644 --- a/core/src/main/java/com/gitee/drinkjava2/frog/objects/EnvObject.java +++ b/core/src/main/java/com/gitee/drinkjava2/frog/objects/EnvObject.java @@ -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) { } } } \ No newline at end of file diff --git a/core/src/main/java/com/gitee/drinkjava2/frog/objects/Eye.java b/core/src/main/java/com/gitee/drinkjava2/frog/objects/Eye.java deleted file mode 100644 index 3abb9a9..0000000 --- a/core/src/main/java/com/gitee/drinkjava2/frog/objects/Eye.java +++ /dev/null @@ -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; - } - } - -} diff --git a/core/src/main/java/com/gitee/drinkjava2/frog/objects/Food.java b/core/src/main/java/com/gitee/drinkjava2/frog/objects/Food.java index 13f3f05..c015858 100644 --- a/core/src/main/java/com/gitee/drinkjava2/frog/objects/Food.java +++ b/core/src/main/java/com/gitee/drinkjava2/frog/objects/Food.java @@ -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++) { diff --git a/core/src/main/java/com/gitee/drinkjava2/frog/objects/OneDotEye.java b/core/src/main/java/com/gitee/drinkjava2/frog/objects/OneDotEye.java deleted file mode 100644 index 451631b..0000000 --- a/core/src/main/java/com/gitee/drinkjava2/frog/objects/OneDotEye.java +++ /dev/null @@ -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]; - } - } - } -} diff --git a/core/src/main/java/com/gitee/drinkjava2/frog/objects/OneInputJudge.java b/core/src/main/java/com/gitee/drinkjava2/frog/objects/OneInputJudge.java new file mode 100644 index 0000000..30ecb3f --- /dev/null +++ b/core/src/main/java/com/gitee/drinkjava2/frog/objects/OneInputJudge.java @@ -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); + } + } + + } + +} diff --git a/core/src/main/java/com/gitee/drinkjava2/frog/objects/ThreeInputJudge.java b/core/src/main/java/com/gitee/drinkjava2/frog/objects/ThreeInputJudge.java new file mode 100644 index 0000000..e30189e --- /dev/null +++ b/core/src/main/java/com/gitee/drinkjava2/frog/objects/ThreeInputJudge.java @@ -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 { + +} diff --git a/core/src/main/java/com/gitee/drinkjava2/frog/objects/TwoInputJudge.java b/core/src/main/java/com/gitee/drinkjava2/frog/objects/TwoInputJudge.java new file mode 100644 index 0000000..deacab0 --- /dev/null +++ b/core/src/main/java/com/gitee/drinkjava2/frog/objects/TwoInputJudge.java @@ -0,0 +1,13 @@ +package com.gitee.drinkjava2.frog.objects; + +import com.gitee.drinkjava2.frog.objects.EnvObject.DefaultEnvObject; + +/** + * + * TwoInputJudge 用来模拟:当有两个视细胞、一个甜细胞、一个咬细胞这种情况下, 判断青蛙能否根据甜味细胞的训练,在下次相同视信号出现时提前咬下去。 + * 因为只有两个视细胞,即青蛙要实现与、或、异或这三种模式识别。 + * + */ +public class TwoInputJudge extends DefaultEnvObject { + +} diff --git a/core/src/main/java/com/gitee/drinkjava2/frog/temp/TestInput3.java b/core/src/main/java/com/gitee/drinkjava2/frog/temp/TestInput3.java deleted file mode 100644 index e0c2539..0000000 --- a/core/src/main/java/com/gitee/drinkjava2/frog/temp/TestInput3.java +++ /dev/null @@ -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; - } - -} diff --git a/core/src/main/java/com/gitee/drinkjava2/frog/util/Logger.java b/core/src/main/java/com/gitee/drinkjava2/frog/util/Logger.java index aa3d6c9..6654d02 100644 --- a/core/src/main/java/com/gitee/drinkjava2/frog/util/Logger.java +++ b/core/src/main/java/com/gitee/drinkjava2/frog/util/Logger.java @@ -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; diff --git a/core/src/main/java/com/gitee/drinkjava2/frog/util/RandomUtils.java b/core/src/main/java/com/gitee/drinkjava2/frog/util/RandomUtils.java index 16f393e..92d7c58 100644 --- a/core/src/main/java/com/gitee/drinkjava2/frog/util/RandomUtils.java +++ b/core/src/main/java/com/gitee/drinkjava2/frog/util/RandomUtils.java @@ -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; diff --git a/history/016a_OneInput/LICENSE b/history/016a_OneInput/LICENSE new file mode 100644 index 0000000..8dada3e --- /dev/null +++ b/history/016a_OneInput/LICENSE @@ -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. diff --git a/history/016a_OneInput/README.md b/history/016a_OneInput/README.md new file mode 100644 index 0000000..a1e1de8 --- /dev/null +++ b/history/016a_OneInput/README.md @@ -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以上后,可能分辨率就会越来越难做上去了,要考虑引入变焦细胞和动眼细胞了,将总体轮廓识别和局部关键特征识别结合起来提高分辨能力) + + diff --git a/history/016a_OneInput/maven_clean.bat b/history/016a_OneInput/maven_clean.bat new file mode 100644 index 0000000..2f3d3e5 --- /dev/null +++ b/history/016a_OneInput/maven_clean.bat @@ -0,0 +1 @@ +mvn clean \ No newline at end of file diff --git a/history/016a_OneInput/maven_eclipse_clean.bat b/history/016a_OneInput/maven_eclipse_clean.bat new file mode 100644 index 0000000..a427bd7 --- /dev/null +++ b/history/016a_OneInput/maven_eclipse_clean.bat @@ -0,0 +1 @@ +mvn eclipse:clean \ No newline at end of file diff --git a/history/016a_OneInput/maven_eclipse_eclipse.bat b/history/016a_OneInput/maven_eclipse_eclipse.bat new file mode 100644 index 0000000..99fa0b2 --- /dev/null +++ b/history/016a_OneInput/maven_eclipse_eclipse.bat @@ -0,0 +1,2 @@ +call mvn eclipse:eclipse +call pause \ No newline at end of file diff --git a/history/016a_OneInput/pom.xml b/history/016a_OneInput/pom.xml new file mode 100644 index 0000000..e304f9c --- /dev/null +++ b/history/016a_OneInput/pom.xml @@ -0,0 +1,102 @@ + + 4.0.0 + com.gitee.drinkjava2 + frog016a + jar + 16a.0 + + frog + 当前目标是用分裂算法来实现第一个具备模式识别功能的神经网络 + https://gitee.com/drinkjava2/frog + + + gitee Issue + https://gitee.com/drinkjava2/frog/issues + + + + + The Apache Software License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + + + + + + Yong Zhu + yong9981@gmail.com + https://gitee.com/drinkjava2/ + + + + + scm:git@gitee.com:drinkjava2/frog.git + scm:git@gitee.com:drinkjava2/frog.git + git@gitee.com:drinkjava2/frog.git + + + + UTF-8 + UTF-8 + UTF-8 + + 1.8 + 6 + 3.3 + 2.6 + 3.0.0 + 2.7 + 2.19 + 2.6 + 2.4 + 2.10.3 + 1.6 + + + + + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${version.compiler-plugin} + + ${version.java} + ${version.java} + UTF-8 + + + + org.apache.maven.plugins + maven-jar-plugin + 3.1.2 + + + + true + false + lib/ + com.gitee.drinkjava2.frog.Application + + + + + + + + + + + \ No newline at end of file diff --git a/history/016a_OneInput/run.bat b/history/016a_OneInput/run.bat new file mode 100644 index 0000000..0156beb --- /dev/null +++ b/history/016a_OneInput/run.bat @@ -0,0 +1,3 @@ +call mvn clean compile +cd target\classes +java -classpath ".;*" com.gitee.drinkjava2.frog.Application \ No newline at end of file diff --git a/history/016a_OneInput/run.sh b/history/016a_OneInput/run.sh new file mode 100644 index 0000000..836c941 --- /dev/null +++ b/history/016a_OneInput/run.sh @@ -0,0 +1,2 @@ +mvn clean package +java -jar target/frog-*.jar diff --git a/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/Animal.java b/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/Animal.java new file mode 100644 index 0000000..3fc589d --- /dev/null +++ b/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/Animal.java @@ -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代表一条由深度树方式存储的基因树,分表控制细胞的一个参数,当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> genes = new ArrayList<>(); // 基因是多个数列,有点象多条染色体。每个数列都代表一个基因的分裂次序(8叉/4叉/2叉)。 + + public static int CountsQTY = 100; //常量总数多少 + public float[] consts = new float[CountsQTY]; // 常量,范围0~1之间,这些常量并不常,会参与遗传算法筛选,规则是有大概率小变异,小概率大变异 + + public ArrayList 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 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 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]; + } + +} diff --git a/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/Application.java b/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/Application.java new file mode 100644 index 0000000..f9e1622 --- /dev/null +++ b/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/Application.java @@ -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(); + } + +} diff --git a/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/Env.java b/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/Env.java new file mode 100644 index 0000000..7d442fc --- /dev/null +++ b/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/Env.java @@ -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 frogs = new ArrayList<>(); // 这里存放所有待测的青蛙,可能分几次测完,由FROG_PER_SCREEN大小来决定 + + public static ArrayList 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); + } + +} diff --git a/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/Frog.java b/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/Frog.java new file mode 100644 index 0000000..f2dbeb8 --- /dev/null +++ b/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/Frog.java @@ -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; + } +} diff --git a/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/brain/BrainPicture.java b/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/brain/BrainPicture.java new file mode 100644 index 0000000..16393a2 --- /dev/null +++ b/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/brain/BrainPicture.java @@ -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维结构,进行有针对性的改进 + * 可以用鼠标进行平移、缩放、旋转,以及t、f、l、r,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,r,x键盘命令 + @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 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; + } + +} diff --git a/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/brain/Cell.java b/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/brain/Cell.java new file mode 100644 index 0000000..464dc11 --- /dev/null +++ b/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/brain/Cell.java @@ -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 + * + * 从效率的角度出发,Line、Cell这两个对象将来可能都不实际创建,因为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 { +} diff --git a/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/brain/Genes.java b/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/brain/Genes.java new file mode 100644 index 0000000..0432bd2 --- /dev/null +++ b/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/brain/Genes.java @@ -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 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) { + + } + +} diff --git a/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/brain/Line.java b/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/brain/Line.java new file mode 100644 index 0000000..5bc0787 --- /dev/null +++ b/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/brain/Line.java @@ -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(全通),其它值处于两者之间 + * ovf:overflow 常量溢流阀值, 当传递的能量大于阀值时,高于阀值的部分能量传递 + * ovb: overflow break 常量溃坝阀值, 当传递的能量大于阀值时, 能量全部传递 + * not: not logic 反相器,如>0.5, 将会对通过的能量反相,即乘以-1 + * mul: multiple 乘法器,会对通过的能量值乘以一个倍数,0为1,1时为10倍 + * min: minus 扣能器, 会对细胞的能量扣除一部分,0为0,1时为1,直到细胞能量为0 + * add: add 加能器,会对细胞能量增加一部分,0为0,1时为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为1,1时为10倍 +// float min = a.consts[start++]; //扣能器, 会对细胞的能量扣除一部分,0为0,1时为1,直到细胞能量为0 +// float add = a.consts[start++]; //加能器,会对细胞能量增加一部分,0为0,1时为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); + } + } +} diff --git a/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/brain/MouseAction.java b/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/brain/MouseAction.java new file mode 100644 index 0000000..337e5de --- /dev/null +++ b/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/brain/MouseAction.java @@ -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 + } +} diff --git a/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/egg/Egg.java b/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/egg/Egg.java new file mode 100644 index 0000000..769928a --- /dev/null +++ b/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/egg/Egg.java @@ -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> genes = new ArrayList<>(); + public float[] consts = new float[Animal.CountsQTY]; //全局常量全放在这里,用随机数来生成,用遗传算法筛选 + public ArrayList 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 gene : a.genes) {//下蛋就是把动物的基因拷贝到新蛋里,并有可能变异 + ArrayList 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 模拟X、Y 染色体合并,两个蛋生成一个新的蛋 + */ + 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 aGene = a.genes.get(i); + ArrayList 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]; + } + } +} diff --git a/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/egg/FrogEggTool.java b/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/egg/FrogEggTool.java new file mode 100644 index 0000000..66af5c9 --- /dev/null +++ b/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/egg/FrogEggTool.java @@ -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() { + 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) 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."); + } + + } + +} diff --git a/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/objects/EnvObject.java b/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/objects/EnvObject.java new file mode 100644 index 0000000..5f78413 --- /dev/null +++ b/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/objects/EnvObject.java @@ -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) { + } + } +} \ No newline at end of file diff --git a/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/objects/Food.java b/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/objects/Food.java new file mode 100644 index 0000000..c015858 --- /dev/null +++ b/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/objects/Food.java @@ -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; + } +} diff --git a/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/objects/Material.java b/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/objects/Material.java new file mode 100644 index 0000000..2e1931d --- /dev/null +++ b/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/objects/Material.java @@ -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; + } + +} diff --git a/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/objects/OneInputJudge.java b/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/objects/OneInputJudge.java new file mode 100644 index 0000000..30ecb3f --- /dev/null +++ b/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/objects/OneInputJudge.java @@ -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); + } + } + + } + +} diff --git a/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/objects/ThreeInputJudge.java b/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/objects/ThreeInputJudge.java new file mode 100644 index 0000000..e30189e --- /dev/null +++ b/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/objects/ThreeInputJudge.java @@ -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 { + +} diff --git a/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/objects/TwoInputJudge.java b/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/objects/TwoInputJudge.java new file mode 100644 index 0000000..deacab0 --- /dev/null +++ b/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/objects/TwoInputJudge.java @@ -0,0 +1,13 @@ +package com.gitee.drinkjava2.frog.objects; + +import com.gitee.drinkjava2.frog.objects.EnvObject.DefaultEnvObject; + +/** + * + * TwoInputJudge 用来模拟:当有两个视细胞、一个甜细胞、一个咬细胞这种情况下, 判断青蛙能否根据甜味细胞的训练,在下次相同视信号出现时提前咬下去。 + * 因为只有两个视细胞,即青蛙要实现与、或、异或这三种模式识别。 + * + */ +public class TwoInputJudge extends DefaultEnvObject { + +} diff --git a/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/util/ColorUtils.java b/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/util/ColorUtils.java new file mode 100644 index 0000000..bd4150e --- /dev/null +++ b/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/util/ColorUtils.java @@ -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); + } +} diff --git a/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/util/GeneUtils.java b/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/util/GeneUtils.java new file mode 100644 index 0000000..5bc43fd --- /dev/null +++ b/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/util/GeneUtils.java @@ -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 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 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>=0,z没定义,这时基因只能分布在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())); + + } + } + +} diff --git a/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/util/LocalFileUtils.java b/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/util/LocalFileUtils.java new file mode 100644 index 0000000..7422435 --- /dev/null +++ b/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/util/LocalFileUtils.java @@ -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(); + } + } + } + } + +} diff --git a/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/util/Logger.java b/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/util/Logger.java new file mode 100644 index 0000000..6654d02 --- /dev/null +++ b/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/util/Logger.java @@ -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 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 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."); + } + } +} diff --git a/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/util/RandomUtils.java b/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/util/RandomUtils.java new file mode 100644 index 0000000..92d7c58 --- /dev/null +++ b/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/util/RandomUtils.java @@ -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; + } + +} diff --git a/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/util/StringPixelUtils.java b/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/util/StringPixelUtils.java new file mode 100644 index 0000000..9541863 --- /dev/null +++ b/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/util/StringPixelUtils.java @@ -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 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 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("==============="); + } + */ +} diff --git a/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/util/Tree2Util.java b/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/util/Tree2Util.java new file mode 100644 index 0000000..825f6f1 --- /dev/null +++ b/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/util/Tree2Util.java @@ -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 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; + } + } + } + } + } + +} diff --git a/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/util/Tree4Util.java b/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/util/Tree4Util.java new file mode 100644 index 0000000..9b6c803 --- /dev/null +++ b/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/util/Tree4Util.java @@ -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 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; + } + } + } + } + } + +} diff --git a/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/util/Tree8Util.java b/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/util/Tree8Util.java new file mode 100644 index 0000000..a5fde8b --- /dev/null +++ b/history/016a_OneInput/src/main/java/com/gitee/drinkjava2/frog/util/Tree8Util.java @@ -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 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; + } + } + } + } + } + +} diff --git a/other/链接_中国通用人工智能协会 b/other/链接_中国通用人工智能协会 index 1214af1..0c7dbbc 100644 --- a/other/链接_中国通用人工智能协会 +++ b/other/链接_中国通用人工智能协会 @@ -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