diff --git a/README.md b/README.md index 41bf68b..d83ad8d 100644 --- a/README.md +++ b/README.md @@ -177,6 +177,14 @@ Frog: 这是人工生命的主体,目前起名叫青蛙(Frog),其实叫什 位于history\009c目录下,采用了阴阳(黑白)节点算法,阴节点基因会删除节点下所有节点,是自顶向下的减材加工,阳节点基因会保留节点下所有节点,是自底向上的增材加工。利用遗传算法的大样本筛选,把自顶向下和自底向上两个进化方向结合起来,这样基本能解决误删分支后缺色不能补回这个问题。而且对于奖罚不再象以前一样要设成差距很大的值,animal.java中awardAAAA()原来要设成8000, 现在设成20也不会产生缺色现象。这个版本是研究技术细节问题,看不懂的同学们可以跳过。 考虑到这个算法的特点,我给它起名“阴阳无极八卦阵算法“,阴阳是指它有阴阳两种节点,无极是指它的分裂阶数没有限制,八卦阵是指它采用了多个8叉树结构,每一维细胞参数都对应一个8叉树。 +2022-07-22 树的生长演示 +位于history\010目录,演示一个树的生长。因为以前小鱼是手工指定一个三维像素点阵模板,有点作弊的嫌疑,所以这次演示是基于规则而不是模板来生成一棵树,这棵树的形状仅由以下规则决定: +1.树根位于底部中心 +2.树的每个细胞水平方向四周不能有其它细胞 +3.树的每个细胞正下方不能有细胞,但必须在斜下方至少有一个细胞 +基于这三条规则,利用阴阳无极八卦阵分裂算法,树能够自动进化出来,但要耐心等一会儿。这个演示的意义是表示形状可以由规则来决定,不同的规则能进化出不同的形状,生物会通过改变自己的形状来适应环境规则。 +这个项目下一个任务就要基于形状由规则决定这个原理,制定模式识别奖惩规则,然后利用分裂算法,让细胞自动在空间上排布出不同细胞参数,从而进化出具有初步模式识别功能的神经网络,比方说,识别出0,1,2 ,3数字。大自然生物的模式识别不是通过人为设计算法,而就是这样无脑随机分裂细胞进化出来的。 +![result17](result17_tree_grow.gif) ## 运行方式 | Run 运行core或history各个子目录下的run.bat批处理文件即可启动运行,history下有多个子目录,按版本号顺序排列,存放着这个项目演化过程中的主要历史版本供演示。 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 982d640..77446ff 100644 --- a/core/src/main/java/com/gitee/drinkjava2/frog/Animal.java +++ b/core/src/main/java/com/gitee/drinkjava2/frog/Animal.java @@ -10,7 +10,7 @@ */ package com.gitee.drinkjava2.frog; -import static com.gitee.drinkjava2.frog.brain.Cells.*; +import static com.gitee.drinkjava2.frog.brain.Cells.GENE_NUMBERS; import java.awt.Graphics; import java.awt.Image; @@ -22,7 +22,8 @@ import java.util.List; import javax.imageio.ImageIO; import com.gitee.drinkjava2.frog.egg.Egg; -import com.gitee.drinkjava2.frog.judge.RainBowFishJudge; +import com.gitee.drinkjava2.frog.judge.TreeShapeJudge; +import com.gitee.drinkjava2.frog.objects.Food; import com.gitee.drinkjava2.frog.objects.Material; import com.gitee.drinkjava2.frog.util.RandomUtils; import com.gitee.drinkjava2.frog.util.Tree8Util; @@ -55,9 +56,9 @@ public abstract class Animal {// 这个程序大量用到public变量而不是ge 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 List photons = new ArrayList<>(); //每个光子是由一个byte数组表示,依次是x,y,z坐标、dx,dy,dz坐标余数(即小数后256分之几),mz,my,mz运动方向矢量,能量值,速度 + public List photons = new ArrayList<>(); //每个光子是由一个float数组表示,依次是x,y,z坐标, mz,my,mz运动方向矢量,能量值,速度 - public List photons2 = new ArrayList<>();// photons2是个临时空间,用来中转存放一下每遍光子运算后的结果,用双鬼拍门来替代单个链表的增删,每个list只增不减以优化速度 + public List photons2 = new ArrayList<>();// photons2是个临时空间,用来中转存放一下每遍光子运算后的结果,用双鬼拍门来替代单个链表的增删,每个list只增不减以优化速度 public int x; // animal在Env中的x坐标 public int y; // animal在Env中的y坐标 @@ -99,7 +100,9 @@ public abstract class Animal {// 这个程序大量用到public变量而不是ge for (ArrayList gene : genes) //基因多也要适当小扣点分,防止基因无限增长 energy -= gene.size(); createCellsFromGene(); //根据基因分裂生成脑细胞 - RainBowFishJudge.judge(this); //外界对是否长得象彩虹鱼打分 + //RainBowFishJudge.judge(this); //外界对是否长得象彩虹鱼打分 + //MoveCellLocationJudge.judge(this); + TreeShapeJudge.judge(this); } private static final int MIN_ENERGY_LIMIT = Integer.MIN_VALUE + 5000; @@ -115,13 +118,13 @@ public abstract class Animal {// 这个程序大量用到public变量而不是ge } //如果改奖罚值,就可能出现缺色,这个要在基因变异算法(从上到下,从下到上)和环境本身奖罚合理性上下功夫 - public void awardAAAA() { changeEnergy(20);} - public void awardAAA() { changeEnergy(10);} + public void awardAAAA() { changeEnergy(2000);} + public void awardAAA() { changeEnergy(200);} public void awardAA() { changeEnergy(5);} public void awardA() { changeEnergy(2);} - public void penaltyAAAA() { changeEnergy(-20);} - public void penaltyAAA() { changeEnergy(-10);} + public void penaltyAAAA() { changeEnergy(-2000);} + public void penaltyAAA() { changeEnergy(-200);} public void penaltyAA() { changeEnergy(-5);} public void penaltyA() { changeEnergy(-2);} public void kill() { this.alive = false; changeEnergy(-500000); Env.clearMaterial(x, y, animalMaterial); } //kill是最大的惩罚 @@ -130,7 +133,7 @@ public abstract class Animal {// 这个程序大量用到public变量而不是ge public void show(Graphics g) {// 显示当前动物 if (!alive) return; - //g.drawImage(animalImage, x - 8, y - 8, 16, 16, null);// 减去坐标,保证嘴巴显示在当前x,y处 + g.drawImage(animalImage, x - 8, y - 8, 16, 16, null);// 减去坐标,保证嘴巴显示在当前x,y处 } /** Check if x,y,z out of animal's brain range */ @@ -214,9 +217,33 @@ public abstract class Animal {// 这个程序大量用到public变量而不是ge return false; } - //TODO:1.视觉光子产生,如果位于眼睛处有细胞,产生光子 + //1.将外界食物信号投放到视网膜上产生光子 + seeFood(); - //TODO:2.光子循环,每个光子行走一步, 直到光子消失,如果光子落在移动细胞上将消失,并会移动 + //2.光子主循环,每个光子行走一步, 直到光子消失,如果光子落在移动细胞上将消失,并会移动。这里有个编程技巧是用另一个list来累加新的光子,不对原list作删增,以加快速度 +// photons2.clear(); +// for (float[] p : photons) { +// float[] p2 = movePhoton(p); +// if (p2 != null) { +// int xx = (int) p2[X]; +// int yy = (int) p2[Y]; +// int zz = (int) p2[Z]; +// if ((cells[xx][yy][zz] & 1) > 0) +// y++; +// if ((cells[xx][yy][zz] & 2) > 0) +// y--; +// if ((cells[xx][yy][zz] & 4) > 0) +// x--; +// if ((cells[xx][yy][zz] & 8) > 0) +// x++; +// photons2.add(p2); +// } +// } + +// if (Food.foundAndAteFood(x, y)) { +// this.ateFood++; +// this.awardAA(); +// } //TODO:3.根据青蛙移动的矢量汇总出移动方向和步数,实际移动青蛙 @@ -224,65 +251,50 @@ public abstract class Animal {// 这个程序大量用到public变量而不是ge //TODO:5.如果青蛙与有毒食物位置重合,在所有痛觉细胞处产生光子,即惩罚信号的发生,痛觉细胞的位置和数量不是指定的,而是进化出来的 - //=============================================================================================== - //现在的分水岭是以光子为循环主体,还是以细胞作为循环主体??? 前者的话,细胞是光子的中转站,后者的话,细胞之间互相用光子挖洞,可以不把光子模拟出来 - //=============================================================================================== - - //依次激活每个细胞,模拟并行激活,这个是依次x,y,z方向激活,可能会产会顺序驱逐信号的bug,以后要考虑改成随机或跳行次序激活 - // 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++) { - // long c = cells[x][y][z]; - // float energy = energys[x][y][z]; - // float wasteEnergy = 0; //细胞会增加或消耗的总量 - // long pos = 1; - // - // if (energy < 0) //如细胞能量小于0,表示过分消耗了,需时间来慢慢回填 - // energy += 0.1; - // - // if (z == Z_TOP && (c & pos) > 0) {//感光细胞,将光信号能量存贮到细胞里, 感光细胞只能出现在最上层 - // if (Env.foundAnyThingOrOutEdge(this.x + x - XY_CENTER, this.y + y - XY_CENTER)) { - // energys[x][y][z] = 100; - // } else { - // energys[x][y][z] = 0; - // } - // } - // - // pos <<= 1; - // if (z == 0 && (c & pos) > 0) {//向上运动细胞,只能出现在底层,任意位置都可 - // wasteEnergy++; - // this.y++; - // } - // - // pos <<= 1; - // if (z == 0 && (c & pos) > 0) {//向下运动细胞,只能出现在底层,任意位置都可 - // wasteEnergy++; - // this.y--; - // } - // - // pos <<= 1; - // if (z == 0 && (c & pos) > 0) {//向左运动细胞,只能出现在底层,任意位置都可 - // wasteEnergy++; - // this.x--; - // } - // - // pos <<= 1; - // if (z == 0 && (c & pos) > 0) {//向右运动细胞,只能出现在底层,任意位置都可 - // wasteEnergy++; - // this.x++; - // } - // - // for (int i = 0; i < 3; i++) - // for (int j = 0; j < 3; j++) { - // pos <<= 1; - // if (energy > 0 && (c & pos) > 0) {//一类固定角度的传输型参数,即能量以指定角度传送到它的相邻细胞 - // //TODO - // } - // } - // - // energys[x][y][z] -= wasteEnergy; - // } + List temp = photons; + photons = photons2; //互换,让photons指向新的结果 + photons2 = temp; //让photons2指向原来的photons,以免创建新对象 return alive; } + public static final int X = 0; + public static final int Y = 1; + public static final int Z = 2; + public static final int MX = 3; + public static final int MY = 4; + public static final int MZ = 5; + public static final int ENERGY = 6; + public static final int SPEED = 7; + + private void createPhoton(float x, float y, float z, float mx, float my, float mz, float energy, float speed) {//在脑空间产生光子 + photons.add(new float[]{x, y, z, mx, my, mz, energy, speed}); + } + + private float[] movePhoton(float[] p) {//光子沿移动方向走一格,能量减少为95% + p[ENERGY] *= .99f; + if (p[ENERGY] < 0.01) + return null; + if (p[SPEED] < 0.01) + return p; + p[X] += p[MX]; + p[Y] += p[MY]; + p[Z] += p[MZ]; + if (Env.insideBrain(p[X], p[Y], p[Z])) + return p; + return null; + } + + private void seeFood() { + if (Food.smell[x][y] > 0) { //这是程序优化,如果闻到香味,说明食物在附近,才允许开启眼睛在香味范围内看图像 + for (int xx = -Food.SMELL_RANGE; xx <= Food.SMELL_RANGE; xx++) + for (int yy = -Food.SMELL_RANGE; yy <= Food.SMELL_RANGE; yy++) { + if (Env.insideBrain(xx + BRAIN_CENTER, yy + BRAIN_CENTER) && Env.foundAnyThingOrOutEdge(x + xx, y + yy)) { //如看到任何东西或看到出界 + for (float xxx = -0.1f; xxx <= 0.1f; xxx += 0.05) + for (float yyy = -0.1f; yyy <= 0.1f; yyy += 0.05) // 形成一个扇面向下发送光子 + createPhoton(xx + BRAIN_CENTER, yy + BRAIN_CENTER, BRAIN_TOP, xxx, yyy, -1f, 1f, 1f); + } + } + } + } + } diff --git a/core/src/main/java/com/gitee/drinkjava2/frog/Application.java b/core/src/main/java/com/gitee/drinkjava2/frog/Application.java index e5f7b09..07b63d6 100644 --- a/core/src/main/java/com/gitee/drinkjava2/frog/Application.java +++ b/core/src/main/java/com/gitee/drinkjava2/frog/Application.java @@ -1,6 +1,5 @@ package com.gitee.drinkjava2.frog; -import java.awt.Component; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; 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 b26f9b3..ba90298 100644 --- a/core/src/main/java/com/gitee/drinkjava2/frog/Env.java +++ b/core/src/main/java/com/gitee/drinkjava2/frog/Env.java @@ -31,7 +31,7 @@ public class Env extends JPanel { /** 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_EGG_QTY = 25; // 每轮下n个青蛙蛋,可调,只有最优秀的前n个青蛙们才允许下蛋 public static final int FROG_PER_EGG = 4; // 每个青蛙蛋可以孵出几个青蛙 @@ -42,16 +42,14 @@ public class Env extends JPanel { public static boolean SAVE_EGGS_FILE = false; //从2021-11-23起,添加这个选项,允许不输出蛋文件到磁盘上 - public static final boolean BORN_AT_RANDOM_PLACE = true;// 孵出动物落在地图上随机位置,而不是在蛋所在地 + public static final boolean BORN_AT_RANDOM_PLACE = true;// 孵出青蛙落在地图上随机位置,而不是在蛋所在地 - /** Frog's brain size */ // 脑细胞位于脑范围内,是个三维结构,在animal中用一个List来存贮表示的同时,也用一个Cell3D动态数组来表示 + /** Frog's brain size */ // 脑细胞位于脑范围内,是个三维结构,在animal中用三维数组来表示 + public static final int BRAIN_CUBE_SIZE = 16; //脑立方边长大小,必须是2的幂数如4,8,16...,原因参见8叉树算法 - public static final int BRAIN_CUBE_SIZE = 8; //脑立方边长大小,必须是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; //脑细胞总数不能超过这个值 + public static final int BRAIN_XSIZE = BRAIN_CUBE_SIZE; // 脑在X方向长度 + public static final int BRAIN_YSIZE = BRAIN_CUBE_SIZE; // 脑在Y方向长度 + public static final int BRAIN_ZSIZE = BRAIN_CUBE_SIZE; // 脑在Z方向长度 /** SHOW first animal's brain structure */ public static boolean SHOW_FIRST_ANIMAL_BRAIN = true; // 是否显示脑图在Env区的右侧 @@ -70,21 +68,18 @@ public class Env extends JPanel { /** Steps of one test round */ public static final int STEPS_PER_ROUND = 20;// 每轮测试步数,可调 - public static int step;// 当前测试步数 - public static final int FOOD_QTY = 1500; // 食物数量, 可调 + public static final int FOOD_QTY = 3500; // 食物数量, 可调 // 以下是程序内部变量,不要手工修改它们 + public static int step; // 当前测试步数 + 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 @@ -93,19 +88,19 @@ public class Env extends JPanel { public static List frog_eggs = new ArrayList<>(); // 这里存放新建或从磁盘载入上轮下的蛋,每个蛋可能生成几个青蛙, - public static EnvObject[] things = new EnvObject[]{new Food() };// 所有外界物体,如食物、字母测试工具都放在这个things里面 - - public static boolean show_split_detail=false; //是否显示脑分裂的细节过程,即从一个细胞开始分裂分裂,而不是只显示分裂的最终结果 - - public static boolean[] display_gene=new boolean[Cells.GENE_NUMBERS]; //脑最多有64个基因,这里用来控制哪些基因需要显示在脑图上 - + 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; + for (int i = 0; i < display_gene.length; i++) + display_gene[i] = true; } public Env() { @@ -114,6 +109,18 @@ public class Env extends JPanel { this.setBounds(1, 1, ENV_WIDTH, ENV_HEIGHT); } + public static boolean insideBrain(int x, int y) {// 如果指定点在边界内 + return !(x < 0 || y < 0 || x >= BRAIN_XSIZE || y >= BRAIN_YSIZE); + } + + public static boolean insideBrain(int x, int y, int z) {// 如果指定点在边界内 + return !(x < 0 || y < 0 || z <= 0 || x >= BRAIN_XSIZE || y >= BRAIN_YSIZE || z >= BRAIN_ZSIZE); + } + + public static boolean insideBrain(float x, float y, float z) {// 如果指定点在边界内 + return !(x < 0 || y < 0 || z <= 0 || x >= BRAIN_XSIZE || y >= BRAIN_YSIZE || z >= BRAIN_ZSIZE); + } + public static boolean insideEnv(int x, int y) {// 如果指定点在边界内 return !(x < 0 || y < 0 || x >= ENV_WIDTH || y >= ENV_HEIGHT); } @@ -130,15 +137,6 @@ public class Env extends JPanel { 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 @@ -209,14 +207,10 @@ public class Env extends JPanel { 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) + return new StringBuilder("吃食率:").append(format100.format(Food.food_ated * 1.00 / FOOD_QTY)).append(", 平均: ").append(Food.food_ated * 1.0f / FROG_PER_SCREEN).append(",最多:").append(maxFound) .toString(); } - 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 { @@ -247,8 +241,6 @@ public class Env extends JPanel { 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(); @@ -272,8 +264,7 @@ public class Env extends JPanel { if (SHOW_SPEED > 0 && step % SHOW_SPEED != 0) // 用是否跳帧画图的方式来控制速度 continue; - if (SHOW_SPEED < 0) // 如果speed小于0,人为加入延迟 - sleep(-SHOW_SPEED); + sleep(100); // 开始画虚拟环境和青蛙和蛇 g.setColor(Color.white); @@ -310,12 +301,8 @@ public class Env extends JPanel { 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; - } + for (EnvObject thing : things)// 去除食物、陷阱等物体 + thing.destory(); } round++; FrogEggTool.layEggs(); //能量高的青蛙才有权下蛋 diff --git a/core/src/main/java/com/gitee/drinkjava2/frog/brain/BrainPicture.java b/core/src/main/java/com/gitee/drinkjava2/frog/brain/BrainPicture.java index a361e84..22a3c64 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 @@ -297,17 +297,15 @@ public class BrainPicture extends JPanel { 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) + if (Env.show_split_detail) drawSplitDetail(); - else + else drawBrainStructure(); } - + public void drawSplitDetail() {// 在这个方法里绘制脑细胞分裂的显示步聚,即从一个细胞开始分裂成最终脑结构的每一步 Animal a = Env.getShowAnimal(); // 第一个青蛙或蛇 @@ -321,7 +319,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.keep[j]>=0) { + if (Tree8Util.keep[j] >= 0) { int[] node = Tree8Util.TREE8[j]; int size = node[0]; if (size == i && Env.display_gene[geneIndex]) {//如果允许显示的话, 显示当前层级的节点 @@ -342,7 +340,7 @@ public class BrainPicture extends JPanel { } } } - + public void drawBrainStructure() {// 在这个方法里进行动物的三维脑结构的绘制,蛇是青蛙的子类,所以也可以当参数传进来 Animal a = Env.getShowAnimal(); // 显示第一个青蛙或蛇 if (a == null || !a.alive) @@ -366,6 +364,10 @@ public class BrainPicture extends JPanel { } } + setPicColor(Color.ORANGE); //开始画出光子 + for (float[] p : a.photons) + drawPoint(p[0]+0.5f, (int)p[1]+0.5f, (int)p[2]+0.5f, 0.2f); + setPicColor(Color.BLACK); //BrainShapeJudge.show(this);//这行显示目标形状这个模子 @@ -384,6 +386,10 @@ public class BrainPicture extends JPanel { if (note != null) // 全局注释 g.drawString(note, 30, 55); this.getGraphics().drawImage(buffImg, 0, 0, this);// 利用缓存避免画面闪烁,这里输出缓存图片 + try { + Thread.sleep(100); + } catch (InterruptedException e) { + } } public static void setNote(String note) { 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 72785c0..6f96629 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,41 +18,46 @@ package com.gitee.drinkjava2.frog.brain; */ @SuppressWarnings("all") public class Cells { - public static long[] masks = new long[64]; + public static long[] GENES = new long[64]; + public static int GENE_NUMBERS = 0; //目前有多少条基因,每个脑细胞用是一个long来存储,所以最多允许64条基因,每个基因控制一个细胞的参数 - private static long mask = 1L; + private static long gene = 1L; - public static final long EYE = nextMask(); //光线感觉细胞,即视网膜 - public static final long FELL_HAPPY = nextMask(); //快乐感觉细胞,通常吃食后激活 - public static final long FELL_PAIN = nextMask(); //痛苦感觉细胞,受伤害后激活 - - public static final long MOVE_UP = nextMask(); //细胞如激活,青蛙向上运动 - public static final long MOVE_DOWN = nextMask();//细胞如激活,青蛙向下运动 - public static final long MOVE_LEFT = nextMask(); //细胞如激活,青蛙向左运动 - public static final long MOVE_RIGHT = nextMask(); //细胞如激活,青蛙向右运动 - - public static final long PHOTON_DELETE = nextMask(); // 删除光子 - public static final long PHOTON_ABSORB = nextMask(); // 删除并吸收光子能量 - public static final long PHOTON_FIX = nextMask(); //固定光子,使光子不能移动 - public static final long PHOTON_ENHENCE = nextMask(); // 提高光子能量 - public static final long PHOTON_WEAKEN = nextMask(); //减弱光子能量 - public static final long PHOTON_SEND = nextMask(); //如细胞有能量,发送光子 - public static final long PHOTON_SEND_NEG = nextMask(); //如细胞有能量,发送负能量光子 - - public static final long PHOTON_LIMIT_UP = nextMask(); //光子只能向上扇面发送 - public static final long PHOTON_LIMIT_DOWN = nextMask(); //光子只能向下扇面发送 - public static final long PHOTON_LIMIT_LEFT = nextMask(); //光子只能向左扇面发送 - public static final long PHOTON_LIMIT_RIGHT = nextMask(); //光子只能向右扇面发送 - public static final long PHOTON_LIMIT_FRONT = nextMask(); //光子只能向前扇面发送 - public static final long PHOTON_LIMIT_BACK = nextMask(); //光子只能向后扇面发送 + public static final long TREE_CELL = nextMask(); //细胞如激活,青蛙向上运动 + + //public static final long EYE = nextMask(); //光线感觉细胞,即视网膜 +// public static final long FELL_HAPPY = nextMask(); //快乐感觉细胞,通常吃食后激活 +// public static final long FELL_PAIN = nextMask(); //痛苦感觉细胞,受伤害后激活 +// +// public static final long MOVE_UP = nextMask(); //细胞如激活,青蛙向上运动 +// public static final long MOVE_DOWN = nextMask();//细胞如激活,青蛙向下运动 +// public static final long MOVE_LEFT = nextMask(); //细胞如激活,青蛙向左运动 +// public static final long MOVE_RIGHT = nextMask(); //细胞如激活,青蛙向右运动 +// public static final long MOVE_ANY = MOVE_UP | MOVE_DOWN | MOVE_LEFT | MOVE_RIGHT; //任意移动,是上面四个bit位的合并 + +// +// public static final long PHOTON_DELETE = nextMask(); // 删除光子 +// public static final long PHOTON_ABSORB = nextMask(); // 删除并吸收光子能量 +// public static final long PHOTON_FIX = nextMask(); //固定光子,使光子不能移动 +// public static final long PHOTON_ENHENCE = nextMask(); // 提高光子能量 +// public static final long PHOTON_WEAKEN = nextMask(); //减弱光子能量 +// public static final long PHOTON_SEND = nextMask(); //如细胞有能量,发送光子 +// public static final long PHOTON_SEND_NEG = nextMask(); //如细胞有能量,发送负能量光子 +// +// public static final long PHOTON_LIMIT_UP = nextMask(); //光子只能向上扇面发送 +// public static final long PHOTON_LIMIT_DOWN = nextMask(); //光子只能向下扇面发送 +// public static final long PHOTON_LIMIT_LEFT = nextMask(); //光子只能向左扇面发送 +// public static final long PHOTON_LIMIT_RIGHT = nextMask(); //光子只能向右扇面发送 +// public static final long PHOTON_LIMIT_FRONT = nextMask(); //光子只能向前扇面发送 +// public static final long PHOTON_LIMIT_BACK = nextMask(); //光子只能向后扇面发送 private static long nextMask() {// 每次将Code左移1位 - long result = mask; + long result = gene; if (result < 0) throw new IllegalArgumentException("Mask out of maximum long integer range"); - masks[GENE_NUMBERS++] = mask; - mask = mask << 1; + GENES[GENE_NUMBERS++] = gene; + gene = gene << 1; //这个gene占用long的一位,将来判断一个细胞是否包含此基因只要与它做“与”运算 return result; } - + } diff --git a/core/src/main/java/com/gitee/drinkjava2/frog/judge/MoveCellLocationJudge.java b/core/src/main/java/com/gitee/drinkjava2/frog/judge/MoveCellLocationJudge.java new file mode 100644 index 0000000..5a005f0 --- /dev/null +++ b/core/src/main/java/com/gitee/drinkjava2/frog/judge/MoveCellLocationJudge.java @@ -0,0 +1,42 @@ +package com.gitee.drinkjava2.frog.judge; + +import com.gitee.drinkjava2.frog.Animal; +import com.gitee.drinkjava2.frog.Env; + +/** + * MoveCellLocationJudge determine move cells can only be on bottom layer of brain + * + * 运动细胞只允许出现在脑的最底层,否则扣分 + */ +public class MoveCellLocationJudge {//NOSONAR + + public static void judge(Animal animal) {////检查animal的脑细胞分布和参数是否符合要求并加减分 + 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++) { + long cell = animal.cells[x][y][z]; + if (z >= 1) { + if ((cell & 1) > 0) //注意四个条件要分别判断和扣分,不能合并放在同一个if条件里,否则互相干扰,进化不出结果 + animal.penaltyAAAA(); + if ((cell & 2) > 0) + animal.penaltyAAAA(); + if ((cell & 4) > 0) + animal.penaltyAAAA(); + if ((cell & 8) > 0) + animal.penaltyAAAA(); + } + if (z == 0) { + if ((cell & 1) > 0) //注意四个条件要分别判断和扣分,不能合并放在同一个if条件里,否则互相干扰,进化不出结果 + animal.awardAAAA(); + if ((cell & 2) > 0) + animal.awardAAAA(); + if ((cell & 4) > 0) + animal.awardAAAA(); + if ((cell & 8) > 0) + animal.awardAAAA(); + } + } + + } + +} diff --git a/core/src/main/java/com/gitee/drinkjava2/frog/judge/TreeShapeJudge.java b/core/src/main/java/com/gitee/drinkjava2/frog/judge/TreeShapeJudge.java new file mode 100644 index 0000000..5c7a95d --- /dev/null +++ b/core/src/main/java/com/gitee/drinkjava2/frog/judge/TreeShapeJudge.java @@ -0,0 +1,53 @@ +package com.gitee.drinkjava2.frog.judge; + +import com.gitee.drinkjava2.frog.Animal; +import com.gitee.drinkjava2.frog.Env; +import com.gitee.drinkjava2.frog.brain.Cells; + +/** + * TreeShapeJudge to create a tree + */ +public class TreeShapeJudge {//NOSONAR + + private static boolean hasCell(long[][][] cells, int x, int y, int z) { //检查指定位置是否有TREE_CELL + if (Animal.outBrainRange(x, y, z)) + return false; + return (cells[x][y][z] & Cells.TREE_CELL) > 0; + } + + //@formatter:off + private static boolean hasCellAround(long[][][] cells, int x, int y, int z) {//检查四周是否有TREE_CELL + if (hasCell(cells, x + 1, y, z))return true; + if (hasCell(cells, x - 1, y, z))return true; + if (hasCell(cells, x + 1, y+1, z))return true; + if (hasCell(cells, x - 1, y+1, z))return true; + if (hasCell(cells, x + 1, y-1, z))return true; + if (hasCell(cells, x - 1, y-1, z))return true; + if (hasCell(cells, x, y+1, z))return true; + if (hasCell(cells, x, y-1, z))return true; + return false; + } + //@formatter:on + + public static void judge(Animal animal) { + long[][][] cells = animal.cells; + 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 - 2; z++) { + long cell = cells[x][y][z]; + if ((cell & Cells.TREE_CELL) > 0) { + if ((z == 0 && x == Env.BRAIN_XSIZE / 2 && y == Env.BRAIN_YSIZE / 2) //如果在底部中心 + || //或 + (!hasCell(cells, x, y, z - 1) // 正下方没有cell + && !hasCellAround(cells, x, y, z) //且周围没有cell + && hasCellAround(cells, x, y, z - 1) //且下方周围有cell + )) + animal.awardAA(); + else + animal.penaltyAA(); + } + } + + } + +} diff --git a/core/src/main/java/com/gitee/drinkjava2/frog/objects/Food.java b/core/src/main/java/com/gitee/drinkjava2/frog/objects/Food.java index be12a6a..6bbbcb2 100644 --- a/core/src/main/java/com/gitee/drinkjava2/frog/objects/Food.java +++ b/core/src/main/java/com/gitee/drinkjava2/frog/objects/Food.java @@ -19,29 +19,63 @@ 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 { +public enum Food implements EnvObject { + FOOD; //FOOD是一个枚举型单例,整个环境只允许有一个FOOD实例 + + public static final int SMELL_RANGE = 3; + + public static int food_ated=0; + + public static int[][] smell = new int[ENV_WIDTH][ENV_HEIGHT];//食物的香味, 这个香味是为了优化速度,和算法无关。有香味,说明食物在附近,程序才会启动眼睛,在视网膜产生光子,没有香味就不启动眼睛以加快速度 @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); + food_ated=0; + for (int i = 0; i < FOOD_QTY; i++) { // 随机位置生成食物 + int x = RandomUtils.nextInt(ENV_WIDTH); + int y = RandomUtils.nextInt(ENV_HEIGHT); + if (!Env.hasMaterial(x, y, Material.FOOD)) { + Env.setMaterial(x, y, Material.FOOD); //在环境里标记上FOOD + changeSmell(x, y, 1); //产生此食物的香气 + } } } + @Override + public void destory() { + food_ated=0; + for (int x = 0; x < ENV_WIDTH; x++) // 清除食物 + for (int y = 0; y < ENV_HEIGHT; y++) { + Env.clearMaterial(x, y, Material.FOOD); + smell[x][y] = 0; //清除所有香气 + } + } + @Override public void active() { + //食物除了被吃,它自己没有什么活动 + } + + private static void changeSmell(int x, int y, int value) { //在食物的附近增加或减少它的香味 + for (int xx = x - SMELL_RANGE; xx <= x + SMELL_RANGE; xx++) + for (int yy = y - SMELL_RANGE; yy <= y + SMELL_RANGE; yy++) + if (Env.insideEnv(xx, yy)) + smell[xx][yy] += value; + } + + public static boolean foundAndAteFood(int x, int y) {// 如果x,y有食物,将其清0,返回true + if (Env.hasMaterial(x, y, Material.FOOD)) { + food_ated++; + Env.clearMaterial(x, y, Material.FOOD);//在环境里清除FOOD + changeSmell(x, y, -1); //仅清除此食物产生的香气 + return true; + } + return false; } } diff --git a/history/001_first_version/pom.xml b/history/001_first_version/pom.xml index ccec02b..2df0b34 100644 --- a/history/001_first_version/pom.xml +++ b/history/001_first_version/pom.xml @@ -57,14 +57,7 @@ - - - - com.alibaba - fastjson - 1.2.54 - - + + 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/010_tree_grow/run.bat b/history/010_tree_grow/run.bat new file mode 100644 index 0000000..0156beb --- /dev/null +++ b/history/010_tree_grow/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/010_tree_grow/run.sh b/history/010_tree_grow/run.sh new file mode 100644 index 0000000..836c941 --- /dev/null +++ b/history/010_tree_grow/run.sh @@ -0,0 +1,2 @@ +mvn clean package +java -jar target/frog-*.jar diff --git a/history/010_tree_grow/src/main/java/com/gitee/drinkjava2/frog/Animal.java b/history/010_tree_grow/src/main/java/com/gitee/drinkjava2/frog/Animal.java new file mode 100644 index 0000000..77446ff --- /dev/null +++ b/history/010_tree_grow/src/main/java/com/gitee/drinkjava2/frog/Animal.java @@ -0,0 +1,300 @@ +/* + * 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 java.util.List; + +import javax.imageio.ImageIO; + +import com.gitee.drinkjava2.frog.egg.Egg; +import com.gitee.drinkjava2.frog.judge.TreeShapeJudge; +import com.gitee.drinkjava2.frog.objects.Food; +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 List photons = new ArrayList<>(); //每个光子是由一个float数组表示,依次是x,y,z坐标, mz,my,mz运动方向矢量,能量值,速度 + + public List photons2 = new ArrayList<>();// photons2是个临时空间,用来中转存放一下每遍光子运算后的结果,用双鬼拍门来替代单个链表的增删,每个list只增不减以优化速度 + + 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); //外界对是否长得象彩虹鱼打分 + //MoveCellLocationJudge.judge(this); + TreeShapeJudge.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(2000);} + public void awardAAA() { changeEnergy(200);} + public void awardAA() { changeEnergy(5);} + public void awardA() { changeEnergy(2);} + + public void penaltyAAAA() { changeEnergy(-2000);} + public void penaltyAAA() { changeEnergy(-200);} + public void penaltyAA() { changeEnergy(-5);} + public void penaltyA() { changeEnergy(-2);} + public void kill() { this.alive = false; changeEnergy(-500000); Env.clearMaterial(x, y, animalMaterial); } //kill是最大的惩罚 + //@formatter:on + + 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(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; + } + } + + private static final int BRAIN_CENTER = Env.BRAIN_CUBE_SIZE / 2; + private static final int BRAIN_TOP = Env.BRAIN_CUBE_SIZE - 1; + + public boolean active() {// 这个active方法在每一步循环都会被调用,是脑思考的最小帧,最复杂的这个方法写在最下面 + // 如果能量小于0、出界、与非食物的点重合则判死 + if (!alive) + return false; + if (energy <= 0 || Env.outsideEnv(x, y) || Env.bricks[x][y] >= Material.KILL_ANIMAL) { + kill(); + return false; + } + + //1.将外界食物信号投放到视网膜上产生光子 + seeFood(); + + //2.光子主循环,每个光子行走一步, 直到光子消失,如果光子落在移动细胞上将消失,并会移动。这里有个编程技巧是用另一个list来累加新的光子,不对原list作删增,以加快速度 +// photons2.clear(); +// for (float[] p : photons) { +// float[] p2 = movePhoton(p); +// if (p2 != null) { +// int xx = (int) p2[X]; +// int yy = (int) p2[Y]; +// int zz = (int) p2[Z]; +// if ((cells[xx][yy][zz] & 1) > 0) +// y++; +// if ((cells[xx][yy][zz] & 2) > 0) +// y--; +// if ((cells[xx][yy][zz] & 4) > 0) +// x--; +// if ((cells[xx][yy][zz] & 8) > 0) +// x++; +// photons2.add(p2); +// } +// } + +// if (Food.foundAndAteFood(x, y)) { +// this.ateFood++; +// this.awardAA(); +// } + + //TODO:3.根据青蛙移动的矢量汇总出移动方向和步数,实际移动青蛙 + + //TODO:4.如果青蛙与食物位置重合,在所有奖励细胞处产生光子,即奖励信号的发生,奖励细胞的位置和数量不是指定的,而是进化出来的 + + //TODO:5.如果青蛙与有毒食物位置重合,在所有痛觉细胞处产生光子,即惩罚信号的发生,痛觉细胞的位置和数量不是指定的,而是进化出来的 + + List temp = photons; + photons = photons2; //互换,让photons指向新的结果 + photons2 = temp; //让photons2指向原来的photons,以免创建新对象 + return alive; + } + + public static final int X = 0; + public static final int Y = 1; + public static final int Z = 2; + public static final int MX = 3; + public static final int MY = 4; + public static final int MZ = 5; + public static final int ENERGY = 6; + public static final int SPEED = 7; + + private void createPhoton(float x, float y, float z, float mx, float my, float mz, float energy, float speed) {//在脑空间产生光子 + photons.add(new float[]{x, y, z, mx, my, mz, energy, speed}); + } + + private float[] movePhoton(float[] p) {//光子沿移动方向走一格,能量减少为95% + p[ENERGY] *= .99f; + if (p[ENERGY] < 0.01) + return null; + if (p[SPEED] < 0.01) + return p; + p[X] += p[MX]; + p[Y] += p[MY]; + p[Z] += p[MZ]; + if (Env.insideBrain(p[X], p[Y], p[Z])) + return p; + return null; + } + + private void seeFood() { + if (Food.smell[x][y] > 0) { //这是程序优化,如果闻到香味,说明食物在附近,才允许开启眼睛在香味范围内看图像 + for (int xx = -Food.SMELL_RANGE; xx <= Food.SMELL_RANGE; xx++) + for (int yy = -Food.SMELL_RANGE; yy <= Food.SMELL_RANGE; yy++) { + if (Env.insideBrain(xx + BRAIN_CENTER, yy + BRAIN_CENTER) && Env.foundAnyThingOrOutEdge(x + xx, y + yy)) { //如看到任何东西或看到出界 + for (float xxx = -0.1f; xxx <= 0.1f; xxx += 0.05) + for (float yyy = -0.1f; yyy <= 0.1f; yyy += 0.05) // 形成一个扇面向下发送光子 + createPhoton(xx + BRAIN_CENTER, yy + BRAIN_CENTER, BRAIN_TOP, xxx, yyy, -1f, 1f, 1f); + } + } + } + } + +} diff --git a/history/010_tree_grow/src/main/java/com/gitee/drinkjava2/frog/Application.java b/history/010_tree_grow/src/main/java/com/gitee/drinkjava2/frog/Application.java new file mode 100644 index 0000000..07b63d6 --- /dev/null +++ b/history/010_tree_grow/src/main/java/com/gitee/drinkjava2/frog/Application.java @@ -0,0 +1,168 @@ +package com.gitee.drinkjava2.frog; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.File; + +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JRadioButton; +import javax.swing.JSlider; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import com.gitee.drinkjava2.frog.brain.BrainPicture; +import com.gitee.drinkjava2.frog.brain.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*16, 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/010_tree_grow/src/main/java/com/gitee/drinkjava2/frog/Env.java b/history/010_tree_grow/src/main/java/com/gitee/drinkjava2/frog/Env.java new file mode 100644 index 0000000..ba90298 --- /dev/null +++ b/history/010_tree_grow/src/main/java/com/gitee/drinkjava2/frog/Env.java @@ -0,0 +1,312 @@ +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 = 25; // 每轮下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中用三维数组来表示 + public static final int BRAIN_CUBE_SIZE = 16; //脑立方边长大小,必须是2的幂数如4,8,16...,原因参见8叉树算法 + + public static final int BRAIN_XSIZE = BRAIN_CUBE_SIZE; // 脑在X方向长度 + public static final int BRAIN_YSIZE = BRAIN_CUBE_SIZE; // 脑在Y方向长度 + public static final int BRAIN_ZSIZE = BRAIN_CUBE_SIZE; // 脑在Z方向长度 + + /** 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 = 20;// 每轮测试步数,可调 + + public static final int FOOD_QTY = 3500; // 食物数量, 可调 + + // 以下是程序内部变量,不要手工修改它们 + public static int step; // 当前测试步数 + + public static final int TOTAL_FROG_QTY = FROG_EGG_QTY * FROG_PER_EGG; // 蛇的总数 + + public static final int FROG_PER_SCREEN = TOTAL_FROG_QTY / SCREEN; // 每屏显示几个青蛙,这个数值由其它常量计算得来 + + public static int current_screen = 0; + + public static boolean pause = false; // 暂停按钮按下将暂停测试 + + public static int[][] bricks = new int[ENV_WIDTH][ENV_HEIGHT];// 组成环境的材料,见Material.java + + public static 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 insideBrain(int x, int y) {// 如果指定点在边界内 + return !(x < 0 || y < 0 || x >= BRAIN_XSIZE || y >= BRAIN_YSIZE); + } + + public static boolean insideBrain(int x, int y, int z) {// 如果指定点在边界内 + return !(x < 0 || y < 0 || z <= 0 || x >= BRAIN_XSIZE || y >= BRAIN_YSIZE || z >= BRAIN_ZSIZE); + } + + public static boolean insideBrain(float x, float y, float z) {// 如果指定点在边界内 + return !(x < 0 || y < 0 || z <= 0 || x >= BRAIN_XSIZE || y >= BRAIN_YSIZE || z >= BRAIN_ZSIZE); + } + + 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 foundFrogOrOutEdge(int x, int y) {// 如果指定点看到青蛙或超出边界,返回true + if (x < 0 || y < 0 || x >= ENV_WIDTH || y >= ENV_HEIGHT) + return true;// 如果出界返回true + if ((Env.bricks[x][y] & Material.FROG_TAG) > 0) + return true; + else + return false; + } + + public static void setMaterial(int x, int y, int material) { + if (Env.insideEnv(x, y)) + Env.bricks[x][y] = Env.bricks[x][y] | material; + } + + public static boolean hasMaterial(int x, int y, int material) { + if (!Env.insideEnv(x, y)) + return false; + return (Env.bricks[x][y] & material) > 0; + } + + public static void clearMaterial(int x, int y, int material) { + if (Env.insideEnv(x, y)) + Env.bricks[x][y] = Env.bricks[x][y] & ~material; + } + + private void rebuildFrogs() {// 根据蛙蛋重新孵化出蛙 + frogs.clear(); + for (int i = 0; i < frog_eggs.size(); i++) {// 创建青蛙,每个蛋生成n个蛙,并随机取一个别的蛋作为精子 + int loop = FROG_PER_EGG; + if (frog_eggs.size() > 20) { // 如果数量多,进行一些优化,让排名靠前的Egg多孵出青蛙 + if (i < FROG_PER_EGG)// 0,1,2,3 + loop = FROG_PER_EGG + 1; + if (i >= (frog_eggs.size() - FROG_PER_EGG)) + loop = FROG_PER_EGG - 1; + } + for (int j = 0; j < loop; j++) { + Egg zygote = new Egg(frog_eggs.get(i), frog_eggs.get(RandomUtils.nextInt(frog_eggs.size()))); + Frog f = new Frog(zygote); + frogs.add(f); + f.no = frogs.size(); + } + } + } + + private void drawWorld(Graphics g) { + int brick; + for (int x = 0; x < ENV_WIDTH; x++) + for (int y = 0; y < ENV_HEIGHT; y++) { + brick = bricks[x][y]; + if (brick != 0) { + g.setColor(Material.color(brick)); + if ((brick & Material.FOOD) > 0) { + g.fillRoundRect(x, y, 4, 4, 2, 2); //食物只有一个点太小,画大一点 + } else + g.drawLine(x, y, x, y); // only 1 point + } + } + g.setColor(Color.BLACK); + } + + static final NumberFormat format100 = NumberFormat.getPercentInstance(); + static { + format100.setMaximumFractionDigits(2); + } + + private String foodAtedCount() {// 统计吃食总数等 + int maxFound = 0; + for (Frog f : frogs) + if (f.ateFood > maxFound) + maxFound = f.ateFood; + return new StringBuilder("吃食率:").append(format100.format(Food.food_ated * 1.00 / FOOD_QTY)).append(", 平均: ").append(Food.food_ated * 1.0f / FROG_PER_SCREEN).append(",最多:").append(maxFound) + .toString(); + } + + public static void checkIfPause() { + 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个蛙 + 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; + + sleep(100); + + // 开始画虚拟环境和青蛙和蛇 + 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(); + } + round++; + FrogEggTool.layEggs(); //能量高的青蛙才有权下蛋 + } while (true); + } + +} diff --git a/history/010_tree_grow/src/main/java/com/gitee/drinkjava2/frog/Frog.java b/history/010_tree_grow/src/main/java/com/gitee/drinkjava2/frog/Frog.java new file mode 100644 index 0000000..f2dbeb8 --- /dev/null +++ b/history/010_tree_grow/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/010_tree_grow/src/main/java/com/gitee/drinkjava2/frog/brain/BrainPicture.java b/history/010_tree_grow/src/main/java/com/gitee/drinkjava2/frog/brain/BrainPicture.java new file mode 100644 index 0000000..22a3c64 --- /dev/null +++ b/history/010_tree_grow/src/main/java/com/gitee/drinkjava2/frog/brain/BrainPicture.java @@ -0,0 +1,456 @@ +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.ORANGE); //开始画出光子 + for (float[] p : a.photons) + drawPoint(p[0]+0.5f, (int)p[1]+0.5f, (int)p[2]+0.5f, 0.2f); + + 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);// 利用缓存避免画面闪烁,这里输出缓存图片 + try { + Thread.sleep(100); + } catch (InterruptedException e) { + } + } + + 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/010_tree_grow/src/main/java/com/gitee/drinkjava2/frog/brain/Cells.java b/history/010_tree_grow/src/main/java/com/gitee/drinkjava2/frog/brain/Cells.java new file mode 100644 index 0000000..6f96629 --- /dev/null +++ b/history/010_tree_grow/src/main/java/com/gitee/drinkjava2/frog/brain/Cells.java @@ -0,0 +1,63 @@ +/* + * 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 long[] GENES = new long[64]; + + public static int GENE_NUMBERS = 0; //目前有多少条基因,每个脑细胞用是一个long来存储,所以最多允许64条基因,每个基因控制一个细胞的参数 + private static long gene = 1L; + + public static final long TREE_CELL = nextMask(); //细胞如激活,青蛙向上运动 + + //public static final long EYE = nextMask(); //光线感觉细胞,即视网膜 +// public static final long FELL_HAPPY = nextMask(); //快乐感觉细胞,通常吃食后激活 +// public static final long FELL_PAIN = nextMask(); //痛苦感觉细胞,受伤害后激活 +// +// public static final long MOVE_UP = nextMask(); //细胞如激活,青蛙向上运动 +// public static final long MOVE_DOWN = nextMask();//细胞如激活,青蛙向下运动 +// public static final long MOVE_LEFT = nextMask(); //细胞如激活,青蛙向左运动 +// public static final long MOVE_RIGHT = nextMask(); //细胞如激活,青蛙向右运动 +// public static final long MOVE_ANY = MOVE_UP | MOVE_DOWN | MOVE_LEFT | MOVE_RIGHT; //任意移动,是上面四个bit位的合并 + +// +// public static final long PHOTON_DELETE = nextMask(); // 删除光子 +// public static final long PHOTON_ABSORB = nextMask(); // 删除并吸收光子能量 +// public static final long PHOTON_FIX = nextMask(); //固定光子,使光子不能移动 +// public static final long PHOTON_ENHENCE = nextMask(); // 提高光子能量 +// public static final long PHOTON_WEAKEN = nextMask(); //减弱光子能量 +// public static final long PHOTON_SEND = nextMask(); //如细胞有能量,发送光子 +// public static final long PHOTON_SEND_NEG = nextMask(); //如细胞有能量,发送负能量光子 +// +// public static final long PHOTON_LIMIT_UP = nextMask(); //光子只能向上扇面发送 +// public static final long PHOTON_LIMIT_DOWN = nextMask(); //光子只能向下扇面发送 +// public static final long PHOTON_LIMIT_LEFT = nextMask(); //光子只能向左扇面发送 +// public static final long PHOTON_LIMIT_RIGHT = nextMask(); //光子只能向右扇面发送 +// public static final long PHOTON_LIMIT_FRONT = nextMask(); //光子只能向前扇面发送 +// public static final long PHOTON_LIMIT_BACK = nextMask(); //光子只能向后扇面发送 + + private static long nextMask() {// 每次将Code左移1位 + long result = gene; + if (result < 0) + throw new IllegalArgumentException("Mask out of maximum long integer range"); + GENES[GENE_NUMBERS++] = gene; + gene = gene << 1; //这个gene占用long的一位,将来判断一个细胞是否包含此基因只要与它做“与”运算 + return result; + } + +} diff --git a/history/010_tree_grow/src/main/java/com/gitee/drinkjava2/frog/brain/Cuboid.java b/history/010_tree_grow/src/main/java/com/gitee/drinkjava2/frog/brain/Cuboid.java new file mode 100644 index 0000000..1e15c42 --- /dev/null +++ b/history/010_tree_grow/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/010_tree_grow/src/main/java/com/gitee/drinkjava2/frog/brain/MouseAction.java b/history/010_tree_grow/src/main/java/com/gitee/drinkjava2/frog/brain/MouseAction.java new file mode 100644 index 0000000..337e5de --- /dev/null +++ b/history/010_tree_grow/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/010_tree_grow/src/main/java/com/gitee/drinkjava2/frog/egg/Egg.java b/history/010_tree_grow/src/main/java/com/gitee/drinkjava2/frog/egg/Egg.java new file mode 100644 index 0000000..b536614 --- /dev/null +++ b/history/010_tree_grow/src/main/java/com/gitee/drinkjava2/frog/egg/Egg.java @@ -0,0 +1,65 @@ +/* + * Copyright 2018 the original author or authors. + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by + * applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS + * OF ANY KIND, either express or implied. See the License for the specific + * language governing permissions and limitations under the License. + */ +package com.gitee.drinkjava2.frog.egg; + +import java.io.Serializable; +import java.util.ArrayList; + +import com.gitee.drinkjava2.frog.Animal; +import com.gitee.drinkjava2.frog.Env; +import com.gitee.drinkjava2.frog.util.RandomUtils; + +/** + * Egg is the static structure description of brain cells + * + * 蛋存在的目的是为了以最小的字节数串行化存储脑细胞,它是海量脑细胞的生成算法描述,而不是脑细胞本身 + * 蛋和基因的关系:基因是一种语言,相当于染色体,不存在坐标位置。蛋则是基因的载体,有x,y坐标,表示在虚拟环境中蛋存在的位置。 + * + * 另外青蛙本身也是基因的载体,所以青蛙里有一个gene属性 + * + * @author Yong Zhu + * @since 1.0 + */ +public class Egg implements Serializable { + private static final long serialVersionUID = 1L; + public int x; // 蛋的x位置 + public int y; // 蛋的y位置 + + // gene record the 8-tree structure of brain cells + // 基因是随机生成的8叉树数据结构,和实际生物每个细胞都要保存一份基因不同,程序中每个脑细胞并不需要保存基因的副本,这样可以极大地减少内存占用 + public ArrayList> genes = new ArrayList<>(); + + public Egg() {// 无中生有,创建一个蛋,先有蛋,后有蛙 + 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); //TODO: 两个蛋的基因混合 + } + +} diff --git a/history/010_tree_grow/src/main/java/com/gitee/drinkjava2/frog/egg/FrogEggTool.java b/history/010_tree_grow/src/main/java/com/gitee/drinkjava2/frog/egg/FrogEggTool.java new file mode 100644 index 0000000..0fa2c0e --- /dev/null +++ b/history/010_tree_grow/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/010_tree_grow/src/main/java/com/gitee/drinkjava2/frog/judge/MoveCellLocationJudge.java b/history/010_tree_grow/src/main/java/com/gitee/drinkjava2/frog/judge/MoveCellLocationJudge.java new file mode 100644 index 0000000..5a005f0 --- /dev/null +++ b/history/010_tree_grow/src/main/java/com/gitee/drinkjava2/frog/judge/MoveCellLocationJudge.java @@ -0,0 +1,42 @@ +package com.gitee.drinkjava2.frog.judge; + +import com.gitee.drinkjava2.frog.Animal; +import com.gitee.drinkjava2.frog.Env; + +/** + * MoveCellLocationJudge determine move cells can only be on bottom layer of brain + * + * 运动细胞只允许出现在脑的最底层,否则扣分 + */ +public class MoveCellLocationJudge {//NOSONAR + + public static void judge(Animal animal) {////检查animal的脑细胞分布和参数是否符合要求并加减分 + 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++) { + long cell = animal.cells[x][y][z]; + if (z >= 1) { + if ((cell & 1) > 0) //注意四个条件要分别判断和扣分,不能合并放在同一个if条件里,否则互相干扰,进化不出结果 + animal.penaltyAAAA(); + if ((cell & 2) > 0) + animal.penaltyAAAA(); + if ((cell & 4) > 0) + animal.penaltyAAAA(); + if ((cell & 8) > 0) + animal.penaltyAAAA(); + } + if (z == 0) { + if ((cell & 1) > 0) //注意四个条件要分别判断和扣分,不能合并放在同一个if条件里,否则互相干扰,进化不出结果 + animal.awardAAAA(); + if ((cell & 2) > 0) + animal.awardAAAA(); + if ((cell & 4) > 0) + animal.awardAAAA(); + if ((cell & 8) > 0) + animal.awardAAAA(); + } + } + + } + +} diff --git a/history/010_tree_grow/src/main/java/com/gitee/drinkjava2/frog/judge/RainBowFishJudge.java b/history/010_tree_grow/src/main/java/com/gitee/drinkjava2/frog/judge/RainBowFishJudge.java new file mode 100644 index 0000000..39cd07f --- /dev/null +++ b/history/010_tree_grow/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; + +/** + * RainBowFishJudge to judge if is a rainbow fish, 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) {//检查颜色与小鱼图像重合,且呈斑马纹色彩分布,是就加分,否则扣分 + float colorWidth = 1.0f * 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的脑细胞分布和参数是否符合要求并加减分 + 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/010_tree_grow/src/main/java/com/gitee/drinkjava2/frog/judge/TreeShapeJudge.java b/history/010_tree_grow/src/main/java/com/gitee/drinkjava2/frog/judge/TreeShapeJudge.java new file mode 100644 index 0000000..5c7a95d --- /dev/null +++ b/history/010_tree_grow/src/main/java/com/gitee/drinkjava2/frog/judge/TreeShapeJudge.java @@ -0,0 +1,53 @@ +package com.gitee.drinkjava2.frog.judge; + +import com.gitee.drinkjava2.frog.Animal; +import com.gitee.drinkjava2.frog.Env; +import com.gitee.drinkjava2.frog.brain.Cells; + +/** + * TreeShapeJudge to create a tree + */ +public class TreeShapeJudge {//NOSONAR + + private static boolean hasCell(long[][][] cells, int x, int y, int z) { //检查指定位置是否有TREE_CELL + if (Animal.outBrainRange(x, y, z)) + return false; + return (cells[x][y][z] & Cells.TREE_CELL) > 0; + } + + //@formatter:off + private static boolean hasCellAround(long[][][] cells, int x, int y, int z) {//检查四周是否有TREE_CELL + if (hasCell(cells, x + 1, y, z))return true; + if (hasCell(cells, x - 1, y, z))return true; + if (hasCell(cells, x + 1, y+1, z))return true; + if (hasCell(cells, x - 1, y+1, z))return true; + if (hasCell(cells, x + 1, y-1, z))return true; + if (hasCell(cells, x - 1, y-1, z))return true; + if (hasCell(cells, x, y+1, z))return true; + if (hasCell(cells, x, y-1, z))return true; + return false; + } + //@formatter:on + + public static void judge(Animal animal) { + long[][][] cells = animal.cells; + 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 - 2; z++) { + long cell = cells[x][y][z]; + if ((cell & Cells.TREE_CELL) > 0) { + if ((z == 0 && x == Env.BRAIN_XSIZE / 2 && y == Env.BRAIN_YSIZE / 2) //如果在底部中心 + || //或 + (!hasCell(cells, x, y, z - 1) // 正下方没有cell + && !hasCellAround(cells, x, y, z) //且周围没有cell + && hasCellAround(cells, x, y, z - 1) //且下方周围有cell + )) + animal.awardAA(); + else + animal.penaltyAA(); + } + } + + } + +} diff --git a/history/010_tree_grow/src/main/java/com/gitee/drinkjava2/frog/objects/EnvObject.java b/history/010_tree_grow/src/main/java/com/gitee/drinkjava2/frog/objects/EnvObject.java new file mode 100644 index 0000000..9f9229d --- /dev/null +++ b/history/010_tree_grow/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/010_tree_grow/src/main/java/com/gitee/drinkjava2/frog/objects/Food.java b/history/010_tree_grow/src/main/java/com/gitee/drinkjava2/frog/objects/Food.java new file mode 100644 index 0000000..6bbbcb2 --- /dev/null +++ b/history/010_tree_grow/src/main/java/com/gitee/drinkjava2/frog/objects/Food.java @@ -0,0 +1,81 @@ +/* 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 + * 食物 + * + * @author Yong Zhu + * @since 1.0 + */ +public enum Food implements EnvObject { + FOOD; //FOOD是一个枚举型单例,整个环境只允许有一个FOOD实例 + + public static final int SMELL_RANGE = 3; + + public static int food_ated=0; + + public static int[][] smell = new int[ENV_WIDTH][ENV_HEIGHT];//食物的香味, 这个香味是为了优化速度,和算法无关。有香味,说明食物在附近,程序才会启动眼睛,在视网膜产生光子,没有香味就不启动眼睛以加快速度 + + @Override + public void build() { + food_ated=0; + for (int i = 0; i < FOOD_QTY; i++) { // 随机位置生成食物 + int x = RandomUtils.nextInt(ENV_WIDTH); + int y = RandomUtils.nextInt(ENV_HEIGHT); + if (!Env.hasMaterial(x, y, Material.FOOD)) { + Env.setMaterial(x, y, Material.FOOD); //在环境里标记上FOOD + changeSmell(x, y, 1); //产生此食物的香气 + } + } + } + + @Override + public void destory() { + food_ated=0; + for (int x = 0; x < ENV_WIDTH; x++) // 清除食物 + for (int y = 0; y < ENV_HEIGHT; y++) { + Env.clearMaterial(x, y, Material.FOOD); + smell[x][y] = 0; //清除所有香气 + } + } + + @Override + public void active() { + //食物除了被吃,它自己没有什么活动 + } + + private static void changeSmell(int x, int y, int value) { //在食物的附近增加或减少它的香味 + for (int xx = x - SMELL_RANGE; xx <= x + SMELL_RANGE; xx++) + for (int yy = y - SMELL_RANGE; yy <= y + SMELL_RANGE; yy++) + if (Env.insideEnv(xx, yy)) + smell[xx][yy] += value; + } + + public static boolean foundAndAteFood(int x, int y) {// 如果x,y有食物,将其清0,返回true + if (Env.hasMaterial(x, y, Material.FOOD)) { + food_ated++; + Env.clearMaterial(x, y, Material.FOOD);//在环境里清除FOOD + changeSmell(x, y, -1); //仅清除此食物产生的香气 + return true; + } + return false; + } + +} diff --git a/history/010_tree_grow/src/main/java/com/gitee/drinkjava2/frog/objects/Material.java b/history/010_tree_grow/src/main/java/com/gitee/drinkjava2/frog/objects/Material.java new file mode 100644 index 0000000..2e1931d --- /dev/null +++ b/history/010_tree_grow/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/010_tree_grow/src/main/java/com/gitee/drinkjava2/frog/util/ColorUtils.java b/history/010_tree_grow/src/main/java/com/gitee/drinkjava2/frog/util/ColorUtils.java new file mode 100644 index 0000000..d9776e7 --- /dev/null +++ b/history/010_tree_grow/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/010_tree_grow/src/main/java/com/gitee/drinkjava2/frog/util/Cube.java b/history/010_tree_grow/src/main/java/com/gitee/drinkjava2/frog/util/Cube.java new file mode 100644 index 0000000..eeaa522 --- /dev/null +++ b/history/010_tree_grow/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/010_tree_grow/src/main/java/com/gitee/drinkjava2/frog/util/LocalFileUtils.java b/history/010_tree_grow/src/main/java/com/gitee/drinkjava2/frog/util/LocalFileUtils.java new file mode 100644 index 0000000..7422435 --- /dev/null +++ b/history/010_tree_grow/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/010_tree_grow/src/main/java/com/gitee/drinkjava2/frog/util/Logger.java b/history/010_tree_grow/src/main/java/com/gitee/drinkjava2/frog/util/Logger.java new file mode 100644 index 0000000..8c72e24 --- /dev/null +++ b/history/010_tree_grow/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/010_tree_grow/src/main/java/com/gitee/drinkjava2/frog/util/RandomUtils.java b/history/010_tree_grow/src/main/java/com/gitee/drinkjava2/frog/util/RandomUtils.java new file mode 100644 index 0000000..dff7533 --- /dev/null +++ b/history/010_tree_grow/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/010_tree_grow/src/main/java/com/gitee/drinkjava2/frog/util/StringPixelUtils.java b/history/010_tree_grow/src/main/java/com/gitee/drinkjava2/frog/util/StringPixelUtils.java new file mode 100644 index 0000000..db8d1ea --- /dev/null +++ b/history/010_tree_grow/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/010_tree_grow/src/main/java/com/gitee/drinkjava2/frog/util/Tree8Util.java b/history/010_tree_grow/src/main/java/com/gitee/drinkjava2/frog/util/Tree8Util.java new file mode 100644 index 0000000..5e84f73 --- /dev/null +++ b/history/010_tree_grow/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(KEEP, 0, keep, 0, NODE_QTY)快速清空enable数组 + + public static int keepNodeQTY = NODE_QTY; //这里临时记录需keep的节点总数,好用来继续敲除,初始值是全部节点 + + private static int index = 0; + static { + tree8Split(0, 0, 0, Env.BRAIN_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/result17_tree_grow.gif b/result17_tree_grow.gif new file mode 100644 index 0000000..6370556 Binary files /dev/null and b/result17_tree_grow.gif differ diff --git a/result3.gif b/result3.gif index ec4ad86..97f1523 100644 Binary files a/result3.gif and b/result3.gif differ diff --git a/初学者入门介绍.md b/初学者入门介绍.md index 8c4d158..6a7907c 100644 --- a/初学者入门介绍.md +++ b/初学者入门介绍.md @@ -1,5 +1,6 @@ ## 初学者入门介绍 -注意这个介绍只是针对码云"人工生命"(以后简称Frog)这个项目本身,并不是神经网络知识入门介绍。 +注意这个介绍只是针对码云"人工生命"(以后简称Frog)这个项目本身,并不是神经网络知识入门介绍。 +下面的介绍仅针对早期版本,因时间关系没有继续更新,后来陆续更新了好多版后,按时间顺序放在history目录下。大家可以参数Readme中的介绍一个目录一个目录地看,如果想研究源码的,建议看懂003b_simple目录。但是总的来说,源码并不重要,关键是思路。 ### 项目的运行环境 Frog项目需要安装JDK8、Maven、GIT客户端,如果已经在本机上安装过的请跳过这一节: diff --git a/捐款记录.md b/捐款记录.md index d430637..bae2fa8 100644 --- a/捐款记录.md +++ b/捐款记录.md @@ -1,4 +1,4 @@ -这个项目的捐款将用于项目本身,如发布开发任务等。设这个捐款记录的目的是为了让收支透明化,如需捐赠请西联汇款"加拿大朱勇"收。本记录按时间顺序记录捐款人和当前总额,不记具体数额,这样即体现了不鼓励金额攀比、自愿随意的原则,也能通过历史记录查询出每笔的具体数额。在此衷心地感谢每一位捐款、点赞、及关注这个项目的同学! +这个项目的捐款将用于项目本身,如发布开发任务等。设这个捐款记录的目的是为了让收支透明化,如用西联汇款请写"Canada"、"Zhu, Yong"收。本记录按时间顺序记录捐款人和当前总额,不记具体数额,这样即体现了自愿随意的原则,也能通过历史记录查询出每笔的具体数额。在此衷心地感谢每一位捐款、点赞、及关注这个项目的同学! 捐款(按时间顺序): wangtao @@ -30,11 +30,17 @@ cctbby seanhuqi 万码牛叔叔 波斯家 -目前收入总额:663.77元 +QuietPear +忨岩 +陈抟老祖 +TommyLemon +老v表 + +目前收入总额:884.77元 支出(按时间顺序): 细胞分裂演示(pama_1234):500元 目前支出总额:500元 -目前余额:163.77元 \ No newline at end of file +目前余额:384.77元 \ No newline at end of file