From 35dc9d06c98c16fa09223cd29a35f8d2f473ef3e Mon Sep 17 00:00:00 2001 From: yong Date: Sat, 15 Jan 2022 21:28:19 -0700 Subject: [PATCH] Done yin yang algorithm, and copy to hsitory\009c --- README.md | 11 + core/README.md | 3 +- core/pom.xml | 2 +- .../com/gitee/drinkjava2/frog/Animal.java | 103 ++-- .../java/com/gitee/drinkjava2/frog/Env.java | 2 +- .../java/com/gitee/drinkjava2/frog/Frog.java | 2 +- .../drinkjava2/frog/brain/BrainPicture.java | 2 +- .../gitee/drinkjava2/frog/brain/Cells.java | 2 +- .../com/gitee/drinkjava2/frog/egg/Egg.java | 18 +- .../drinkjava2/frog/egg/FrogEggTool.java | 5 - .../drinkjava2/frog/judge/FlowerJudge.java | 85 ++++ .../frog/judge/RainBowFishJudge.java | 84 ++++ .../drinkjava2/frog/util/RandomUtils.java | 9 +- .../gitee/drinkjava2/frog/util/Tree8Util.java | 40 +- history/009b_fish3d/LICENSE | 201 ++++++++ history/009b_fish3d/README.md | 9 + history/009b_fish3d/maven_clean.bat | 1 + history/009b_fish3d/maven_eclipse_clean.bat | 1 + history/009b_fish3d/maven_eclipse_eclipse.bat | 2 + history/009b_fish3d/pom.xml | 102 ++++ history/009b_fish3d/run.bat | 3 + history/009b_fish3d/run.sh | 2 + .../009b_fish3d/src/history - Shortcut.lnk | Bin 0 -> 1084 bytes .../com/gitee/drinkjava2/frog/Animal.java | 198 ++++++++ .../gitee/drinkjava2/frog/Application.java | 169 +++++++ .../java/com/gitee/drinkjava2/frog/Env.java | 325 +++++++++++++ .../java/com/gitee/drinkjava2/frog/Frog.java | 29 ++ .../drinkjava2/frog/brain/BrainPicture.java | 450 ++++++++++++++++++ .../gitee/drinkjava2/frog/brain/Cells.java | 23 + .../gitee/drinkjava2/frog/brain/Cuboid.java | 55 +++ .../drinkjava2/frog/brain/MouseAction.java | 107 +++++ .../com/gitee/drinkjava2/frog/egg/Egg.java | 75 +++ .../drinkjava2/frog/egg/FrogEggTool.java | 117 +++++ .../frog/judge/BrainRainbowColorJudge.java | 0 .../frog/judge/BrainShapeJudge.java | 0 .../drinkjava2/frog/objects/EnvObject.java | 26 + .../gitee/drinkjava2/frog/objects/Food.java | 47 ++ .../drinkjava2/frog/objects/Material.java | 50 ++ .../drinkjava2/frog/util/ColorUtils.java | 62 +++ .../com/gitee/drinkjava2/frog/util/Cube.java | 32 ++ .../drinkjava2/frog/util/LocalFileUtils.java | 141 ++++++ .../gitee/drinkjava2/frog/util/Logger.java | 193 ++++++++ .../drinkjava2/frog/util/RandomUtils.java | 94 ++++ .../frog/util/StringPixelUtils.java | 105 ++++ .../gitee/drinkjava2/frog/util/Tree8Util.java | 85 ++++ history/009c_yinyang/LICENSE | 201 ++++++++ history/009c_yinyang/README.md | 3 + history/009c_yinyang/maven_clean.bat | 1 + history/009c_yinyang/maven_eclipse_clean.bat | 1 + .../009c_yinyang/maven_eclipse_eclipse.bat | 2 + history/009c_yinyang/pom.xml | 102 ++++ history/009c_yinyang/run.bat | 3 + history/009c_yinyang/run.sh | 2 + .../com/gitee/drinkjava2/frog/Animal.java | 239 ++++++++++ .../gitee/drinkjava2/frog/Application.java | 169 +++++++ .../java/com/gitee/drinkjava2/frog/Env.java | 325 +++++++++++++ .../java/com/gitee/drinkjava2/frog/Frog.java | 29 ++ .../drinkjava2/frog/brain/BrainPicture.java | 450 ++++++++++++++++++ .../gitee/drinkjava2/frog/brain/Cells.java | 23 + .../gitee/drinkjava2/frog/brain/Cuboid.java | 55 +++ .../drinkjava2/frog/brain/MouseAction.java | 107 +++++ .../com/gitee/drinkjava2/frog/egg/Egg.java | 75 +++ .../drinkjava2/frog/egg/FrogEggTool.java | 112 +++++ .../drinkjava2/frog/judge/FlowerJudge.java | 85 ++++ .../frog/judge/RainBowFishJudge.java | 84 ++++ .../drinkjava2/frog/objects/EnvObject.java | 26 + .../gitee/drinkjava2/frog/objects/Food.java | 47 ++ .../drinkjava2/frog/objects/Material.java | 50 ++ .../drinkjava2/frog/util/ColorUtils.java | 62 +++ .../com/gitee/drinkjava2/frog/util/Cube.java | 32 ++ .../drinkjava2/frog/util/LocalFileUtils.java | 141 ++++++ .../gitee/drinkjava2/frog/util/Logger.java | 193 ++++++++ .../drinkjava2/frog/util/RandomUtils.java | 101 ++++ .../frog/util/StringPixelUtils.java | 105 ++++ .../gitee/drinkjava2/frog/util/Tree8Util.java | 89 ++++ result16_cell_split.gif | Bin 0 -> 34045 bytes 76 files changed, 5915 insertions(+), 71 deletions(-) create mode 100644 core/src/main/java/com/gitee/drinkjava2/frog/judge/FlowerJudge.java create mode 100644 core/src/main/java/com/gitee/drinkjava2/frog/judge/RainBowFishJudge.java create mode 100644 history/009b_fish3d/LICENSE create mode 100644 history/009b_fish3d/README.md create mode 100644 history/009b_fish3d/maven_clean.bat create mode 100644 history/009b_fish3d/maven_eclipse_clean.bat create mode 100644 history/009b_fish3d/maven_eclipse_eclipse.bat create mode 100644 history/009b_fish3d/pom.xml create mode 100644 history/009b_fish3d/run.bat create mode 100644 history/009b_fish3d/run.sh create mode 100644 history/009b_fish3d/src/history - Shortcut.lnk create mode 100644 history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/Animal.java create mode 100644 history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/Application.java create mode 100644 history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/Env.java create mode 100644 history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/Frog.java create mode 100644 history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/brain/BrainPicture.java create mode 100644 history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/brain/Cells.java create mode 100644 history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/brain/Cuboid.java create mode 100644 history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/brain/MouseAction.java create mode 100644 history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/egg/Egg.java create mode 100644 history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/egg/FrogEggTool.java rename {core => history/009b_fish3d}/src/main/java/com/gitee/drinkjava2/frog/judge/BrainRainbowColorJudge.java (100%) rename {core => history/009b_fish3d}/src/main/java/com/gitee/drinkjava2/frog/judge/BrainShapeJudge.java (100%) create mode 100644 history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/objects/EnvObject.java create mode 100644 history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/objects/Food.java create mode 100644 history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/objects/Material.java create mode 100644 history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/util/ColorUtils.java create mode 100644 history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/util/Cube.java create mode 100644 history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/util/LocalFileUtils.java create mode 100644 history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/util/Logger.java create mode 100644 history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/util/RandomUtils.java create mode 100644 history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/util/StringPixelUtils.java create mode 100644 history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/util/Tree8Util.java create mode 100644 history/009c_yinyang/LICENSE create mode 100644 history/009c_yinyang/README.md create mode 100644 history/009c_yinyang/maven_clean.bat create mode 100644 history/009c_yinyang/maven_eclipse_clean.bat create mode 100644 history/009c_yinyang/maven_eclipse_eclipse.bat create mode 100644 history/009c_yinyang/pom.xml create mode 100644 history/009c_yinyang/run.bat create mode 100644 history/009c_yinyang/run.sh create mode 100644 history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/Animal.java create mode 100644 history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/Application.java create mode 100644 history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/Env.java create mode 100644 history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/Frog.java create mode 100644 history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/brain/BrainPicture.java create mode 100644 history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/brain/Cells.java create mode 100644 history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/brain/Cuboid.java create mode 100644 history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/brain/MouseAction.java create mode 100644 history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/egg/Egg.java create mode 100644 history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/egg/FrogEggTool.java create mode 100644 history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/judge/FlowerJudge.java create mode 100644 history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/judge/RainBowFishJudge.java create mode 100644 history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/objects/EnvObject.java create mode 100644 history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/objects/Food.java create mode 100644 history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/objects/Material.java create mode 100644 history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/util/ColorUtils.java create mode 100644 history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/util/Cube.java create mode 100644 history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/util/LocalFileUtils.java create mode 100644 history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/util/Logger.java create mode 100644 history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/util/RandomUtils.java create mode 100644 history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/util/StringPixelUtils.java create mode 100644 history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/util/Tree8Util.java create mode 100644 result16_cell_split.gif diff --git a/README.md b/README.md index 6d438ea..7dd5c89 100644 --- a/README.md +++ b/README.md @@ -166,6 +166,17 @@ Frog: 这是人工生命的主体,目前起名叫青蛙(Frog),其实叫什 这个动画的每一帧是细胞分裂到最小不可再分的最终结果,而且是从400个青蛙中生存下来的最佳个体,这就是遗传算法,遗传算法就是穷举法。这个16x16x16的大立方体要理解成第一个细胞,只是画的大了而已。以后等有时间可以做1个细胞分成8个,8个变64个的动画,能更好地演示分裂的中间过程。 细胞分裂研究到此结束,下面要开始生成神经网络空间结构了。我的思路是,脑结构也无非就是三维细胞的空间排布而已,细胞有各种参数,比如触突长度、方向、密度、信号收发阀值、信号强度、信号遗忘曲线等,只要每个细胞不超过64个构造参数,就可以用分裂算法来随机试错把神经网络的空间结构给试出来。分裂算法的优点是遵循从主干到细节的生成次序,如果要完成的任务(即外界信号输入输出)也是从简单到复杂,就可能正好符合这个脑的空间结构生成顺序。 +2022-01-03 多参数的细胞分裂的小改进 +位于history\009b目录下,与009a分支相比,算法是相同的,只是作了以下一些小改动: +1.可增加颜色 2.添加存盘选择框,可以在运行期选择存盘 3.添加每维参数分别显示的选择框 4.将颜色参数与细胞位置参数产生关联 5.新增了上次提到的显示分裂过程动画图,见下图: +![result16](result16_cell_split.gif) +因为参数之间有关联,更容易发生缺色现象,这是因为进化过程中,主要参数(细胞位置)的进化会导致次要参数的分支被误删,然后就很难再补回来了。为了解决这个问题,下一个版本打算改进算法,采用黑白节点的方式,黑节点删除节点以下的细胞,白节点保留节点以下的细胞。 +细胞分裂研究算法目前还不能结束(打脸自己),还要先解决这个缺色问题。多参数的进化,如果一旦某个参数被误删除就不能再进化回来,这种算法是不能接受的。 + +2022-01-15 多参数的细胞分裂继续改进:阴阳无极八卦阵算法 +位于history\009c目录下,采用了阴阳(黑白)节点算法,阴节点基因会删除节点下所有节点,是自顶向下的减材加工,阳节点基因会保留节点下所有节点,是自底向上的增材加工。利用遗传算法的大样本筛选,把自顶向下和自底向上两个进化方向结合起来,这样基本能解决误删分支后缺色不能补回这个问题。而且对于奖罚不再象以前一样要设成差距很大的值,animal.java中awardAAAA()原来要设成8000, 现在设成20也不会产生缺色现象。这个版本是研究技术细节问题,看不懂的同学们可以跳过。 +考虑到这个算法的特点,我给它起名“阴阳无极八卦阵算法“,阴阳是指它有阴阳两种节点,无极是指它的分裂阶数没有限制,八卦阵是指它采用了多个8叉树结构,每一维细胞参数都对应一个8叉树。 + ## 运行方式 | Run 运行core或history各个子目录下的run.bat批处理文件即可启动运行,history下有多个子目录,按版本号顺序排列,存放着这个项目演化过程中的主要历史版本供演示。 diff --git a/core/README.md b/core/README.md index d1c5819..198623c 100644 --- a/core/README.md +++ b/core/README.md @@ -1,6 +1,5 @@ ## core目录简介 core目录是当前工作目录,如果跑出什么结果就会拷贝一份放到history目录里存档。 - 原来core分为bottom_up和top_down两个目录,从这版起,还是恢复成一个目录,以集中精力,不管是手工算法试验,还是用细胞分裂自动生成脑结构,都放在core一个目录里,切换时从history里拷贝一份来接着工作即可。 -当前目标是由遗传算法来自动排列脑细胞和触突参数,以实现模式识别功能,并与上下左右运动细胞、进食奖罚感觉细胞结合起来,实现吃掉无毒蘑菇,避开有毒蘑菇这个任务 +当前目标是大方向是由遗传算法来自动排列脑细胞和触突参数,以实现模式识别功能,并与上下左右运动细胞、进食奖罚感觉细胞结合起来,实现吃掉无毒蘑菇,避开有毒蘑菇这个任务。当前小目标是要利用阴阳无极八卦阵算法进化出第一个可以工作(向食物运动)的神经网络。 \ No newline at end of file diff --git a/core/pom.xml b/core/pom.xml index 287c381..023737c 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -7,7 +7,7 @@ 10.0 frog - 当前目标是由遗传算法来自动排列脑细胞和触突参数,以实现模式识别功能,并与上下左右运动细胞、进食奖罚感觉细胞结合起来,实现吃掉无毒蘑菇,避开有毒蘑菇这个任务 + 当前目标是大方向是由遗传算法来自动排列脑细胞和触突参数,以实现模式识别功能,并与上下左右运动细胞、进食奖罚感觉细胞结合起来,实现吃掉无毒蘑菇,避开有毒蘑菇这个任务。 https://gitee.com/drinkjava2/jsqlbox/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 7e91067..a8c68e9 100644 --- a/core/src/main/java/com/gitee/drinkjava2/frog/Animal.java +++ b/core/src/main/java/com/gitee/drinkjava2/frog/Animal.java @@ -21,28 +21,26 @@ import java.util.ArrayList; import javax.imageio.ImageIO; import com.gitee.drinkjava2.frog.egg.Egg; -import com.gitee.drinkjava2.frog.judge.BrainRainbowColorJudge; -import com.gitee.drinkjava2.frog.judge.BrainShapeJudge; +import com.gitee.drinkjava2.frog.judge.RainBowFishJudge; import com.gitee.drinkjava2.frog.objects.Material; import com.gitee.drinkjava2.frog.util.RandomUtils; import com.gitee.drinkjava2.frog.util.Tree8Util; /** * Animal is all artificial lives' father class - * - * Animal是所有动物(青蛙、蛇等)的父类, animal是由蛋孵出来的,蛋里保存着脑细胞结构生成的基因 - * genes是一个list结构, 每一条list代表一条由深度树方式存储的基因树,分表控制细胞的一个参数,用cells长整的一位表示,比如genes.get(0)是控制细胞的存在,即cells三维数组的元素的最低位 + * 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; public static BufferedImage snakeImage; - public ArrayList> genes = new ArrayList<>(); // Animal的基因只保存一份,这是人工生命与实际生物(每个细胞都保留一份基因)的最大不同 + public ArrayList> genes = new ArrayList<>(); // 基因是多个数列,有点象多条染色体 static { try { @@ -58,7 +56,7 @@ public abstract class Animal {// 这个程序大量用到public变量而不是ge public int x; // animal在Env中的x坐标 public int y; // animal在Env中的y坐标 - public long energy = 100000; // 青蛙的能量为0则死掉 + public long energy = 1000000000; // 青蛙的能量为0则死掉 public boolean alive = true; // 设为false表示青蛙死掉了,将不参与计算和显示,以节省时间 public int ateFood = 0; // 青蛙曾吃过的食物总数,下蛋时如果两个青蛙能量相等,可以比数量 public int no; // 青蛙在Env.animals中的序号,从1开始, 会在运行期写到当前brick的最低位,可利用Env.animals.get(no-1)快速定位青蛙 @@ -68,12 +66,12 @@ public abstract class Animal {// 这个程序大量用到public变量而不是ge public Animal(Egg egg) {// x, y 是虑拟环境的坐标 for (int i = 0; i < GENE_NUMBERS; i++) { - ArrayList gene = new ArrayList<>(); - genes.add(gene); + genes.add(new ArrayList<>()); } int i = 0; - for (ArrayList gene : egg.genes) //动物的基因是蛋的基因的拷贝 + for (ArrayList gene : egg.genes)//动物的基因是蛋的基因的拷贝 genes.get(i++).addAll(gene); + i = 0; if (Env.BORN_AT_RANDOM_PLACE) { //是否随机出生在地图上? x = RandomUtils.nextInt(Env.ENV_WIDTH); y = RandomUtils.nextInt(Env.ENV_HEIGHT); @@ -93,9 +91,11 @@ public abstract class Animal {// 这个程序大量用到public变量而不是ge public void initAnimal() { // 初始化animal,生成脑细胞是在这一步,这个方法是在当前屏animal生成之后调用,比方说有一千个青蛙分为500屏测试,每屏只生成2个青蛙的脑细胞,可以节约内存 geneMutation(); //有小概率基因突变 + for (ArrayList gene : genes) //基因多也要适当小扣点分,防止基因无限增长 + energy -= gene.size(); createCellsFromGene(); //运行基因语言,生成脑细胞 - BrainShapeJudge.judge(this); //外界判断,对这个动物打分 - BrainRainbowColorJudge.judge(this); + RainBowFishJudge.judge(this); //外界对是否长得象彩虹鱼打分 + // FlowerJudge.judge(this);//外界对是否长得象小花儿打分 } private static final int MIN_ENERGY_LIMIT = Integer.MIN_VALUE + 5000; @@ -109,15 +109,16 @@ public abstract class Animal {// 这个程序大量用到public变量而不是ge if (energy < MIN_ENERGY_LIMIT) energy = MIN_ENERGY_LIMIT; } - - public void awardAAAA() { changeEnergy(8000);} - public void awardAAA() { changeEnergy(1000);} - public void awardAA() { changeEnergy(100);} //TODO:如果改为20,就可能出现缺色,所以下面要用细胞8叉树从底向上扩张的算法把缺色补上 + + //如果改奖罚值,就可能出现缺色,这个要在基因变异算法(从上到下,从下到上)和环境本身奖罚合理性上下功夫 + public void awardAAAA() { changeEnergy(20);} + public void awardAAA() { changeEnergy(10);} + public void awardAA() { changeEnergy(5);} public void awardA() { changeEnergy(2);} - public void penaltyAAAA() { changeEnergy(-8000);} - public void penaltyAAA() { changeEnergy(-1000);} - public void penaltyAA() { changeEnergy(-100);} + public void penaltyAAAA() { changeEnergy(-20);} + public void penaltyAAA() { changeEnergy(-10);} + public void penaltyAA() { changeEnergy(-5);} public void penaltyA() { changeEnergy(-2);} public void kill() { this.alive = false; changeEnergy(-5); Env.clearMaterial(x, y, animalMaterial); } //kill是最大的惩罚 //@formatter:on @@ -151,14 +152,33 @@ public abstract class Animal {// 这个程序大量用到public变量而不是ge } public void geneMutation() { //基因变异,注意这一个算法同时变异所有条基因,目前最多允许64条基因 - for (int g = 0; g < GENE_NUMBERS; g++) {//依次对每条基因对应的参数,在相应的细胞处把细胞参数位置1 - if (RandomUtils.percent(10)) { //随机新增基因, 在基因里插入一个8叉树位置号,表示这个位置的8叉树整个节点会被敲除 + for (int g = 0; g < GENE_NUMBERS; g++) {//随机新增阴节点基因 + if (RandomUtils.percent(10)) { ArrayList gene = genes.get(g); - Tree8Util.knockNodesByGene(gene);//根据基因,把要敲除的8叉树节点作个标记 - int randomIndex = RandomUtils.nextInt(Tree8Util.ENABLE_NODE_QTY); + Tree8Util.knockNodesByGene(gene);//根据基因,把要敲除的8叉树节点作个标记,下面的算法保证阴节点基因只添加阳节点上 + int randomIndex = RandomUtils.nextInt(Tree8Util.keepNodeQTY); int count = -1; for (int i = 0; i < Tree8Util.NODE_QTY; i++) { - if (Tree8Util.ENABLE[i]) { + if (Tree8Util.keep[i] >= 0) { + count++; + if (count >= randomIndex && !gene.contains(-i)) { + gene.add(-i); + break; + } + } + } + } + } + + for (int g = 0; g < GENE_NUMBERS; g++) {//随机新增阳节点基因 + if (RandomUtils.percent(5)) { + ArrayList gene = genes.get(g); + Tree8Util.knockNodesByGene(gene);//根据基因,把要敲除的8叉树节点作个标记,下面的算法保证阳节点基因只添加在阴节点上 + int yinNodeQTY = Tree8Util.NODE_QTY - Tree8Util.keepNodeQTY; //阴节点总数 + int randomIndex = RandomUtils.nextInt(yinNodeQTY); + int count = -1; + for (int i = 0; i < yinNodeQTY; i++) { + if (Tree8Util.keep[i] < 0) { count++; if (count >= randomIndex && !gene.contains(i)) { gene.add(i); @@ -169,8 +189,29 @@ public abstract class Animal {// 这个程序大量用到public变量而不是ge } } - for (int g = 0; g < GENE_NUMBERS; g++) {//随机变异删除一个基因 - if (RandomUtils.percent(3)) { +// for (int g = 0; g < GENE_NUMBERS; g++) {//随机变异将阳节点向上提升一级,相当于单个细胞的自底向上扩散式生长 +// if (RandomUtils.percent(3)) { +// ArrayList gene = genes.get(g); +// int randomIndex = RandomUtils.nextInt(gene.size()); +// if (randomIndex > 0 && gene.get(randomIndex) > 0) {//如基因是阳基因,且节点不是顶节点 +// int size = Tree8Util.TREE8[randomIndex][0]; +// gene.remove(randomIndex); //先删除底层这个阳基因 +// for (int i = randomIndex - 1; i > 0; i--) { +// if (Tree8Util.TREE8[i][0] > size) { //深度树只要大于size就是它的父节点 +// if (!gene.contains(i)) +// gene.add(i); +// int x = gene.indexOf(-i);//如果有阴节点也删除 +// if (x > 0) +// gene.remove(x); +// break; +// } +// } +// } +// } +// } + + for (int g = 0; g < GENE_NUMBERS; g++) {//随机变异删除一个基因,这样可以去除无用的拉圾基因,防止基因无限增大 + if (RandomUtils.percent(10)) { ArrayList gene = genes.get(g); if (!gene.isEmpty()) gene.remove(RandomUtils.nextInt(gene.size())); @@ -180,14 +221,14 @@ public abstract class Animal {// 这个程序大量用到public变量而不是ge private void createCellsFromGene() {//根据基因生成细胞参数 long geneMask = 1; - for (int g = 0; g < GENE_NUMBERS; g++) {//依次对每条基因对应的参数,在相应的细胞处把细胞参数位置1 + for (int g = 0; g < GENE_NUMBERS; g++) {//动物有多条基因,一条基因控制一维细胞参数,最多有64维,也就是最多有64条基因 ArrayList gene = genes.get(g); Tree8Util.knockNodesByGene(gene);//根据基因,把要敲除的8叉树节点作个标记 - for (int i = 0; i < Tree8Util.NODE_QTY; i++) {//再根据敲剩下的8叉树最小节点标记细胞参数位 - if (Tree8Util.ENABLE[i]) { + 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 - cells[node[1]][node[2]][node[3]] = cells[node[1]][node[2]][node[3]] | geneMask; + cells[node[1]][node[2]][node[3]] = cells[node[1]][node[2]][node[3]] | geneMask; //在相应的细胞处把细胞参数位置1 } } } 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 62cd691..32d937f 100644 --- a/core/src/main/java/com/gitee/drinkjava2/frog/Env.java +++ b/core/src/main/java/com/gitee/drinkjava2/frog/Env.java @@ -83,7 +83,7 @@ public class Env extends JPanel { public static int food_ated = 0; // 用来统计总共多少个食物被青蛙吃掉 - public static int frog_ated = 0; // 用来统计总共多少个食物被青蛙吃掉 + public static int frog_ated = 0; // 用来统计总共多少个青蛙被蛇吃掉 public static boolean pause = false; // 暂停按钮按下将暂停测试 diff --git a/core/src/main/java/com/gitee/drinkjava2/frog/Frog.java b/core/src/main/java/com/gitee/drinkjava2/frog/Frog.java index 35e1e19..f2dbeb8 100644 --- a/core/src/main/java/com/gitee/drinkjava2/frog/Frog.java +++ b/core/src/main/java/com/gitee/drinkjava2/frog/Frog.java @@ -14,7 +14,7 @@ import com.gitee.drinkjava2.frog.egg.Egg; import com.gitee.drinkjava2.frog.objects.Material; /** - * Frog is child class of Animal, Animal's name is Sam. + * Frog is child class of Animal, Frog's name is Sam * Frog是Animal的一个子类 * * @since 1.0 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 a4340c2..a361e84 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 @@ -321,7 +321,7 @@ public class BrainPicture extends JPanel { ArrayList gene = a.genes.get(geneIndex); Tree8Util.knockNodesByGene(gene); for (int j = 0; j < Tree8Util.NODE_QTY; j++) { - if (Tree8Util.ENABLE[j]) { + if (Tree8Util.keep[j]>=0) { int[] node = Tree8Util.TREE8[j]; int size = node[0]; if (size == i && Env.display_gene[geneIndex]) {//如果允许显示的话, 显示当前层级的节点 diff --git a/core/src/main/java/com/gitee/drinkjava2/frog/brain/Cells.java b/core/src/main/java/com/gitee/drinkjava2/frog/brain/Cells.java index 68e238f..b172d80 100644 --- a/core/src/main/java/com/gitee/drinkjava2/frog/brain/Cells.java +++ b/core/src/main/java/com/gitee/drinkjava2/frog/brain/Cells.java @@ -18,6 +18,6 @@ package com.gitee.drinkjava2.frog.brain; */ @SuppressWarnings("all") public class Cells { - public static int GENE_NUMBERS = 5; //目前有多少条基因,每个脑细胞用是一个long来存储,所以最多允许64条基因,每个基因控制一个细胞的参数 + public static int GENE_NUMBERS = 4; //目前有多少条基因,每个脑细胞用是一个long来存储,所以最多允许64条基因,每个基因控制一个细胞的参数 } 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 d2a7c1c..5592971 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 @@ -61,15 +61,15 @@ public class Egg implements Serializable { x = a.x; y = a.y; genes.addAll(a.genes); - if (!genes.isEmpty() && b != null) - for (int i = 0; i < Cells.GENE_NUMBERS; i++) { - if (RandomUtils.percent(10)) { - ArrayList agene = a.genes.get(i); - ArrayList bgene = b.genes.get(i); - if (!bgene.isEmpty()) - agene.add(bgene.get(RandomUtils.nextInt(bgene.size()))); - } - } +// if (!genes.isEmpty() && b != null) +// for (int i = 0; i < Cells.GENE_NUMBERS; i++) { +// if (RandomUtils.percent(3)) { +// ArrayList agene = a.genes.get(i); +// ArrayList bgene = b.genes.get(i); +// if (!bgene.isEmpty()) +// agene.add(bgene.get(RandomUtils.nextInt(bgene.size()))); +// } +// } } } 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 6f849da..0fa2c0e 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 @@ -43,11 +43,6 @@ public class FrogEggTool { * 用能量的多少来简化生存竟争模拟,每次下蛋数量固定为EGG_QTY个 */ public static void layEggs() { - for (Frog frog : Env.frogs) { - for (ArrayList gene : frog.genes) {//基因多的青蛙也要适当扣点分,防止蛋文件太大 - frog.energy-=gene.size(); - } - } sortFrogsOrderByEnergyDesc(); Frog first = Env.frogs.get(0); Frog last = Env.frogs.get(Env.frogs.size() - 1); diff --git a/core/src/main/java/com/gitee/drinkjava2/frog/judge/FlowerJudge.java b/core/src/main/java/com/gitee/drinkjava2/frog/judge/FlowerJudge.java new file mode 100644 index 0000000..f272d74 --- /dev/null +++ b/core/src/main/java/com/gitee/drinkjava2/frog/judge/FlowerJudge.java @@ -0,0 +1,85 @@ +package com.gitee.drinkjava2.frog.judge; + +import java.awt.Font; +import java.util.ArrayList; +import java.util.List; + +import com.gitee.drinkjava2.frog.Animal; +import com.gitee.drinkjava2.frog.Env; +import com.gitee.drinkjava2.frog.brain.BrainPicture; +import com.gitee.drinkjava2.frog.brain.Cells; +import com.gitee.drinkjava2.frog.util.StringPixelUtils; + +/** + * judge method be called in animal's initAnimal method + * + * 这个类的judge方法在动物的初始化后被调用,根据脑细胞群的三维结构和参数来对动物进行奖罚,即加减它的能量值,这是一个临时类,只是用来检验细胞三维成形功能,以后可能改名或删除 + * 这个类的show方法在绘脑图时调用,在脑图里显示脑细胞群的三维形状和参数,用不同颜色直径的空心圆来表示不同参数,judge方法就像是一个模子,细胞长在这个模子里的有奖,否则扣分 + */ +public class FlowerJudge extends RainBowFishJudge {//NOSONAR + private static int[] C = new int[]{0, 0, Env.BRAIN_CUBE_SIZE / 2}; //C是中心点 + private static boolean[][][] shape = new boolean[Env.BRAIN_XSIZE][Env.BRAIN_YSIZE][Env.BRAIN_ZSIZE]; + private static List pointList = new ArrayList<>(); //pointList存放上面shape的所有有效点,用来加快显示循环而不用遍历三维数组 + static { + putPixiel("🌸"); + } + + public static void putPixiel(String str) { + byte[][] c = StringPixelUtils.getStringPixels(Font.SANS_SERIF, Font.PLAIN, Env.BRAIN_CUBE_SIZE, str); //要把frog二维像素变成立体的三维点放到points里和pointsList里供使用 + int w = c.length; + int h = c[0].length; + for (int z = 0; z < 5; z++) { + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + if (c[x][y] > 0) { + int[] p = new int[]{C[0] + x, C[1] + y + 2, C[2] + z}; + if (!Animal.outBrainRange(p[0], p[1]-2, p[2])) {//减2是把花朵向下移两格 + shape[p[0]][p[1]-2][p[2]] = true; + pointList.add(p); + } + } + } + } + } + } + + private static void judgeShape(Animal animal) {//检查animal的脑细胞是否位于brainShape的范围内 + for (int x = 0; x < Env.BRAIN_CUBE_SIZE; x++) { + for (int y = 0; y < Env.BRAIN_CUBE_SIZE; y++) { + for (int z = 0; z < Env.BRAIN_CUBE_SIZE; z++) { + if ((animal.cells[x][y][z] & 1) != 0) + if (shape[x][y][z]) { + animal.awardAAAA(); + } else { + animal.penaltyAAA(); + } + } + } + } + } + + public static void judgeColor(Animal animal) {//检查颜色与小鱼图像重合,且呈斑马纹色彩分布,是就加分,否则扣分 + int colorWidth = Env.BRAIN_CUBE_SIZE / Cells.GENE_NUMBERS; //每条彩带宽度 + for (int x = 0; x < Env.BRAIN_CUBE_SIZE; x++) + for (int y = 0; y < Env.BRAIN_CUBE_SIZE; y++) + for (int z = 0; z < Env.BRAIN_CUBE_SIZE; z++) + for (int i = 1; i <= Cells.GENE_NUMBERS; i++) + if ((animal.cells[x][y][z] & (1 << i)) != 0) { //如果色彩存在 + if ((animal.cells[x][y][z] & 1) != 0 && y >= (i - 1) * colorWidth && y < (i * colorWidth)) + animal.awardAA(); //如果细胞存在且颜色分布符合要求奖励 + else + animal.penaltyA();//否则扣分 + } + } + + public static void judge(Animal animal) {//检查animal的脑细胞是否位于brainShape的范围内 + judgeShape(animal); + judgeColor(animal); + } + + public static void show(BrainPicture pic) {// 在脑图上显示当前形状 + for (int[] p : pointList) + pic.drawCircle(p[0] + 0.5f, p[1] + 0.5f, p[2] + 0.5f, 1); + } + +} diff --git a/core/src/main/java/com/gitee/drinkjava2/frog/judge/RainBowFishJudge.java b/core/src/main/java/com/gitee/drinkjava2/frog/judge/RainBowFishJudge.java new file mode 100644 index 0000000..434a9f3 --- /dev/null +++ b/core/src/main/java/com/gitee/drinkjava2/frog/judge/RainBowFishJudge.java @@ -0,0 +1,84 @@ +package com.gitee.drinkjava2.frog.judge; + +import java.awt.Font; +import java.util.ArrayList; +import java.util.List; + +import com.gitee.drinkjava2.frog.Animal; +import com.gitee.drinkjava2.frog.Env; +import com.gitee.drinkjava2.frog.brain.BrainPicture; +import com.gitee.drinkjava2.frog.brain.Cells; +import com.gitee.drinkjava2.frog.util.StringPixelUtils; + +/** + * judge method be called in animal's initAnimal method + * + * 这个类的judge方法在动物的初始化后被调用,根据脑细胞群的三维结构和参数来对动物进行奖罚,即加减它的能量值,这是一个临时类,只是用来检验细胞三维成形功能,以后可能改名或删除 + * 这个类的show方法在绘脑图时调用,在脑图里显示脑细胞群的三维形状和参数,用不同颜色直径的空心圆来表示不同参数,judge方法就像是一个模子,细胞长在这个模子里的有奖,否则扣分 + */ +public class RainBowFishJudge {//NOSONAR + private static int[] C = new int[]{0, 0, Env.BRAIN_CUBE_SIZE / 2}; //C是中心点 + private static boolean[][][] shape = new boolean[Env.BRAIN_XSIZE][Env.BRAIN_YSIZE][Env.BRAIN_ZSIZE]; + private static List pointList = new ArrayList<>(); //pointList存放上面shape的所有有效点,用来加快显示循环而不用遍历三维数组 + static { + putPixiel("🐟"); + } + + public static void putPixiel(String str) { + byte[][] c = StringPixelUtils.getStringPixels(Font.SANS_SERIF, Font.PLAIN, Env.BRAIN_CUBE_SIZE, str); //要把frog二维像素变成立体的三维点放到points里和pointsList里供使用 + int w = c.length; + int h = c[0].length; + for (int z = 0; z < 5; z++) { + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + if (c[x][y] > 0) { + int[] p = new int[]{C[0] + x, C[1] + y + 2, C[2] + z}; + if (!Animal.outBrainRange(p[0], p[1], p[2])) { + shape[p[0]][p[1]][p[2]] = true; + pointList.add(p); + } + } + } + } + } + } + + private static void judgeShape(Animal animal) {//检查animal的脑细胞是否位于brainShape的范围内 + for (int x = 0; x < Env.BRAIN_CUBE_SIZE; x++) { + for (int y = 0; y < Env.BRAIN_CUBE_SIZE; y++) { + for (int z = 0; z < Env.BRAIN_CUBE_SIZE; z++) { + if ((animal.cells[x][y][z] & 1) != 0) + if (shape[x][y][z]) { + animal.awardAAAA(); + } else { + animal.penaltyAAA(); + } + } + } + } + } + + public static void judgeColor(Animal animal) {//检查颜色与小鱼图像重合,且呈斑马纹色彩分布,是就加分,否则扣分 + int colorWidth = Env.BRAIN_CUBE_SIZE / Cells.GENE_NUMBERS; //每条彩带宽度 + for (int x = 0; x < Env.BRAIN_CUBE_SIZE; x++) + for (int y = 0; y < Env.BRAIN_CUBE_SIZE; y++) + for (int z = 0; z < Env.BRAIN_CUBE_SIZE; z++) + for (int i = 1; i <= Cells.GENE_NUMBERS; i++) + if ((animal.cells[x][y][z] & (1 << i)) != 0) //如果色彩存 + if ((animal.cells[x][y][z] & 1) != 0 && x >= (i - 1) * colorWidth && x < (i * colorWidth)) + animal.awardAA(); + else + animal.penaltyA(); + } + + public static void judge(Animal animal) {//检查animal的脑细胞是否位于brainShape的范围内 + judgeShape(animal); + judgeColor(animal); + } + + public static void show(BrainPicture pic) {// 在脑图上显示当前形状 + for (int[] p : pointList) + pic.drawCircle(p[0] + 0.5f, p[1] + 0.5f, p[2] + 0.5f, 1); + } + +} 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 541f4dd..dff7533 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 @@ -25,11 +25,18 @@ public class RandomUtils { private static final Random rand = new Random(); - public static int nextInt(int i) { + 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(); diff --git a/core/src/main/java/com/gitee/drinkjava2/frog/util/Tree8Util.java b/core/src/main/java/com/gitee/drinkjava2/frog/util/Tree8Util.java index ddcb228..eb9ff4a 100644 --- a/core/src/main/java/com/gitee/drinkjava2/frog/util/Tree8Util.java +++ b/core/src/main/java/com/gitee/drinkjava2/frog/util/Tree8Util.java @@ -29,9 +29,11 @@ public class Tree8Util { public static int[][] TREE8 = new int[NODE_QTY][4]; //八叉数用数组表示,第一维是深度树的行号,第二维是一个整数数组,内容是深度树表示的八叉树细胞的size, x, y, z值 - public static boolean[] ENABLE = new boolean[NODE_QTY]; //这里记录树的敲除记录,被敲除的节点用false表示 + public static byte[] keep = new byte[NODE_QTY]; //这里临时记录树的敲除记录,大于等于0的值表示要keep, 负数节点表示要敲除 - public static int ENABLE_NODE_QTY = NODE_QTY; //这里记录未被敲除的总节点数,好用来下次继续敲除 + private static byte[] KEEP = new byte[NODE_QTY]; //这里保存初值为0的数组常量,可以用System.arraycopy(enable0, 0, enable, 0, NODE_QTY)快速清空enable数组 + + public static int keepNodeQTY = NODE_QTY; //这里临时记录需keep的节点总数,好用来继续敲除,初始值是全部节点 private static int index = 0; static { @@ -60,22 +62,24 @@ public class Tree8Util { tree8Split(x + half, y + half, z + half, half); } - public static void knockNodesByGene(List gene) {//根据基因,把要敲除的8叉树节点作个标记0 - for (int i = 0; i < Tree8Util.NODE_QTY; i++) - ENABLE[i] = true; - ENABLE_NODE_QTY = NODE_QTY; - for (int g : gene) {//g是要敲除的节点的行号 - if (Tree8Util.ENABLE[g]) { - int gSize = Tree8Util.TREE8[g][0]; //gSize是节点对应的立方体边长 - for (int i = g; i < Tree8Util.NODE_QTY; i++) {//从这个g节点开始,往下找节点 - int iSize = Tree8Util.TREE8[i][0]; - if (i > g && iSize >= gSize) //如果除了第一个节点外,边长与g相同或大于g的边长,说明节点不是g的子节点,退出 - break; - else {//否则就是g的子节点,需要敲除 - if (Tree8Util.ENABLE[i]) { //如是还没敲除 - ENABLE_NODE_QTY--; //有效节点数减1,这个 - Tree8Util.ENABLE[i] = false; //作敲除标记 - } + public static void knockNodesByGene(List gene) {//根据基因,把要敲除的8叉树节点作敲除或保留标记 + System.arraycopy(KEEP, 0, keep, 0, NODE_QTY);//清空keep数组 + keepNodeQTY = NODE_QTY; + for (int g : gene) {//g基因,用带符号的8叉数的行号表示,负数表示阴节点要敲除,0或正数表示是阳节点要保留 + 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是阴节点 + Tree8Util.keep[line]--; + if (Tree8Util.keep[line] == -1) //如果正好是-1,表示这个节点从保留状态转为删除状态 + keepNodeQTY--; + } else if (g > 0) { //g是阳节点 + Tree8Util.keep[line]++; + if (Tree8Util.keep[line] == 0) //如果正好是0,表示这个节点从删除状态又转回了保留状态 + keepNodeQTY++; } } } diff --git a/history/009b_fish3d/LICENSE b/history/009b_fish3d/LICENSE new file mode 100644 index 0000000..8dada3e --- /dev/null +++ b/history/009b_fish3d/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/009b_fish3d/README.md b/history/009b_fish3d/README.md new file mode 100644 index 0000000..c7859a7 --- /dev/null +++ b/history/009b_fish3d/README.md @@ -0,0 +1,9 @@ +## 009b_fish3d 分支介绍 +009b与009a分支相比,算法是相同的,只是作了以下一些小改动: +1.增加了颜色数,在Cells.GENE_NUMBERS中设定 +2.增加了存盘到文件选择框,可以在运行期点击再存盘 +3.增加了每维参数分别显示的选择框,每个参数可以单独显示进化过程和分裂过程 +4.增加了显示分裂详细过程按钮 +5.BrainRainbowColorJudge.java里将颜色参数与细胞位置参数产生关联,也就是说参数之间有关联关系,这种情况下更容易发生缺色现象出现,这是因为进化过程中,主要参数(细胞位置)的进化会导致个别次要参数的分支被误删,然后就很难再补回来了。 +为了解决这个缺色问题,,也就是分支一旦被误删就很难再补回来的问题,下一个版本打算改变算法,采用黑白节点的方式,白节点以下为无限分裂并生成细胞,黑节点依然是分裂但不生成实际细胞,除非碰到白节点为止。以前做法是只有根节点是白节点,以后改成在任意位置随机生成黑或白节点。这样变异就包括:黑白节点的生成、消失、上下左右漂移、黑白转换等。 + diff --git a/history/009b_fish3d/maven_clean.bat b/history/009b_fish3d/maven_clean.bat new file mode 100644 index 0000000..2f3d3e5 --- /dev/null +++ b/history/009b_fish3d/maven_clean.bat @@ -0,0 +1 @@ +mvn clean \ No newline at end of file diff --git a/history/009b_fish3d/maven_eclipse_clean.bat b/history/009b_fish3d/maven_eclipse_clean.bat new file mode 100644 index 0000000..a427bd7 --- /dev/null +++ b/history/009b_fish3d/maven_eclipse_clean.bat @@ -0,0 +1 @@ +mvn eclipse:clean \ No newline at end of file diff --git a/history/009b_fish3d/maven_eclipse_eclipse.bat b/history/009b_fish3d/maven_eclipse_eclipse.bat new file mode 100644 index 0000000..99fa0b2 --- /dev/null +++ b/history/009b_fish3d/maven_eclipse_eclipse.bat @@ -0,0 +1,2 @@ +call mvn eclipse:eclipse +call pause \ No newline at end of file diff --git a/history/009b_fish3d/pom.xml b/history/009b_fish3d/pom.xml new file mode 100644 index 0000000..1ddb46d --- /dev/null +++ b/history/009b_fish3d/pom.xml @@ -0,0 +1,102 @@ + + 4.0.0 + com.gitee.drinkjava2 + frog009b + jar + 9.0 + + frog + 当前目标是由遗传算法来自动排列脑细胞和触突参数,以实现模式识别功能,并与上下左右运动细胞、进食奖罚感觉细胞结合起来,实现吃掉无毒蘑菇,避开有毒蘑菇这个任务 + https://gitee.com/drinkjava2/jsqlbox/frog + + + gitee Issue + https://gitee.com/drinkjava2/jsqlbox/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/009b_fish3d/run.bat b/history/009b_fish3d/run.bat new file mode 100644 index 0000000..0156beb --- /dev/null +++ b/history/009b_fish3d/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/009b_fish3d/run.sh b/history/009b_fish3d/run.sh new file mode 100644 index 0000000..836c941 --- /dev/null +++ b/history/009b_fish3d/run.sh @@ -0,0 +1,2 @@ +mvn clean package +java -jar target/frog-*.jar diff --git a/history/009b_fish3d/src/history - Shortcut.lnk b/history/009b_fish3d/src/history - Shortcut.lnk new file mode 100644 index 0000000000000000000000000000000000000000..1c2730c3060c50fbb4fcac3ef4ce15e3267d3825 GIT binary patch literal 1084 zcma)5T}YE*6n;LnniV;1N|`oOl!dgmB&6X|O6QDnzl9ecwEWrj&B-^n8A)9!6yby| z>>{Ev@rTGR3POkqueypFUfAlYyP!Yus=5d}=W}kNK^=I`d*1V&_c`Z1=lf0r5HvZY zfq6y4G6EIUWVa>8wmnO|6U_Iw=E_J3sovMF^Rz05bS|vMXY{b3f!6-fmaO<}lGGC^ z6>1HJJ9}}s#Uy5evW6W-Ws`E&b*SMBh2;PmkdHhpy&QJb7`{7I$MqB;IXG-%vZK~& z!G{pSkP)DB4LoVSPfnqd_@tmduGu;@R+&v{<~~)13nmu9PS59!qKOOdD? z9z;`yFF|}mI_9&UWtzryTx{yzSgA${5vov*g_0?h>G^C-XHIvl_hu~6qQ4Vyb-CsyIwNl>Pl+vl%wdx z6Mgxuf>&jW{*kxO5&T*>bEDx(qj;w4U*`|0=zL(jhq#R{yX%|b#fOu)-a7L$xqC9= zt!yKIHz(%i?jx*?rvf^9jETz3eiCeN9^^s3u1K6TCowEn&n>lvasT&FG1DAz(+ijlI7Z-vk4yTRAB~2rGu9^k hr1?^;ocw+cuo51u_;-x`o@l=|)ohGu;w$W5lRsyi+g$(v literal 0 HcmV?d00001 diff --git a/history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/Animal.java b/history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/Animal.java new file mode 100644 index 0000000..7e91067 --- /dev/null +++ b/history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/Animal.java @@ -0,0 +1,198 @@ +/* + * 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.Cells.GENE_NUMBERS; + +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.egg.Egg; +import com.gitee.drinkjava2.frog.judge.BrainRainbowColorJudge; +import com.gitee.drinkjava2.frog.judge.BrainShapeJudge; +import com.gitee.drinkjava2.frog.objects.Material; +import com.gitee.drinkjava2.frog.util.RandomUtils; +import com.gitee.drinkjava2.frog.util.Tree8Util; + +/** + * Animal is all artificial lives' father class + * + * Animal是所有动物(青蛙、蛇等)的父类, animal是由蛋孵出来的,蛋里保存着脑细胞结构生成的基因 + * genes是一个list结构, 每一条list代表一条由深度树方式存储的基因树,分表控制细胞的一个参数,用cells长整的一位表示,比如genes.get(0)是控制细胞的存在,即cells三维数组的元素的最低位 + * + * + * @author Yong Zhu + * + * @since 1.0 + */ +public abstract class Animal {// 这个程序大量用到public变量而不是getter/setter,主要是为了编程方便和简洁,但缺点是编程者需要小心维护各个变量 + public static BufferedImage FROG_IMAGE; + public static BufferedImage snakeImage; + + public ArrayList> genes = new ArrayList<>(); // Animal的基因只保存一份,这是人工生命与实际生物(每个细胞都保留一份基因)的最大不同 + + static { + try { + FROG_IMAGE = ImageIO.read(new FileInputStream(Application.CLASSPATH + "frog.png")); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** brain cells */ + public long[][][] cells = new long[Env.BRAIN_CUBE_SIZE][Env.BRAIN_CUBE_SIZE][Env.BRAIN_CUBE_SIZE]; + public float[][][] energys = new float[Env.BRAIN_CUBE_SIZE][Env.BRAIN_CUBE_SIZE][Env.BRAIN_CUBE_SIZE]; + + public int x; // animal在Env中的x坐标 + public int y; // animal在Env中的y坐标 + public long energy = 100000; // 青蛙的能量为0则死掉 + public boolean alive = true; // 设为false表示青蛙死掉了,将不参与计算和显示,以节省时间 + public int ateFood = 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 是虑拟环境的坐标 + for (int i = 0; i < GENE_NUMBERS; i++) { + ArrayList gene = new ArrayList<>(); + genes.add(gene); + } + int i = 0; + for (ArrayList gene : egg.genes) //动物的基因是蛋的基因的拷贝 + genes.get(i++).addAll(gene); + if (Env.BORN_AT_RANDOM_PLACE) { //是否随机出生在地图上? + x = RandomUtils.nextInt(Env.ENV_WIDTH); + y = RandomUtils.nextInt(Env.ENV_HEIGHT); + } else {//否则出生成指定区域 + this.x = egg.x + RandomUtils.nextInt(80) - 40; + this.y = egg.y + RandomUtils.nextInt(80) - 40; + if (this.x < 0) + this.x = 0; + if (this.y < 0) + this.y = 0; + if (this.x >= (Env.ENV_WIDTH - 1)) + this.x = Env.ENV_WIDTH - 1; + if (this.y >= (Env.ENV_HEIGHT - 1)) + this.y = Env.ENV_HEIGHT - 1; + } + } + + public void initAnimal() { // 初始化animal,生成脑细胞是在这一步,这个方法是在当前屏animal生成之后调用,比方说有一千个青蛙分为500屏测试,每屏只生成2个青蛙的脑细胞,可以节约内存 + geneMutation(); //有小概率基因突变 + createCellsFromGene(); //运行基因语言,生成脑细胞 + BrainShapeJudge.judge(this); //外界判断,对这个动物打分 + BrainRainbowColorJudge.judge(this); + } + + private static final int MIN_ENERGY_LIMIT = Integer.MIN_VALUE + 5000; + private static final int MAX_ENERGY_LIMIT = Integer.MAX_VALUE - 5000; + + //@formatter:off 下面几行是重要的奖罚方法,会经常调整或注释掉,集中放在一起,不要格式化为多行 + public void changeEnergy(int energy_) {//正数为奖励,负数为惩罚, energy大小是环境对animal唯一的奖罚,也是animal唯一的下蛋竞争标准 + energy += energy_; + if (energy > MAX_ENERGY_LIMIT) + energy = MAX_ENERGY_LIMIT; + if (energy < MIN_ENERGY_LIMIT) + energy = MIN_ENERGY_LIMIT; + } + + public void awardAAAA() { changeEnergy(8000);} + public void awardAAA() { changeEnergy(1000);} + public void awardAA() { changeEnergy(100);} //TODO:如果改为20,就可能出现缺色,所以下面要用细胞8叉树从底向上扩张的算法把缺色补上 + public void awardA() { changeEnergy(2);} + + public void penaltyAAAA() { changeEnergy(-8000);} + public void penaltyAAA() { changeEnergy(-1000);} + public void penaltyAA() { changeEnergy(-100);} + public void penaltyA() { changeEnergy(-2);} + public void kill() { this.alive = false; changeEnergy(-5); Env.clearMaterial(x, y, animalMaterial); } //kill是最大的惩罚 + //@formatter:on + + public boolean active() {// 这个active方法在每一步循环都会被调用,是脑思考的最小帧 + // 如果能量小于0、出界、与非食物的点重合则判死 + if (!alive) { + energy = MIN_ENERGY_LIMIT; // 死掉的青蛙确保淘汰出局 + return false; + } + if (energy <= 0 || Env.outsideEnv(x, y) || Env.bricks[x][y] >= Material.KILL_ANIMAL) { + kill(); + return false; + } + //energy -= 20; + // 依次调用每个cell的active方法 + //for (Cell cell : cells) + // cell.organ.active(this, cell); + return alive; + } + + public void show(Graphics g) {// 显示当前动物 + if (!alive) + return; + // g.drawImage(animalImage, x - 8, y - 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_XSIZE || y < 0 || y >= Env.BRAIN_YSIZE || z < 0 || z >= Env.BRAIN_ZSIZE; + } + + public void geneMutation() { //基因变异,注意这一个算法同时变异所有条基因,目前最多允许64条基因 + for (int g = 0; g < GENE_NUMBERS; g++) {//依次对每条基因对应的参数,在相应的细胞处把细胞参数位置1 + if (RandomUtils.percent(10)) { //随机新增基因, 在基因里插入一个8叉树位置号,表示这个位置的8叉树整个节点会被敲除 + ArrayList gene = genes.get(g); + Tree8Util.knockNodesByGene(gene);//根据基因,把要敲除的8叉树节点作个标记 + int randomIndex = RandomUtils.nextInt(Tree8Util.ENABLE_NODE_QTY); + int count = -1; + for (int i = 0; i < Tree8Util.NODE_QTY; i++) { + if (Tree8Util.ENABLE[i]) { + count++; + if (count >= randomIndex && !gene.contains(i)) { + gene.add(i); + break; + } + } + } + } + } + + for (int g = 0; g < GENE_NUMBERS; g++) {//随机变异删除一个基因 + if (RandomUtils.percent(3)) { + ArrayList gene = genes.get(g); + if (!gene.isEmpty()) + gene.remove(RandomUtils.nextInt(gene.size())); + } + } + } + + private void createCellsFromGene() {//根据基因生成细胞参数 + long geneMask = 1; + for (int g = 0; g < GENE_NUMBERS; g++) {//依次对每条基因对应的参数,在相应的细胞处把细胞参数位置1 + ArrayList gene = genes.get(g); + Tree8Util.knockNodesByGene(gene);//根据基因,把要敲除的8叉树节点作个标记 + for (int i = 0; i < Tree8Util.NODE_QTY; i++) {//再根据敲剩下的8叉树最小节点标记细胞参数位 + if (Tree8Util.ENABLE[i]) { + int[] node = Tree8Util.TREE8[i]; + if (node[0] == 1) {//如果node边长为1,即不可以再分裂了,就在三维空间对间数组的位置把当前基因geneMask置1 + cells[node[1]][node[2]][node[3]] = cells[node[1]][node[2]][node[3]] | geneMask; + } + } + } + geneMask <<= 1; + } + } + +} diff --git a/history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/Application.java b/history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/Application.java new file mode 100644 index 0000000..31f13a8 --- /dev/null +++ b/history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/Application.java @@ -0,0 +1,169 @@ +package com.gitee.drinkjava2.frog; + +import java.awt.Component; +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.Cells; + +/** + * Application's main method start the program + * + * @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_XSIZE, 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 < Cells.GENE_NUMBERS; i++) { + JRadioButton geneRadio=new JRadioButton(); + geneRadio.setBounds(buttonXpos+300+i*20, Env.ENV_HEIGHT + 8, 20, 22); + geneRadio.setSelected(true); + geneRadio.setName(""+i); + ActionListener geneRadioAction = new ActionListener() { + public void actionPerformed(ActionEvent e) { + int i= Integer.parseInt(geneRadio.getName()); + if (geneRadio.isSelected()) + Env.display_gene[i]=true; + else + Env.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.setVisible(true); + env.run(); + } + +} diff --git a/history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/Env.java b/history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/Env.java new file mode 100644 index 0000000..62cd691 --- /dev/null +++ b/history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/Env.java @@ -0,0 +1,325 @@ +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 java.util.List; + +import javax.swing.JPanel; + +import com.gitee.drinkjava2.frog.brain.Cells; +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.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 = 100; // 每轮下n个青蛙蛋,可调,只有最优秀的前n个青蛙们才允许下蛋 + + public static final int FROG_PER_EGG = 4; // 每个青蛙蛋可以孵出几个青蛙 + + public static final int SCREEN = 1; // 分几屏测完 + + /** 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中用一个List来存贮表示的同时,也用一个Cell3D动态数组来表示 + + public static final int BRAIN_CUBE_SIZE = 16; //脑立方边长大小,必须是2的幂数如4,8,16... + public static final int BRAIN_XSIZE = BRAIN_CUBE_SIZE; // 脑在X方向长度,取值最大为1000 + public static final int BRAIN_YSIZE = BRAIN_CUBE_SIZE; // 脑在Y方向长度,取值最大为1000 + public static final int BRAIN_ZSIZE = BRAIN_CUBE_SIZE; // 脑在Z方向长度,取值最大为1000 + + public static final int CELLS_MAX_QTY = 4000; //脑细胞总数不能超过这个值 + + /** 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 = 2000;// 每轮测试步数,可调 + public static int step;// 当前测试步数 + + public static final int FOOD_QTY = 1500; // 食物数量, 可调 + + // 以下是程序内部变量,不要手工修改它们 + 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 int food_ated = 0; // 用来统计总共多少个食物被青蛙吃掉 + + public static int frog_ated = 0; // 用来统计总共多少个食物被青蛙吃掉 + + public static boolean pause = false; // 暂停按钮按下将暂停测试 + + public static int[][] bricks = new int[ENV_WIDTH][ENV_HEIGHT];// 组成环境的材料,见Material.java + + public static List frogs = new ArrayList<>(); // 这里存放所有待测的青蛙,可能分几次测完,由FROG_PER_SCREEN大小来决定 + + public static List frog_eggs = new ArrayList<>(); // 这里存放新建或从磁盘载入上轮下的蛋,每个蛋可能生成几个青蛙, + + public static EnvObject[] things = new EnvObject[]{ };// 所有外界物体,如食物、字母测试工具都放在这个things里面 + + public static boolean show_split_detail=false; //是否显示脑分裂的细节过程,即从一个细胞开始分裂分裂,而不是只显示分裂的最终结果 + + public static boolean[] display_gene=new boolean[Cells.GENE_NUMBERS]; //脑最多有64个基因,这里用来控制哪些基因需要显示在脑图上 + + static { + Logger.info("唵缚悉波罗摩尼莎诃!"); // 杀生前先打印往生咒,见码云issue#IW4H8 + Logger.info("脑图快捷键: T:顶视 F:前视 L:左视 R:右视 X:斜视 方向键:剖视 空格:暂停 鼠标:缩放旋转平移"); + if (DELETE_FROG_EGGS) + FrogEggTool.deleteEggs(); + for (int i = 0; i < display_gene.length; i++) + display_gene[i]=true; + } + + public Env() { + super(); + this.setLayout(null);// 空布局 + this.setBounds(1, 1, ENV_WIDTH, ENV_HEIGHT); + } + + 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.x < 20 || a.y < 20 || a.x > (Env.ENV_WIDTH - 20) || a.y > (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 foundAndAteFood(int x, int y) {// 如果x,y有食物,将其清0,返回true + if (insideEnv(x, y) && (Env.bricks[x][y] & Material.FOOD) > 0) { + Env.food_ated++; + clearMaterial(x, y, Material.FOOD);// 清空任意食物 + return true; + } + return false; + } + + 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(Env.food_ated * 1.00 / FOOD_QTY)).append(", 平均: ").append(Env.food_ated * 1.0f / FROG_PER_SCREEN).append(",最多:").append(maxFound) + .toString(); + } + + private String frogAtedCount() {// 统计食蛙总数 + return new StringBuilder("吃蛙率:").append(format100.format(Env.frog_ated * 1.00 / TOTAL_FROG_QTY)).toString(); + } + + public static void checkIfPause() { + if (pause) + do { + Application.brainPic.drawBrainPicture(); + 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 void run() { + FrogEggTool.loadFrogEggs(); // 从磁盘加载蛙egg,或新建一批egg + Image buffImg = createImage(this.getWidth(), this.getHeight()); + Graphics g = buffImg.getGraphics(); + long time0;// 计时用 + int round = 1; + do { + rebuildFrogs(); // 根据蛙蛋重新孵化出蛙,注意基因变异有可能在孵化过程中发生 + for (current_screen = 0; current_screen < SCREEN; current_screen++) {// 分屏测试,每屏FROG_PER_SCREEN个蛙 + Env.food_ated = 0; // 先清0吃食物数 + Env.frog_ated = 0;// 先清0吃蛙数 + time0 = System.currentTimeMillis(); + for (EnvObject thing : things) // 创建食物、陷阱等物体 + thing.build(); + 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(); + if (allDead) + break; // 青蛙全死光了就直接跳到下一轮,以节省时间 + allDead = true; + for (int j = 0; j < FROG_PER_SCREEN; j++) { + Frog f = frogs.get(current_screen * FROG_PER_SCREEN + j); + if (f.active())// 调用青蛙的Active方法,并返回是否还活着 + allDead = false; + } + + if (SHOW_SPEED > 0 && step % SHOW_SPEED != 0) // 用是否跳帧画图的方式来控制速度 + continue; + + if (SHOW_SPEED < 0) // 如果speed小于0,人为加入延迟 + sleep(-SHOW_SPEED); + + // 开始画虚拟环境和青蛙和蛇 + g.setColor(Color.white); + g.fillRect(0, 0, this.getWidth(), this.getHeight()); // 先清空虚拟环境 + g.setColor(Color.BLACK); + drawWorld(g);// 画整个虚拟环境 + for (int j = 0; j < FROG_PER_SCREEN; j++) { // 显示青蛙 + Frog f = frogs.get(current_screen * FROG_PER_SCREEN + j); + f.show(g); + } + + if (SHOW_FIRST_ANIMAL_BRAIN) {// 在showAnimal上画一个红圈 + Animal showAnimal = getShowAnimal(); + if (showAnimal != null) { + g.setColor(Color.red); + g.drawArc(showAnimal.x - 15, showAnimal.y - 15, 30, 30, 0, 360); + g.setColor(Color.BLACK); + } + } + if (DRAW_BRAIN_AFTER_STEPS > 0 && step % DRAW_BRAIN_AFTER_STEPS == 0) + Application.brainPic.drawBrainPicture(); + Graphics g2 = this.getGraphics(); + g2.drawImage(buffImg, 0, 0, this); + } + if (SHOW_FIRST_ANIMAL_BRAIN) + Application.brainPic.drawBrainPicture(); + checkIfPause(); + 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(foodAtedCount()); + + Application.mainFrame.setTitle(sb.toString()); + // for (EnvObject thing : things)// 去除食物、陷阱等物体 + // thing.destory(); + for (int i = 0; i < ENV_WIDTH; i++) {// 清除食物 + for (int j = 0; j < ENV_HEIGHT; j++) + bricks[i][j] = 0; + } + } + round++; + FrogEggTool.layEggs(); //能量高的青蛙才有权下蛋 + } while (true); + } + +} diff --git a/history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/Frog.java b/history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/Frog.java new file mode 100644 index 0000000..35e1e19 --- /dev/null +++ b/history/009b_fish3d/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, Animal'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/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/brain/BrainPicture.java b/history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/brain/BrainPicture.java new file mode 100644 index 0000000..a4340c2 --- /dev/null +++ b/history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/brain/BrainPicture.java @@ -0,0 +1,450 @@ +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_YSIZE) + yMask = Env.BRAIN_YSIZE; + 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_XSIZE) + xMask = Env.BRAIN_XSIZE; + 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(Cuboid c) {// 在脑图上画一个长立方体框架,视角是TopView + drawCuboid(c.x, c.y, c.z, c.xe, c.ye, c.ze); + } + + 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); + } + + /*- + 画线,固定以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_XSIZE / 2; + double y1 = -py1 + Env.BRAIN_YSIZE / 2;// 屏幕的y坐标是反的,显示时要正过来 + double z1 = pz1 - Env.BRAIN_ZSIZE / 2; + double x2 = px2 - Env.BRAIN_XSIZE / 2; + double y2 = -py2 + Env.BRAIN_YSIZE / 2;// 屏幕的y坐标是反的,显示时要正过来 + double z2 = pz2 - Env.BRAIN_ZSIZE / 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); + } + + /** 画点,固定以top视角的角度,所以只需要在x1,y1位置画一个点 */ + public void drawPoint(float px1, float py1, float pz1, float r) { + double x1 = px1 - Env.BRAIN_XSIZE / 2; + double y1 = -py1 + Env.BRAIN_YSIZE / 2;// 屏幕的y坐标是反的,显示时要正过来 + double z1 = pz1 - Env.BRAIN_ZSIZE / 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_XSIZE / 2; + double y1 = -py1 + Env.BRAIN_YSIZE / 2;// 屏幕的y坐标是反的,显示时要正过来 + double z1 = pz1 - Env.BRAIN_ZSIZE / 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 drawText(float px1, float py1, float pz1, String text, float textSize) { + double x1 = px1 - Env.BRAIN_XSIZE / 2; + double y1 = -py1 + Env.BRAIN_YSIZE / 2;// 屏幕的y坐标是反的,显示时要正过来 + double z1 = pz1 - Env.BRAIN_ZSIZE / 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); + + } + + private static Cuboid brain = new Cuboid(0, 0, 0, Env.BRAIN_XSIZE, Env.BRAIN_YSIZE, Env.BRAIN_ZSIZE); + + + + public void drawBrainPicture() {// 在这个方法里进行动物的三维脑结构的绘制,蛇是青蛙的子类,所以也可以当参数传进来 + if (!Env.SHOW_FIRST_ANIMAL_BRAIN) + return; + if(Env.show_split_detail) + drawSplitDetail(); + else + drawBrainStructure(); + } + + public void drawSplitDetail() {// 在这个方法里绘制脑细胞分裂的显示步聚,即从一个细胞开始分裂成最终脑结构的每一步 + Animal a = Env.getShowAnimal(); // 第一个青蛙或蛇 + + for (int i = Env.BRAIN_CUBE_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 < Cells.GENE_NUMBERS; geneIndex++) { + ArrayList gene = a.genes.get(geneIndex); + Tree8Util.knockNodesByGene(gene); + for (int j = 0; j < Tree8Util.NODE_QTY; j++) { + if (Tree8Util.ENABLE[j]) { + int[] node = Tree8Util.TREE8[j]; + int size = node[0]; + if (size == i && Env.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(brain);// 把脑的框架画出来 + this.getGraphics().drawImage(buffImg, 0, 0, this);// 利用缓存避免画面闪烁,这里输出缓存图片 + if (!Env.show_split_detail) + return; + try { + Thread.sleep(500); + } catch (InterruptedException e) { + } + } + } + + public void drawBrainStructure() {// 在这个方法里进行动物的三维脑结构的绘制,蛇是青蛙的子类,所以也可以当参数传进来 + 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_CUBE_SIZE; z++) { + for (int y = Env.BRAIN_CUBE_SIZE - 1; y >= 0; y--) { + for (int x = Env.BRAIN_CUBE_SIZE - 1; x >= 0; x--) { + if (x >= xMask && y >= yMask && a.cells[x][y][z] != 0) + for (int geneIndex = 0; geneIndex < Cells.GENE_NUMBERS; geneIndex++) { + if ((a.cells[x][y][z] & (1 << geneIndex)) != 0 && Env.display_gene[geneIndex]) { + setPicColor(ColorUtils.colorByCode(geneIndex)); //开始画出对应的细胞基因参数,用不同颜色直径圆表示 + drawPoint(x + 0.5f, y + 0.5f, z + 0.5f, geneIndex == 0 ? 0.8f : 0.5f - geneIndex * 0.05f); + } + } + } + } + } + + setPicColor(Color.BLACK); + //BrainShapeJudge.show(this);//这行显示目标形状这个模子 + + drawCuboid(brain);// 把脑的框架画出来 + + setPicColor(BLACK); //把x,y,z坐标画出来 + drawText(Env.BRAIN_CUBE_SIZE, 0, 0, "x", 2); + drawText(0, Env.BRAIN_CUBE_SIZE, 0, "y", 2); + drawText(0, 0, Env.BRAIN_CUBE_SIZE, "z", 2); + setPicColor(RED); + drawLine(0, 0, 0, Env.BRAIN_CUBE_SIZE, 0, 0); + drawLine(0, 0, 0, 0, Env.BRAIN_CUBE_SIZE, 0); + drawLine(0, 0, 0, 0, 0, Env.BRAIN_CUBE_SIZE); + + g.setColor(Color.black); + if (note != null) // 全局注释 + g.drawString(note, 30, 55); + 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/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/brain/Cells.java b/history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/brain/Cells.java new file mode 100644 index 0000000..68e238f --- /dev/null +++ b/history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/brain/Cells.java @@ -0,0 +1,23 @@ +/* + * 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; + +/** + * Cells代表不同的脑细胞参数,对应每个参数,细胞有不同的行为, + * + * @author Yong Zhu + * @since 10.0 + */ +@SuppressWarnings("all") +public class Cells { + public static int GENE_NUMBERS = 5; //目前有多少条基因,每个脑细胞用是一个long来存储,所以最多允许64条基因,每个基因控制一个细胞的参数 + +} diff --git a/history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/brain/Cuboid.java b/history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/brain/Cuboid.java new file mode 100644 index 0000000..1e15c42 --- /dev/null +++ b/history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/brain/Cuboid.java @@ -0,0 +1,55 @@ +/* + * 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; + +/** + * Cuboid represents a rectangular prism 3d zone in brain + * + * Cuboid是一个长方体,通常用来表示脑内器官的形状 + * + * @author Yong Zhu + * @since 2.0.2 + */ +@SuppressWarnings("all") +public class Cuboid { + private static final long serialVersionUID = 1L; + + public float x;// x,y,z是长方体的左下角坐标 + public float y; + public float z; + public float xe;// xe,ye,ze分别是长方体三边长 + public float ye; + public float ze; + + public Cuboid() { + // 空构造器不能省 + } + + public Cuboid(float x, float y, float z, float xe, float ye, float ze) {// 用x,y,z,r来构造 + this.x = x; + this.y = y; + this.z = z; + this.xe = xe; + this.ye = ye; + this.ze = ze; + } + + public Cuboid(Cuboid c) {// 用另一个Cuboid来构造 + this.x = c.x; + this.y = c.y; + this.z = c.z; + this.xe = c.xe; + this.ye = c.ye; + this.ze = c.ze; + } + + +} diff --git a/history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/brain/MouseAction.java b/history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/brain/MouseAction.java new file mode 100644 index 0000000..337e5de --- /dev/null +++ b/history/009b_fish3d/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/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/egg/Egg.java b/history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/egg/Egg.java new file mode 100644 index 0000000..d2a7c1c --- /dev/null +++ b/history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/egg/Egg.java @@ -0,0 +1,75 @@ +/* + * 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.brain.Cells; +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 is a language similar like BASIC created by random + // 基因是随机生成的一种类似Basic语言的字符串符列,保存在蛋中,和实际生物每个细胞都要保存一份基因不同,程序中每个细胞仅保存着基因的指针和当前细胞位于基因链中的行号,并不需要保存基因的副本,这样可以极大地减少内存占用 + public ArrayList> genes = new ArrayList<>(); + + public Egg() {// 无中生有,创建一个蛋,先有蛋,后有蛙d + x = RandomUtils.nextInt(Env.ENV_WIDTH); + y = RandomUtils.nextInt(Env.ENV_HEIGHT); + } + + /** Create egg from animal */ + public Egg(Animal a) { // 下蛋,每个器官会创建自已的副本或变异,可以是0或多个 + x = a.x; + y = a.y; + for (ArrayList gene : a.genes) {//下蛋就是把动物的基因拷贝到新蛋里,并有可能变异 + ArrayList g = new ArrayList<>(); + g.addAll(gene); + genes.add(g); + } + } + + /** + * 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); + if (!genes.isEmpty() && b != null) + for (int i = 0; i < Cells.GENE_NUMBERS; i++) { + if (RandomUtils.percent(10)) { + ArrayList agene = a.genes.get(i); + ArrayList bgene = b.genes.get(i); + if (!bgene.isEmpty()) + agene.add(bgene.get(RandomUtils.nextInt(bgene.size()))); + } + } + } + +} diff --git a/history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/egg/FrogEggTool.java b/history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/egg/FrogEggTool.java new file mode 100644 index 0000000..6f849da --- /dev/null +++ b/history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/egg/FrogEggTool.java @@ -0,0 +1,117 @@ +/* + * 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.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.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 energy lay eggs + * + * 利用Java串行机制存盘。 能量多(也就是吃的更多)的Frog下蛋并存盘, 以进行下一轮测试,能量少的Frog被淘汰,没有下蛋的资格。 + * 用能量的多少来简化生存竟争模拟,每次下蛋数量固定为EGG_QTY个 + */ + public static void layEggs() { + for (Frog frog : Env.frogs) { + for (ArrayList gene : frog.genes) {//基因多的青蛙也要适当扣点分,防止蛋文件太大 + frog.energy-=gene.size(); + } + } + sortFrogsOrderByEnergyDesc(); + 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++) + Env.frog_eggs.add(new Egg(Env.frogs.get(i))); + Logger.info("Fist frog energy={}, Last frog energy={}", first.energy, last.energy); + 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 void sortFrogsOrderByEnergyDesc() {// 按能量多少给青蛙排序 + Collections.sort(Env.frogs, new Comparator() { + public int compare(Animal a, Animal b) { + if (a.energy > b.energy) + return -1; + else if (a.energy == b.energy) + 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 = (List) eggsInputStream.readObject(); + Logger.info("Loaded " + Env.frog_eggs.size() + " eggs from file '" + Application.CLASSPATH + + "frog_eggs.ser" + "'.\n"); + 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/core/src/main/java/com/gitee/drinkjava2/frog/judge/BrainRainbowColorJudge.java b/history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/judge/BrainRainbowColorJudge.java similarity index 100% rename from core/src/main/java/com/gitee/drinkjava2/frog/judge/BrainRainbowColorJudge.java rename to history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/judge/BrainRainbowColorJudge.java diff --git a/core/src/main/java/com/gitee/drinkjava2/frog/judge/BrainShapeJudge.java b/history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/judge/BrainShapeJudge.java similarity index 100% rename from core/src/main/java/com/gitee/drinkjava2/frog/judge/BrainShapeJudge.java rename to history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/judge/BrainShapeJudge.java diff --git a/history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/objects/EnvObject.java b/history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/objects/EnvObject.java new file mode 100644 index 0000000..9f9229d --- /dev/null +++ b/history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/objects/EnvObject.java @@ -0,0 +1,26 @@ +/* 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; + +/** + * EnvObject means some virtual object in Env + * + * @author Yong Zhu + * @since 1.0 + */ +public interface EnvObject { + + public void build(); // 在Env中创建本身物体,指改变Env.bricks数组元素为本身物体的组成材料。只在每屏测试前调用一次 + + public void destory();// 从Env中清除本身物体,只在每屏测试完成后调用一次 + + public void active(); // 每个步长都会调用一次这个方法 +} diff --git a/history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/objects/Food.java b/history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/objects/Food.java new file mode 100644 index 0000000..be12a6a --- /dev/null +++ b/history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/objects/Food.java @@ -0,0 +1,47 @@ +/* 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 com.gitee.drinkjava2.frog.Env; +import com.gitee.drinkjava2.frog.util.RandomUtils; + +/** + * Food randomly scatter on Env + * 生成食物(静态食物或苍蝇,苍蝇如果Env中FOOD_CAN_MOVE=true,会向四个方向移动) + * + * @author Yong Zhu + * @since 1.0 + */ +public class Food implements EnvObject { + + @Override + public void build() { + for (int i = 0; i < FOOD_QTY; i++) // 生成食物 + Env.setMaterial(RandomUtils.nextInt(ENV_WIDTH), RandomUtils.nextInt(ENV_HEIGHT), Material.FOOD); + } + + @Override + public void destory() { + for (int i = 0; i < ENV_WIDTH; i++) {// 清除食物 + for (int j = 0; j < ENV_HEIGHT; j++) + Env.clearMaterial(i, j, Material.FOOD); + } + } + + @Override + public void active() { + } + +} diff --git a/history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/objects/Material.java b/history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/objects/Material.java new file mode 100644 index 0000000..2e1931d --- /dev/null +++ b/history/009b_fish3d/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/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/util/ColorUtils.java b/history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/util/ColorUtils.java new file mode 100644 index 0000000..d9776e7 --- /dev/null +++ b/history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/util/ColorUtils.java @@ -0,0 +1,62 @@ +/* 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 { + + private static final Color[] rainbow = new Color[]{Color.GRAY, Color.GREEN, Color.RED, Color.BLUE, Color.YELLOW, Color.ORANGE, Color.MAGENTA, Color.CYAN}; + + 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/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/util/Cube.java b/history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/util/Cube.java new file mode 100644 index 0000000..eeaa522 --- /dev/null +++ b/history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/util/Cube.java @@ -0,0 +1,32 @@ +/* + * 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; + +/** + * Cube has x,y,z value + * + * @author Yong Zhu + * @since 2021-11-07 + */ +public class Cube { + public int x;//x,y,z是cube的左下前点坐标 + public int y; + public int z; + public int size;//size是cube的边长 + + public Cube(int x, int y, int z, int size) { + this.x = x; + this.y = y; + this.z = z; + this.size = size; + } + +} diff --git a/history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/util/LocalFileUtils.java b/history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/util/LocalFileUtils.java new file mode 100644 index 0000000..7422435 --- /dev/null +++ b/history/009b_fish3d/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/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/util/Logger.java b/history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/util/Logger.java new file mode 100644 index 0000000..8c72e24 --- /dev/null +++ b/history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/util/Logger.java @@ -0,0 +1,193 @@ +/* + * 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 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", 2); + LEVEL_MAP.put("ERROR", 2); + 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) { + try { + if (levelInt >= LEVEL_INT) { + LOG_LIST.put(generateMsg(getLevelStr(levelInt), msg, params)); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + private static String generateMsg(String levelStr, String msg, Object... 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/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/util/RandomUtils.java b/history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/util/RandomUtils.java new file mode 100644 index 0000000..541f4dd --- /dev/null +++ b/history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/util/RandomUtils.java @@ -0,0 +1,94 @@ +/* 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) { + if(i==0) + return 0; + return rand.nextInt(i); + } + + public static float 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(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 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/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/util/StringPixelUtils.java b/history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/util/StringPixelUtils.java new file mode 100644 index 0000000..db8d1ea --- /dev/null +++ b/history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/util/StringPixelUtils.java @@ -0,0 +1,105 @@ +/* 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; + } + + byte[][] b = new byte[strWidth][yend-ystart+1]; + for (int y = ystart; y <= yend; y++) + for (int x = 0; x < strWidth; x++) + if (bi.getRGB(x, y ) == -1) + b[x][yend-y] = 1; + else + b[x][yend-y] = 0; + lettersMap.put(key, b); + return b; + } + + /*- 这个是测试输出,平时不需要用 + public static void main(String[] args) { + System.out.println("==============="); + byte[][] c = getStringPixels(Font.SANS_SERIF, Font.PLAIN, 12, "FROG"); + int w = c.length; + int h = c[0].length; + + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + if (c[x][h - y - 1] > 0) + System.out.print("*"); + else + System.out.print(" "); + } + System.out.println(); + } + System.out.println("==============="); + } + */ +} diff --git a/history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/util/Tree8Util.java b/history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/util/Tree8Util.java new file mode 100644 index 0000000..ddcb228 --- /dev/null +++ b/history/009b_fish3d/src/main/java/com/gitee/drinkjava2/frog/util/Tree8Util.java @@ -0,0 +1,85 @@ +/* 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_CUBE_SIZE); + + public static int[][] TREE8 = new int[NODE_QTY][4]; //八叉数用数组表示,第一维是深度树的行号,第二维是一个整数数组,内容是深度树表示的八叉树细胞的size, x, y, z值 + + public static boolean[] ENABLE = new boolean[NODE_QTY]; //这里记录树的敲除记录,被敲除的节点用false表示 + + public static int ENABLE_NODE_QTY = NODE_QTY; //这里记录未被敲除的总节点数,好用来下次继续敲除 + + private static int index = 0; + static { + tree8Split(0, 0, 0, Env.BRAIN_CUBE_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叉树节点作个标记0 + for (int i = 0; i < Tree8Util.NODE_QTY; i++) + ENABLE[i] = true; + ENABLE_NODE_QTY = NODE_QTY; + for (int g : gene) {//g是要敲除的节点的行号 + if (Tree8Util.ENABLE[g]) { + int gSize = Tree8Util.TREE8[g][0]; //gSize是节点对应的立方体边长 + for (int i = g; i < Tree8Util.NODE_QTY; i++) {//从这个g节点开始,往下找节点 + int iSize = Tree8Util.TREE8[i][0]; + if (i > g && iSize >= gSize) //如果除了第一个节点外,边长与g相同或大于g的边长,说明节点不是g的子节点,退出 + break; + else {//否则就是g的子节点,需要敲除 + if (Tree8Util.ENABLE[i]) { //如是还没敲除 + ENABLE_NODE_QTY--; //有效节点数减1,这个 + Tree8Util.ENABLE[i] = false; //作敲除标记 + } + } + } + } + } + } + +} diff --git a/history/009c_yinyang/LICENSE b/history/009c_yinyang/LICENSE new file mode 100644 index 0000000..8dada3e --- /dev/null +++ b/history/009c_yinyang/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/009c_yinyang/README.md b/history/009c_yinyang/README.md new file mode 100644 index 0000000..f4120ac --- /dev/null +++ b/history/009c_yinyang/README.md @@ -0,0 +1,3 @@ +## history\009c分支简介 +本分支是在009b基础上的继续改进,采用了阴阳(黑白)节点算法,阴节点基因会删除节点下所有节点,是自顶向下的减材加工,阳节点基因会保留节点下所有节点,是自底向上的增材加工。利用遗传算法的大样本筛选,把自顶向下和自底向上两个进化方向结合起来,这样基本能解决误删分支后缺色不能补回这个问题。而且对于奖罚不再象以前一样要设成差距很大的值,animal.java中awardAAAA()原来要设成8000, 现在设成20也不会产生缺色现象。这个版本是研究技术细节问题,看不懂的同学们可以跳过。 +考虑到这个算法的特点,我给它起名“阴阳无极八卦阵算法“,阴阳是指它有阴阳两种节点,无极是指它的分裂阶数没有限制,八卦阵是指它采用了多个8叉树结构,每一维细胞参数都对应一个8叉树。 diff --git a/history/009c_yinyang/maven_clean.bat b/history/009c_yinyang/maven_clean.bat new file mode 100644 index 0000000..2f3d3e5 --- /dev/null +++ b/history/009c_yinyang/maven_clean.bat @@ -0,0 +1 @@ +mvn clean \ No newline at end of file diff --git a/history/009c_yinyang/maven_eclipse_clean.bat b/history/009c_yinyang/maven_eclipse_clean.bat new file mode 100644 index 0000000..a427bd7 --- /dev/null +++ b/history/009c_yinyang/maven_eclipse_clean.bat @@ -0,0 +1 @@ +mvn eclipse:clean \ No newline at end of file diff --git a/history/009c_yinyang/maven_eclipse_eclipse.bat b/history/009c_yinyang/maven_eclipse_eclipse.bat new file mode 100644 index 0000000..99fa0b2 --- /dev/null +++ b/history/009c_yinyang/maven_eclipse_eclipse.bat @@ -0,0 +1,2 @@ +call mvn eclipse:eclipse +call pause \ No newline at end of file diff --git a/history/009c_yinyang/pom.xml b/history/009c_yinyang/pom.xml new file mode 100644 index 0000000..a359a2c --- /dev/null +++ b/history/009c_yinyang/pom.xml @@ -0,0 +1,102 @@ + + 4.0.0 + com.gitee.drinkjava2 + frog009c + jar + 9.0 + + frog + 当前目标是大方向是由遗传算法来自动排列脑细胞和触突参数,以实现模式识别功能,并与上下左右运动细胞、进食奖罚感觉细胞结合起来,实现吃掉无毒蘑菇,避开有毒蘑菇这个任务。当前小目标是要引入黑白节点算法 + https://gitee.com/drinkjava2/jsqlbox/frog + + + gitee Issue + https://gitee.com/drinkjava2/jsqlbox/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/009c_yinyang/run.bat b/history/009c_yinyang/run.bat new file mode 100644 index 0000000..0156beb --- /dev/null +++ b/history/009c_yinyang/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/009c_yinyang/run.sh b/history/009c_yinyang/run.sh new file mode 100644 index 0000000..836c941 --- /dev/null +++ b/history/009c_yinyang/run.sh @@ -0,0 +1,2 @@ +mvn clean package +java -jar target/frog-*.jar diff --git a/history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/Animal.java b/history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/Animal.java new file mode 100644 index 0000000..a8c68e9 --- /dev/null +++ b/history/009c_yinyang/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.Cells.GENE_NUMBERS; + +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.egg.Egg; +import com.gitee.drinkjava2.frog.judge.RainBowFishJudge; +import com.gitee.drinkjava2.frog.objects.Material; +import com.gitee.drinkjava2.frog.util.RandomUtils; +import com.gitee.drinkjava2.frog.util.Tree8Util; + +/** + * 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; + public static BufferedImage snakeImage; + + public ArrayList> genes = new ArrayList<>(); // 基因是多个数列,有点象多条染色体 + + static { + try { + FROG_IMAGE = ImageIO.read(new FileInputStream(Application.CLASSPATH + "frog.png")); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** brain cells */ + public long[][][] cells = new long[Env.BRAIN_CUBE_SIZE][Env.BRAIN_CUBE_SIZE][Env.BRAIN_CUBE_SIZE]; + public float[][][] energys = new float[Env.BRAIN_CUBE_SIZE][Env.BRAIN_CUBE_SIZE][Env.BRAIN_CUBE_SIZE]; + + public int x; // animal在Env中的x坐标 + public int y; // animal在Env中的y坐标 + public long energy = 1000000000; // 青蛙的能量为0则死掉 + public boolean alive = true; // 设为false表示青蛙死掉了,将不参与计算和显示,以节省时间 + public int ateFood = 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 是虑拟环境的坐标 + 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); + i = 0; + if (Env.BORN_AT_RANDOM_PLACE) { //是否随机出生在地图上? + x = RandomUtils.nextInt(Env.ENV_WIDTH); + y = RandomUtils.nextInt(Env.ENV_HEIGHT); + } else {//否则出生成指定区域 + this.x = egg.x + RandomUtils.nextInt(80) - 40; + this.y = egg.y + RandomUtils.nextInt(80) - 40; + if (this.x < 0) + this.x = 0; + if (this.y < 0) + this.y = 0; + if (this.x >= (Env.ENV_WIDTH - 1)) + this.x = Env.ENV_WIDTH - 1; + if (this.y >= (Env.ENV_HEIGHT - 1)) + this.y = Env.ENV_HEIGHT - 1; + } + } + + public void initAnimal() { // 初始化animal,生成脑细胞是在这一步,这个方法是在当前屏animal生成之后调用,比方说有一千个青蛙分为500屏测试,每屏只生成2个青蛙的脑细胞,可以节约内存 + geneMutation(); //有小概率基因突变 + for (ArrayList gene : genes) //基因多也要适当小扣点分,防止基因无限增长 + energy -= gene.size(); + createCellsFromGene(); //运行基因语言,生成脑细胞 + RainBowFishJudge.judge(this); //外界对是否长得象彩虹鱼打分 + // FlowerJudge.judge(this);//外界对是否长得象小花儿打分 + } + + private static final int MIN_ENERGY_LIMIT = Integer.MIN_VALUE + 5000; + private static final int MAX_ENERGY_LIMIT = Integer.MAX_VALUE - 5000; + + //@formatter:off 下面几行是重要的奖罚方法,会经常调整或注释掉,集中放在一起,不要格式化为多行 + public void changeEnergy(int energy_) {//正数为奖励,负数为惩罚, energy大小是环境对animal唯一的奖罚,也是animal唯一的下蛋竞争标准 + energy += energy_; + if (energy > MAX_ENERGY_LIMIT) + energy = MAX_ENERGY_LIMIT; + if (energy < MIN_ENERGY_LIMIT) + energy = MIN_ENERGY_LIMIT; + } + + //如果改奖罚值,就可能出现缺色,这个要在基因变异算法(从上到下,从下到上)和环境本身奖罚合理性上下功夫 + public void awardAAAA() { changeEnergy(20);} + public void awardAAA() { changeEnergy(10);} + public void awardAA() { changeEnergy(5);} + public void awardA() { changeEnergy(2);} + + public void penaltyAAAA() { changeEnergy(-20);} + public void penaltyAAA() { changeEnergy(-10);} + public void penaltyAA() { changeEnergy(-5);} + public void penaltyA() { changeEnergy(-2);} + public void kill() { this.alive = false; changeEnergy(-5); Env.clearMaterial(x, y, animalMaterial); } //kill是最大的惩罚 + //@formatter:on + + public boolean active() {// 这个active方法在每一步循环都会被调用,是脑思考的最小帧 + // 如果能量小于0、出界、与非食物的点重合则判死 + if (!alive) { + energy = MIN_ENERGY_LIMIT; // 死掉的青蛙确保淘汰出局 + return false; + } + if (energy <= 0 || Env.outsideEnv(x, y) || Env.bricks[x][y] >= Material.KILL_ANIMAL) { + kill(); + return false; + } + //energy -= 20; + // 依次调用每个cell的active方法 + //for (Cell cell : cells) + // cell.organ.active(this, cell); + return alive; + } + + public void show(Graphics g) {// 显示当前动物 + if (!alive) + return; + // g.drawImage(animalImage, x - 8, y - 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_XSIZE || y < 0 || y >= Env.BRAIN_YSIZE || z < 0 || z >= Env.BRAIN_ZSIZE; + } + + public void geneMutation() { //基因变异,注意这一个算法同时变异所有条基因,目前最多允许64条基因 + for (int g = 0; g < GENE_NUMBERS; g++) {//随机新增阴节点基因 + if (RandomUtils.percent(10)) { + ArrayList gene = genes.get(g); + Tree8Util.knockNodesByGene(gene);//根据基因,把要敲除的8叉树节点作个标记,下面的算法保证阴节点基因只添加阳节点上 + int randomIndex = RandomUtils.nextInt(Tree8Util.keepNodeQTY); + int count = -1; + for (int i = 0; i < Tree8Util.NODE_QTY; i++) { + if (Tree8Util.keep[i] >= 0) { + count++; + if (count >= randomIndex && !gene.contains(-i)) { + gene.add(-i); + break; + } + } + } + } + } + + for (int g = 0; g < GENE_NUMBERS; g++) {//随机新增阳节点基因 + if (RandomUtils.percent(5)) { + ArrayList gene = genes.get(g); + Tree8Util.knockNodesByGene(gene);//根据基因,把要敲除的8叉树节点作个标记,下面的算法保证阳节点基因只添加在阴节点上 + int yinNodeQTY = Tree8Util.NODE_QTY - Tree8Util.keepNodeQTY; //阴节点总数 + int randomIndex = RandomUtils.nextInt(yinNodeQTY); + int count = -1; + for (int i = 0; i < yinNodeQTY; i++) { + if (Tree8Util.keep[i] < 0) { + count++; + if (count >= randomIndex && !gene.contains(i)) { + gene.add(i); + break; + } + } + } + } + } + +// for (int g = 0; g < GENE_NUMBERS; g++) {//随机变异将阳节点向上提升一级,相当于单个细胞的自底向上扩散式生长 +// if (RandomUtils.percent(3)) { +// ArrayList gene = genes.get(g); +// int randomIndex = RandomUtils.nextInt(gene.size()); +// if (randomIndex > 0 && gene.get(randomIndex) > 0) {//如基因是阳基因,且节点不是顶节点 +// int size = Tree8Util.TREE8[randomIndex][0]; +// gene.remove(randomIndex); //先删除底层这个阳基因 +// for (int i = randomIndex - 1; i > 0; i--) { +// if (Tree8Util.TREE8[i][0] > size) { //深度树只要大于size就是它的父节点 +// if (!gene.contains(i)) +// gene.add(i); +// int x = gene.indexOf(-i);//如果有阴节点也删除 +// if (x > 0) +// gene.remove(x); +// break; +// } +// } +// } +// } +// } + + for (int g = 0; g < GENE_NUMBERS; g++) {//随机变异删除一个基因,这样可以去除无用的拉圾基因,防止基因无限增大 + if (RandomUtils.percent(10)) { + ArrayList gene = genes.get(g); + if (!gene.isEmpty()) + gene.remove(RandomUtils.nextInt(gene.size())); + } + } + } + + private void createCellsFromGene() {//根据基因生成细胞参数 + long geneMask = 1; + for (int g = 0; g < GENE_NUMBERS; g++) {//动物有多条基因,一条基因控制一维细胞参数,最多有64维,也就是最多有64条基因 + ArrayList gene = genes.get(g); + 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 + cells[node[1]][node[2]][node[3]] = cells[node[1]][node[2]][node[3]] | geneMask; //在相应的细胞处把细胞参数位置1 + } + } + } + geneMask <<= 1; + } + } + +} diff --git a/history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/Application.java b/history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/Application.java new file mode 100644 index 0000000..31f13a8 --- /dev/null +++ b/history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/Application.java @@ -0,0 +1,169 @@ +package com.gitee.drinkjava2.frog; + +import java.awt.Component; +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.Cells; + +/** + * Application's main method start the program + * + * @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_XSIZE, 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 < Cells.GENE_NUMBERS; i++) { + JRadioButton geneRadio=new JRadioButton(); + geneRadio.setBounds(buttonXpos+300+i*20, Env.ENV_HEIGHT + 8, 20, 22); + geneRadio.setSelected(true); + geneRadio.setName(""+i); + ActionListener geneRadioAction = new ActionListener() { + public void actionPerformed(ActionEvent e) { + int i= Integer.parseInt(geneRadio.getName()); + if (geneRadio.isSelected()) + Env.display_gene[i]=true; + else + Env.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.setVisible(true); + env.run(); + } + +} diff --git a/history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/Env.java b/history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/Env.java new file mode 100644 index 0000000..32d937f --- /dev/null +++ b/history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/Env.java @@ -0,0 +1,325 @@ +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 java.util.List; + +import javax.swing.JPanel; + +import com.gitee.drinkjava2.frog.brain.Cells; +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.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 = 100; // 每轮下n个青蛙蛋,可调,只有最优秀的前n个青蛙们才允许下蛋 + + public static final int FROG_PER_EGG = 4; // 每个青蛙蛋可以孵出几个青蛙 + + public static final int SCREEN = 1; // 分几屏测完 + + /** 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中用一个List来存贮表示的同时,也用一个Cell3D动态数组来表示 + + public static final int BRAIN_CUBE_SIZE = 16; //脑立方边长大小,必须是2的幂数如4,8,16... + public static final int BRAIN_XSIZE = BRAIN_CUBE_SIZE; // 脑在X方向长度,取值最大为1000 + public static final int BRAIN_YSIZE = BRAIN_CUBE_SIZE; // 脑在Y方向长度,取值最大为1000 + public static final int BRAIN_ZSIZE = BRAIN_CUBE_SIZE; // 脑在Z方向长度,取值最大为1000 + + public static final int CELLS_MAX_QTY = 4000; //脑细胞总数不能超过这个值 + + /** 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 = 2000;// 每轮测试步数,可调 + public static int step;// 当前测试步数 + + public static final int FOOD_QTY = 1500; // 食物数量, 可调 + + // 以下是程序内部变量,不要手工修改它们 + 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 int food_ated = 0; // 用来统计总共多少个食物被青蛙吃掉 + + public static int frog_ated = 0; // 用来统计总共多少个青蛙被蛇吃掉 + + public static boolean pause = false; // 暂停按钮按下将暂停测试 + + public static int[][] bricks = new int[ENV_WIDTH][ENV_HEIGHT];// 组成环境的材料,见Material.java + + public static List frogs = new ArrayList<>(); // 这里存放所有待测的青蛙,可能分几次测完,由FROG_PER_SCREEN大小来决定 + + public static List frog_eggs = new ArrayList<>(); // 这里存放新建或从磁盘载入上轮下的蛋,每个蛋可能生成几个青蛙, + + public static EnvObject[] things = new EnvObject[]{ };// 所有外界物体,如食物、字母测试工具都放在这个things里面 + + public static boolean show_split_detail=false; //是否显示脑分裂的细节过程,即从一个细胞开始分裂分裂,而不是只显示分裂的最终结果 + + public static boolean[] display_gene=new boolean[Cells.GENE_NUMBERS]; //脑最多有64个基因,这里用来控制哪些基因需要显示在脑图上 + + static { + Logger.info("唵缚悉波罗摩尼莎诃!"); // 杀生前先打印往生咒,见码云issue#IW4H8 + Logger.info("脑图快捷键: T:顶视 F:前视 L:左视 R:右视 X:斜视 方向键:剖视 空格:暂停 鼠标:缩放旋转平移"); + if (DELETE_FROG_EGGS) + FrogEggTool.deleteEggs(); + for (int i = 0; i < display_gene.length; i++) + display_gene[i]=true; + } + + public Env() { + super(); + this.setLayout(null);// 空布局 + this.setBounds(1, 1, ENV_WIDTH, ENV_HEIGHT); + } + + 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.x < 20 || a.y < 20 || a.x > (Env.ENV_WIDTH - 20) || a.y > (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 foundAndAteFood(int x, int y) {// 如果x,y有食物,将其清0,返回true + if (insideEnv(x, y) && (Env.bricks[x][y] & Material.FOOD) > 0) { + Env.food_ated++; + clearMaterial(x, y, Material.FOOD);// 清空任意食物 + return true; + } + return false; + } + + 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(Env.food_ated * 1.00 / FOOD_QTY)).append(", 平均: ").append(Env.food_ated * 1.0f / FROG_PER_SCREEN).append(",最多:").append(maxFound) + .toString(); + } + + private String frogAtedCount() {// 统计食蛙总数 + return new StringBuilder("吃蛙率:").append(format100.format(Env.frog_ated * 1.00 / TOTAL_FROG_QTY)).toString(); + } + + public static void checkIfPause() { + if (pause) + do { + Application.brainPic.drawBrainPicture(); + 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 void run() { + FrogEggTool.loadFrogEggs(); // 从磁盘加载蛙egg,或新建一批egg + Image buffImg = createImage(this.getWidth(), this.getHeight()); + Graphics g = buffImg.getGraphics(); + long time0;// 计时用 + int round = 1; + do { + rebuildFrogs(); // 根据蛙蛋重新孵化出蛙,注意基因变异有可能在孵化过程中发生 + for (current_screen = 0; current_screen < SCREEN; current_screen++) {// 分屏测试,每屏FROG_PER_SCREEN个蛙 + Env.food_ated = 0; // 先清0吃食物数 + Env.frog_ated = 0;// 先清0吃蛙数 + time0 = System.currentTimeMillis(); + for (EnvObject thing : things) // 创建食物、陷阱等物体 + thing.build(); + 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(); + if (allDead) + break; // 青蛙全死光了就直接跳到下一轮,以节省时间 + allDead = true; + for (int j = 0; j < FROG_PER_SCREEN; j++) { + Frog f = frogs.get(current_screen * FROG_PER_SCREEN + j); + if (f.active())// 调用青蛙的Active方法,并返回是否还活着 + allDead = false; + } + + if (SHOW_SPEED > 0 && step % SHOW_SPEED != 0) // 用是否跳帧画图的方式来控制速度 + continue; + + if (SHOW_SPEED < 0) // 如果speed小于0,人为加入延迟 + sleep(-SHOW_SPEED); + + // 开始画虚拟环境和青蛙和蛇 + g.setColor(Color.white); + g.fillRect(0, 0, this.getWidth(), this.getHeight()); // 先清空虚拟环境 + g.setColor(Color.BLACK); + drawWorld(g);// 画整个虚拟环境 + for (int j = 0; j < FROG_PER_SCREEN; j++) { // 显示青蛙 + Frog f = frogs.get(current_screen * FROG_PER_SCREEN + j); + f.show(g); + } + + if (SHOW_FIRST_ANIMAL_BRAIN) {// 在showAnimal上画一个红圈 + Animal showAnimal = getShowAnimal(); + if (showAnimal != null) { + g.setColor(Color.red); + g.drawArc(showAnimal.x - 15, showAnimal.y - 15, 30, 30, 0, 360); + g.setColor(Color.BLACK); + } + } + if (DRAW_BRAIN_AFTER_STEPS > 0 && step % DRAW_BRAIN_AFTER_STEPS == 0) + Application.brainPic.drawBrainPicture(); + Graphics g2 = this.getGraphics(); + g2.drawImage(buffImg, 0, 0, this); + } + if (SHOW_FIRST_ANIMAL_BRAIN) + Application.brainPic.drawBrainPicture(); + checkIfPause(); + 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(foodAtedCount()); + + Application.mainFrame.setTitle(sb.toString()); + // for (EnvObject thing : things)// 去除食物、陷阱等物体 + // thing.destory(); + for (int i = 0; i < ENV_WIDTH; i++) {// 清除食物 + for (int j = 0; j < ENV_HEIGHT; j++) + bricks[i][j] = 0; + } + } + round++; + FrogEggTool.layEggs(); //能量高的青蛙才有权下蛋 + } while (true); + } + +} diff --git a/history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/Frog.java b/history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/Frog.java new file mode 100644 index 0000000..f2dbeb8 --- /dev/null +++ b/history/009c_yinyang/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/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/brain/BrainPicture.java b/history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/brain/BrainPicture.java new file mode 100644 index 0000000..a361e84 --- /dev/null +++ b/history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/brain/BrainPicture.java @@ -0,0 +1,450 @@ +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_YSIZE) + yMask = Env.BRAIN_YSIZE; + 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_XSIZE) + xMask = Env.BRAIN_XSIZE; + 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(Cuboid c) {// 在脑图上画一个长立方体框架,视角是TopView + drawCuboid(c.x, c.y, c.z, c.xe, c.ye, c.ze); + } + + 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); + } + + /*- + 画线,固定以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_XSIZE / 2; + double y1 = -py1 + Env.BRAIN_YSIZE / 2;// 屏幕的y坐标是反的,显示时要正过来 + double z1 = pz1 - Env.BRAIN_ZSIZE / 2; + double x2 = px2 - Env.BRAIN_XSIZE / 2; + double y2 = -py2 + Env.BRAIN_YSIZE / 2;// 屏幕的y坐标是反的,显示时要正过来 + double z2 = pz2 - Env.BRAIN_ZSIZE / 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); + } + + /** 画点,固定以top视角的角度,所以只需要在x1,y1位置画一个点 */ + public void drawPoint(float px1, float py1, float pz1, float r) { + double x1 = px1 - Env.BRAIN_XSIZE / 2; + double y1 = -py1 + Env.BRAIN_YSIZE / 2;// 屏幕的y坐标是反的,显示时要正过来 + double z1 = pz1 - Env.BRAIN_ZSIZE / 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_XSIZE / 2; + double y1 = -py1 + Env.BRAIN_YSIZE / 2;// 屏幕的y坐标是反的,显示时要正过来 + double z1 = pz1 - Env.BRAIN_ZSIZE / 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 drawText(float px1, float py1, float pz1, String text, float textSize) { + double x1 = px1 - Env.BRAIN_XSIZE / 2; + double y1 = -py1 + Env.BRAIN_YSIZE / 2;// 屏幕的y坐标是反的,显示时要正过来 + double z1 = pz1 - Env.BRAIN_ZSIZE / 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); + + } + + private static Cuboid brain = new Cuboid(0, 0, 0, Env.BRAIN_XSIZE, Env.BRAIN_YSIZE, Env.BRAIN_ZSIZE); + + + + public void drawBrainPicture() {// 在这个方法里进行动物的三维脑结构的绘制,蛇是青蛙的子类,所以也可以当参数传进来 + if (!Env.SHOW_FIRST_ANIMAL_BRAIN) + return; + if(Env.show_split_detail) + drawSplitDetail(); + else + drawBrainStructure(); + } + + public void drawSplitDetail() {// 在这个方法里绘制脑细胞分裂的显示步聚,即从一个细胞开始分裂成最终脑结构的每一步 + Animal a = Env.getShowAnimal(); // 第一个青蛙或蛇 + + for (int i = Env.BRAIN_CUBE_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 < Cells.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 && Env.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(brain);// 把脑的框架画出来 + this.getGraphics().drawImage(buffImg, 0, 0, this);// 利用缓存避免画面闪烁,这里输出缓存图片 + if (!Env.show_split_detail) + return; + try { + Thread.sleep(500); + } catch (InterruptedException e) { + } + } + } + + public void drawBrainStructure() {// 在这个方法里进行动物的三维脑结构的绘制,蛇是青蛙的子类,所以也可以当参数传进来 + 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_CUBE_SIZE; z++) { + for (int y = Env.BRAIN_CUBE_SIZE - 1; y >= 0; y--) { + for (int x = Env.BRAIN_CUBE_SIZE - 1; x >= 0; x--) { + if (x >= xMask && y >= yMask && a.cells[x][y][z] != 0) + for (int geneIndex = 0; geneIndex < Cells.GENE_NUMBERS; geneIndex++) { + if ((a.cells[x][y][z] & (1 << geneIndex)) != 0 && Env.display_gene[geneIndex]) { + setPicColor(ColorUtils.colorByCode(geneIndex)); //开始画出对应的细胞基因参数,用不同颜色直径圆表示 + drawPoint(x + 0.5f, y + 0.5f, z + 0.5f, geneIndex == 0 ? 0.8f : 0.5f - geneIndex * 0.05f); + } + } + } + } + } + + setPicColor(Color.BLACK); + //BrainShapeJudge.show(this);//这行显示目标形状这个模子 + + drawCuboid(brain);// 把脑的框架画出来 + + setPicColor(BLACK); //把x,y,z坐标画出来 + drawText(Env.BRAIN_CUBE_SIZE, 0, 0, "x", 2); + drawText(0, Env.BRAIN_CUBE_SIZE, 0, "y", 2); + drawText(0, 0, Env.BRAIN_CUBE_SIZE, "z", 2); + setPicColor(RED); + drawLine(0, 0, 0, Env.BRAIN_CUBE_SIZE, 0, 0); + drawLine(0, 0, 0, 0, Env.BRAIN_CUBE_SIZE, 0); + drawLine(0, 0, 0, 0, 0, Env.BRAIN_CUBE_SIZE); + + g.setColor(Color.black); + if (note != null) // 全局注释 + g.drawString(note, 30, 55); + 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/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/brain/Cells.java b/history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/brain/Cells.java new file mode 100644 index 0000000..b172d80 --- /dev/null +++ b/history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/brain/Cells.java @@ -0,0 +1,23 @@ +/* + * 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; + +/** + * Cells代表不同的脑细胞参数,对应每个参数,细胞有不同的行为, + * + * @author Yong Zhu + * @since 10.0 + */ +@SuppressWarnings("all") +public class Cells { + public static int GENE_NUMBERS = 4; //目前有多少条基因,每个脑细胞用是一个long来存储,所以最多允许64条基因,每个基因控制一个细胞的参数 + +} diff --git a/history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/brain/Cuboid.java b/history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/brain/Cuboid.java new file mode 100644 index 0000000..1e15c42 --- /dev/null +++ b/history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/brain/Cuboid.java @@ -0,0 +1,55 @@ +/* + * 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; + +/** + * Cuboid represents a rectangular prism 3d zone in brain + * + * Cuboid是一个长方体,通常用来表示脑内器官的形状 + * + * @author Yong Zhu + * @since 2.0.2 + */ +@SuppressWarnings("all") +public class Cuboid { + private static final long serialVersionUID = 1L; + + public float x;// x,y,z是长方体的左下角坐标 + public float y; + public float z; + public float xe;// xe,ye,ze分别是长方体三边长 + public float ye; + public float ze; + + public Cuboid() { + // 空构造器不能省 + } + + public Cuboid(float x, float y, float z, float xe, float ye, float ze) {// 用x,y,z,r来构造 + this.x = x; + this.y = y; + this.z = z; + this.xe = xe; + this.ye = ye; + this.ze = ze; + } + + public Cuboid(Cuboid c) {// 用另一个Cuboid来构造 + this.x = c.x; + this.y = c.y; + this.z = c.z; + this.xe = c.xe; + this.ye = c.ye; + this.ze = c.ze; + } + + +} diff --git a/history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/brain/MouseAction.java b/history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/brain/MouseAction.java new file mode 100644 index 0000000..337e5de --- /dev/null +++ b/history/009c_yinyang/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/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/egg/Egg.java b/history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/egg/Egg.java new file mode 100644 index 0000000..5592971 --- /dev/null +++ b/history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/egg/Egg.java @@ -0,0 +1,75 @@ +/* + * 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.brain.Cells; +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 is a language similar like BASIC created by random + // 基因是随机生成的一种类似Basic语言的字符串符列,保存在蛋中,和实际生物每个细胞都要保存一份基因不同,程序中每个细胞仅保存着基因的指针和当前细胞位于基因链中的行号,并不需要保存基因的副本,这样可以极大地减少内存占用 + public ArrayList> genes = new ArrayList<>(); + + public Egg() {// 无中生有,创建一个蛋,先有蛋,后有蛙d + x = RandomUtils.nextInt(Env.ENV_WIDTH); + y = RandomUtils.nextInt(Env.ENV_HEIGHT); + } + + /** Create egg from animal */ + public Egg(Animal a) { // 下蛋,每个器官会创建自已的副本或变异,可以是0或多个 + x = a.x; + y = a.y; + for (ArrayList gene : a.genes) {//下蛋就是把动物的基因拷贝到新蛋里,并有可能变异 + ArrayList g = new ArrayList<>(); + g.addAll(gene); + genes.add(g); + } + } + + /** + * 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); +// if (!genes.isEmpty() && b != null) +// for (int i = 0; i < Cells.GENE_NUMBERS; i++) { +// if (RandomUtils.percent(3)) { +// ArrayList agene = a.genes.get(i); +// ArrayList bgene = b.genes.get(i); +// if (!bgene.isEmpty()) +// agene.add(bgene.get(RandomUtils.nextInt(bgene.size()))); +// } +// } + } + +} diff --git a/history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/egg/FrogEggTool.java b/history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/egg/FrogEggTool.java new file mode 100644 index 0000000..0fa2c0e --- /dev/null +++ b/history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/egg/FrogEggTool.java @@ -0,0 +1,112 @@ +/* + * 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.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.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 energy lay eggs + * + * 利用Java串行机制存盘。 能量多(也就是吃的更多)的Frog下蛋并存盘, 以进行下一轮测试,能量少的Frog被淘汰,没有下蛋的资格。 + * 用能量的多少来简化生存竟争模拟,每次下蛋数量固定为EGG_QTY个 + */ + public static void layEggs() { + sortFrogsOrderByEnergyDesc(); + 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++) + Env.frog_eggs.add(new Egg(Env.frogs.get(i))); + Logger.info("Fist frog energy={}, Last frog energy={}", first.energy, last.energy); + 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 void sortFrogsOrderByEnergyDesc() {// 按能量多少给青蛙排序 + Collections.sort(Env.frogs, new Comparator() { + public int compare(Animal a, Animal b) { + if (a.energy > b.energy) + return -1; + else if (a.energy == b.energy) + 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 = (List) eggsInputStream.readObject(); + Logger.info("Loaded " + Env.frog_eggs.size() + " eggs from file '" + Application.CLASSPATH + + "frog_eggs.ser" + "'.\n"); + 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/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/judge/FlowerJudge.java b/history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/judge/FlowerJudge.java new file mode 100644 index 0000000..f272d74 --- /dev/null +++ b/history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/judge/FlowerJudge.java @@ -0,0 +1,85 @@ +package com.gitee.drinkjava2.frog.judge; + +import java.awt.Font; +import java.util.ArrayList; +import java.util.List; + +import com.gitee.drinkjava2.frog.Animal; +import com.gitee.drinkjava2.frog.Env; +import com.gitee.drinkjava2.frog.brain.BrainPicture; +import com.gitee.drinkjava2.frog.brain.Cells; +import com.gitee.drinkjava2.frog.util.StringPixelUtils; + +/** + * judge method be called in animal's initAnimal method + * + * 这个类的judge方法在动物的初始化后被调用,根据脑细胞群的三维结构和参数来对动物进行奖罚,即加减它的能量值,这是一个临时类,只是用来检验细胞三维成形功能,以后可能改名或删除 + * 这个类的show方法在绘脑图时调用,在脑图里显示脑细胞群的三维形状和参数,用不同颜色直径的空心圆来表示不同参数,judge方法就像是一个模子,细胞长在这个模子里的有奖,否则扣分 + */ +public class FlowerJudge extends RainBowFishJudge {//NOSONAR + private static int[] C = new int[]{0, 0, Env.BRAIN_CUBE_SIZE / 2}; //C是中心点 + private static boolean[][][] shape = new boolean[Env.BRAIN_XSIZE][Env.BRAIN_YSIZE][Env.BRAIN_ZSIZE]; + private static List pointList = new ArrayList<>(); //pointList存放上面shape的所有有效点,用来加快显示循环而不用遍历三维数组 + static { + putPixiel("🌸"); + } + + public static void putPixiel(String str) { + byte[][] c = StringPixelUtils.getStringPixels(Font.SANS_SERIF, Font.PLAIN, Env.BRAIN_CUBE_SIZE, str); //要把frog二维像素变成立体的三维点放到points里和pointsList里供使用 + int w = c.length; + int h = c[0].length; + for (int z = 0; z < 5; z++) { + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + if (c[x][y] > 0) { + int[] p = new int[]{C[0] + x, C[1] + y + 2, C[2] + z}; + if (!Animal.outBrainRange(p[0], p[1]-2, p[2])) {//减2是把花朵向下移两格 + shape[p[0]][p[1]-2][p[2]] = true; + pointList.add(p); + } + } + } + } + } + } + + private static void judgeShape(Animal animal) {//检查animal的脑细胞是否位于brainShape的范围内 + for (int x = 0; x < Env.BRAIN_CUBE_SIZE; x++) { + for (int y = 0; y < Env.BRAIN_CUBE_SIZE; y++) { + for (int z = 0; z < Env.BRAIN_CUBE_SIZE; z++) { + if ((animal.cells[x][y][z] & 1) != 0) + if (shape[x][y][z]) { + animal.awardAAAA(); + } else { + animal.penaltyAAA(); + } + } + } + } + } + + public static void judgeColor(Animal animal) {//检查颜色与小鱼图像重合,且呈斑马纹色彩分布,是就加分,否则扣分 + int colorWidth = Env.BRAIN_CUBE_SIZE / Cells.GENE_NUMBERS; //每条彩带宽度 + for (int x = 0; x < Env.BRAIN_CUBE_SIZE; x++) + for (int y = 0; y < Env.BRAIN_CUBE_SIZE; y++) + for (int z = 0; z < Env.BRAIN_CUBE_SIZE; z++) + for (int i = 1; i <= Cells.GENE_NUMBERS; i++) + if ((animal.cells[x][y][z] & (1 << i)) != 0) { //如果色彩存在 + if ((animal.cells[x][y][z] & 1) != 0 && y >= (i - 1) * colorWidth && y < (i * colorWidth)) + animal.awardAA(); //如果细胞存在且颜色分布符合要求奖励 + else + animal.penaltyA();//否则扣分 + } + } + + public static void judge(Animal animal) {//检查animal的脑细胞是否位于brainShape的范围内 + judgeShape(animal); + judgeColor(animal); + } + + public static void show(BrainPicture pic) {// 在脑图上显示当前形状 + for (int[] p : pointList) + pic.drawCircle(p[0] + 0.5f, p[1] + 0.5f, p[2] + 0.5f, 1); + } + +} diff --git a/history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/judge/RainBowFishJudge.java b/history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/judge/RainBowFishJudge.java new file mode 100644 index 0000000..434a9f3 --- /dev/null +++ b/history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/judge/RainBowFishJudge.java @@ -0,0 +1,84 @@ +package com.gitee.drinkjava2.frog.judge; + +import java.awt.Font; +import java.util.ArrayList; +import java.util.List; + +import com.gitee.drinkjava2.frog.Animal; +import com.gitee.drinkjava2.frog.Env; +import com.gitee.drinkjava2.frog.brain.BrainPicture; +import com.gitee.drinkjava2.frog.brain.Cells; +import com.gitee.drinkjava2.frog.util.StringPixelUtils; + +/** + * judge method be called in animal's initAnimal method + * + * 这个类的judge方法在动物的初始化后被调用,根据脑细胞群的三维结构和参数来对动物进行奖罚,即加减它的能量值,这是一个临时类,只是用来检验细胞三维成形功能,以后可能改名或删除 + * 这个类的show方法在绘脑图时调用,在脑图里显示脑细胞群的三维形状和参数,用不同颜色直径的空心圆来表示不同参数,judge方法就像是一个模子,细胞长在这个模子里的有奖,否则扣分 + */ +public class RainBowFishJudge {//NOSONAR + private static int[] C = new int[]{0, 0, Env.BRAIN_CUBE_SIZE / 2}; //C是中心点 + private static boolean[][][] shape = new boolean[Env.BRAIN_XSIZE][Env.BRAIN_YSIZE][Env.BRAIN_ZSIZE]; + private static List pointList = new ArrayList<>(); //pointList存放上面shape的所有有效点,用来加快显示循环而不用遍历三维数组 + static { + putPixiel("🐟"); + } + + public static void putPixiel(String str) { + byte[][] c = StringPixelUtils.getStringPixels(Font.SANS_SERIF, Font.PLAIN, Env.BRAIN_CUBE_SIZE, str); //要把frog二维像素变成立体的三维点放到points里和pointsList里供使用 + int w = c.length; + int h = c[0].length; + for (int z = 0; z < 5; z++) { + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + if (c[x][y] > 0) { + int[] p = new int[]{C[0] + x, C[1] + y + 2, C[2] + z}; + if (!Animal.outBrainRange(p[0], p[1], p[2])) { + shape[p[0]][p[1]][p[2]] = true; + pointList.add(p); + } + } + } + } + } + } + + private static void judgeShape(Animal animal) {//检查animal的脑细胞是否位于brainShape的范围内 + for (int x = 0; x < Env.BRAIN_CUBE_SIZE; x++) { + for (int y = 0; y < Env.BRAIN_CUBE_SIZE; y++) { + for (int z = 0; z < Env.BRAIN_CUBE_SIZE; z++) { + if ((animal.cells[x][y][z] & 1) != 0) + if (shape[x][y][z]) { + animal.awardAAAA(); + } else { + animal.penaltyAAA(); + } + } + } + } + } + + public static void judgeColor(Animal animal) {//检查颜色与小鱼图像重合,且呈斑马纹色彩分布,是就加分,否则扣分 + int colorWidth = Env.BRAIN_CUBE_SIZE / Cells.GENE_NUMBERS; //每条彩带宽度 + for (int x = 0; x < Env.BRAIN_CUBE_SIZE; x++) + for (int y = 0; y < Env.BRAIN_CUBE_SIZE; y++) + for (int z = 0; z < Env.BRAIN_CUBE_SIZE; z++) + for (int i = 1; i <= Cells.GENE_NUMBERS; i++) + if ((animal.cells[x][y][z] & (1 << i)) != 0) //如果色彩存 + if ((animal.cells[x][y][z] & 1) != 0 && x >= (i - 1) * colorWidth && x < (i * colorWidth)) + animal.awardAA(); + else + animal.penaltyA(); + } + + public static void judge(Animal animal) {//检查animal的脑细胞是否位于brainShape的范围内 + judgeShape(animal); + judgeColor(animal); + } + + public static void show(BrainPicture pic) {// 在脑图上显示当前形状 + for (int[] p : pointList) + pic.drawCircle(p[0] + 0.5f, p[1] + 0.5f, p[2] + 0.5f, 1); + } + +} diff --git a/history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/objects/EnvObject.java b/history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/objects/EnvObject.java new file mode 100644 index 0000000..9f9229d --- /dev/null +++ b/history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/objects/EnvObject.java @@ -0,0 +1,26 @@ +/* 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; + +/** + * EnvObject means some virtual object in Env + * + * @author Yong Zhu + * @since 1.0 + */ +public interface EnvObject { + + public void build(); // 在Env中创建本身物体,指改变Env.bricks数组元素为本身物体的组成材料。只在每屏测试前调用一次 + + public void destory();// 从Env中清除本身物体,只在每屏测试完成后调用一次 + + public void active(); // 每个步长都会调用一次这个方法 +} diff --git a/history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/objects/Food.java b/history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/objects/Food.java new file mode 100644 index 0000000..be12a6a --- /dev/null +++ b/history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/objects/Food.java @@ -0,0 +1,47 @@ +/* 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 com.gitee.drinkjava2.frog.Env; +import com.gitee.drinkjava2.frog.util.RandomUtils; + +/** + * Food randomly scatter on Env + * 生成食物(静态食物或苍蝇,苍蝇如果Env中FOOD_CAN_MOVE=true,会向四个方向移动) + * + * @author Yong Zhu + * @since 1.0 + */ +public class Food implements EnvObject { + + @Override + public void build() { + for (int i = 0; i < FOOD_QTY; i++) // 生成食物 + Env.setMaterial(RandomUtils.nextInt(ENV_WIDTH), RandomUtils.nextInt(ENV_HEIGHT), Material.FOOD); + } + + @Override + public void destory() { + for (int i = 0; i < ENV_WIDTH; i++) {// 清除食物 + for (int j = 0; j < ENV_HEIGHT; j++) + Env.clearMaterial(i, j, Material.FOOD); + } + } + + @Override + public void active() { + } + +} diff --git a/history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/objects/Material.java b/history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/objects/Material.java new file mode 100644 index 0000000..2e1931d --- /dev/null +++ b/history/009c_yinyang/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/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/util/ColorUtils.java b/history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/util/ColorUtils.java new file mode 100644 index 0000000..d9776e7 --- /dev/null +++ b/history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/util/ColorUtils.java @@ -0,0 +1,62 @@ +/* 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 { + + private static final Color[] rainbow = new Color[]{Color.GRAY, Color.GREEN, Color.RED, Color.BLUE, Color.YELLOW, Color.ORANGE, Color.MAGENTA, Color.CYAN}; + + 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/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/util/Cube.java b/history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/util/Cube.java new file mode 100644 index 0000000..eeaa522 --- /dev/null +++ b/history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/util/Cube.java @@ -0,0 +1,32 @@ +/* + * 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; + +/** + * Cube has x,y,z value + * + * @author Yong Zhu + * @since 2021-11-07 + */ +public class Cube { + public int x;//x,y,z是cube的左下前点坐标 + public int y; + public int z; + public int size;//size是cube的边长 + + public Cube(int x, int y, int z, int size) { + this.x = x; + this.y = y; + this.z = z; + this.size = size; + } + +} diff --git a/history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/util/LocalFileUtils.java b/history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/util/LocalFileUtils.java new file mode 100644 index 0000000..7422435 --- /dev/null +++ b/history/009c_yinyang/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/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/util/Logger.java b/history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/util/Logger.java new file mode 100644 index 0000000..8c72e24 --- /dev/null +++ b/history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/util/Logger.java @@ -0,0 +1,193 @@ +/* + * 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 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", 2); + LEVEL_MAP.put("ERROR", 2); + 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) { + try { + if (levelInt >= LEVEL_INT) { + LOG_LIST.put(generateMsg(getLevelStr(levelInt), msg, params)); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + private static String generateMsg(String levelStr, String msg, Object... 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/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/util/RandomUtils.java b/history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/util/RandomUtils.java new file mode 100644 index 0000000..dff7533 --- /dev/null +++ b/history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/util/RandomUtils.java @@ -0,0 +1,101 @@ +/* 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 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(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 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/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/util/StringPixelUtils.java b/history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/util/StringPixelUtils.java new file mode 100644 index 0000000..db8d1ea --- /dev/null +++ b/history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/util/StringPixelUtils.java @@ -0,0 +1,105 @@ +/* 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; + } + + byte[][] b = new byte[strWidth][yend-ystart+1]; + for (int y = ystart; y <= yend; y++) + for (int x = 0; x < strWidth; x++) + if (bi.getRGB(x, y ) == -1) + b[x][yend-y] = 1; + else + b[x][yend-y] = 0; + lettersMap.put(key, b); + return b; + } + + /*- 这个是测试输出,平时不需要用 + public static void main(String[] args) { + System.out.println("==============="); + byte[][] c = getStringPixels(Font.SANS_SERIF, Font.PLAIN, 12, "FROG"); + int w = c.length; + int h = c[0].length; + + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + if (c[x][h - y - 1] > 0) + System.out.print("*"); + else + System.out.print(" "); + } + System.out.println(); + } + System.out.println("==============="); + } + */ +} diff --git a/history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/util/Tree8Util.java b/history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/util/Tree8Util.java new file mode 100644 index 0000000..eb9ff4a --- /dev/null +++ b/history/009c_yinyang/src/main/java/com/gitee/drinkjava2/frog/util/Tree8Util.java @@ -0,0 +1,89 @@ +/* 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_CUBE_SIZE); + + public static int[][] TREE8 = new int[NODE_QTY][4]; //八叉数用数组表示,第一维是深度树的行号,第二维是一个整数数组,内容是深度树表示的八叉树细胞的size, x, y, z值 + + public static byte[] keep = new byte[NODE_QTY]; //这里临时记录树的敲除记录,大于等于0的值表示要keep, 负数节点表示要敲除 + + private static byte[] KEEP = new byte[NODE_QTY]; //这里保存初值为0的数组常量,可以用System.arraycopy(enable0, 0, enable, 0, NODE_QTY)快速清空enable数组 + + public static int keepNodeQTY = NODE_QTY; //这里临时记录需keep的节点总数,好用来继续敲除,初始值是全部节点 + + private static int index = 0; + static { + tree8Split(0, 0, 0, Env.BRAIN_CUBE_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(KEEP, 0, keep, 0, NODE_QTY);//清空keep数组 + keepNodeQTY = NODE_QTY; + for (int g : gene) {//g基因,用带符号的8叉数的行号表示,负数表示阴节点要敲除,0或正数表示是阳节点要保留 + 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是阴节点 + Tree8Util.keep[line]--; + if (Tree8Util.keep[line] == -1) //如果正好是-1,表示这个节点从保留状态转为删除状态 + keepNodeQTY--; + } else if (g > 0) { //g是阳节点 + Tree8Util.keep[line]++; + if (Tree8Util.keep[line] == 0) //如果正好是0,表示这个节点从删除状态又转回了保留状态 + keepNodeQTY++; + } + } + } + } + } + +} diff --git a/result16_cell_split.gif b/result16_cell_split.gif new file mode 100644 index 0000000000000000000000000000000000000000..e74d4aae1ea332cce8183e95175197493d68124d GIT binary patch literal 34045 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 UWE5_A2yHsard)X(j*$QWJ4+w96951J literal 0 HcmV?d00001