diff --git a/core/src/main/java/com/github/drinkjava2/frog/Env.java b/core/src/main/java/com/github/drinkjava2/frog/Env.java index 8df9378..8504bca 100644 --- a/core/src/main/java/com/github/drinkjava2/frog/Env.java +++ b/core/src/main/java/com/github/drinkjava2/frog/Env.java @@ -21,7 +21,7 @@ import com.github.drinkjava2.frog.egg.EggTool; @SuppressWarnings("serial") public class Env extends JPanel { /** Speed of test */ - public static final int SHOW_SPEED = 80; // 测试速度,1~1000,可调, 数值越小,速度越慢 + public static final int SHOW_SPEED = 1; // 测试速度,1~1000,可调, 数值越小,速度越慢 public static final int ENV_WIDTH = 400; // 虚拟环境的宽度, 可调 @@ -40,6 +40,8 @@ public class Env extends JPanel { /** Delete eggs at beginning of each run */ public static final boolean DELETE_EGGS = false;// 每次运行是否先删除保存的蛋 + public static final boolean DEBUG_MODE = false; // Debug 模式下会打印出更多的调试信息 + static { if (DELETE_EGGS) EggTool.deleteEggs(); @@ -53,7 +55,7 @@ public class Env extends JPanel { public static boolean pause = false; // 暂停按钮按下将暂停测试 - public static final boolean[][] foods = new boolean[ENV_WIDTH][ENV_HEIGHT];// 食物数组定义 + private static final boolean[][] foods = new boolean[ENV_WIDTH][ENV_HEIGHT];// 食物数组定义 public List frogs = new ArrayList<>(); @@ -65,6 +67,26 @@ public class Env extends JPanel { this.setBounds(1, 1, ENV_WIDTH, ENV_HEIGHT); } + public static boolean insideEnv(int x, int y) { + return !(x < 0 || y < 0 || x >= ENV_WIDTH || y >= ENV_HEIGHT); + } + + public static boolean outsideEnv(int x, int y) { + return x < 0 || y < 0 || x >= ENV_WIDTH || y >= ENV_HEIGHT; + } + + public static boolean foundFood(int x, int y) { + return !(x < 0 || y < 0 || x >= ENV_WIDTH || y >= ENV_HEIGHT) && Env.foods[x][y]; + } + + public static boolean foundAndDeleteFood(int x, int y) {// 如果x,y有食物,将其清0,返回true + if (foundFood(x, y)) { + foods[x][y] = false; + return true; + } + return false; + } + private void rebuildFrogAndFood() { frogs.clear(); for (int i = 0; i < ENV_WIDTH; i++) {// 清除食物 @@ -124,7 +146,7 @@ public class Env extends JPanel { for (int i = 0; i < STEPS_PER_ROUND; i++) { if (allDead) { System.out.println("All dead at round:" + i); - break; // 全死光了就直接跳到下一轮,以节省时间 + break; // 青蛙全死光了就直接跳到下一轮,以节省时间 } allDead = true; for (Frog frog : frogs) diff --git a/core/src/main/java/com/github/drinkjava2/frog/Frog.java b/core/src/main/java/com/github/drinkjava2/frog/Frog.java index 9bfe216..1607324 100644 --- a/core/src/main/java/com/github/drinkjava2/frog/Frog.java +++ b/core/src/main/java/com/github/drinkjava2/frog/Frog.java @@ -15,43 +15,35 @@ import java.awt.Image; import java.io.FileInputStream; import java.util.ArrayList; import java.util.List; -import java.util.Random; import javax.imageio.ImageIO; import com.github.drinkjava2.frog.brain.Cell; import com.github.drinkjava2.frog.brain.Input; import com.github.drinkjava2.frog.brain.Organ; -import com.github.drinkjava2.frog.brain.Output; -import com.github.drinkjava2.frog.brain.Zone; -import com.github.drinkjava2.frog.brain.group.RandomConnectGroup; import com.github.drinkjava2.frog.egg.Egg; /** - * Frog = brain + organ, but now let's only focus on brain, organs are hard - * coded in egg + * Frog = organs + brain cells * - * 青蛙由脑细胞和器官组成,目前脑细胞可以变异、进化、遗传,以由电脑自动生成神经网络,但是器官在蛋里硬编码,不许进化,将来可以考虑器官的进化 + * 青蛙由器官组成,器官中的Group类会生成各种脑细胞 * * @author Yong Zhu * @since 1.0 */ public class Frog { - public RandomConnectGroup[] cellGroups; - /** brain cells */ public List cells = new ArrayList<>(); /** organs */ public List organs = new ArrayList<>(); - public int x; // frog在env中的x坐标 - public int y; // frog在env中的y坐标 - public long energy = 10000; // 能量为0则死掉 - public boolean alive = true; // 设为false表示青蛙死掉了,将不参与任何计算和显示,以节省时间 + public int x; // frog在Env中的x坐标 + public int y; // frog在Env中的y坐标 + public long frogEngery = 100000; // 能量为0则死掉 + public boolean alive = true; // 设为false表示青蛙死掉了,将不参与计算和显示,以节省时间 - static final Random r = new Random(); static Image frogImg; static { try { @@ -64,62 +56,36 @@ public class Frog { public Frog(int x, int y, Egg egg) { this.x = x; this.y = y; - - // Brain cells - if (egg.cellGroups != null) { - cellGroups = new RandomConnectGroup[egg.cellGroups.length]; - for (int k = 0; k < egg.cellGroups.length; k++) { - RandomConnectGroup g = egg.cellGroups[k]; - cellGroups[k] = new RandomConnectGroup(g); - for (int i = 0; i < g.cellQty; i++) {// 开始根据蛋来创建脑细胞 - Cell c = new Cell(); - c.group = k; - int cellQTY = Math.round(g.inputQtyPerCell); - c.inputs = new Input[cellQTY]; - for (int j = 0; j < cellQTY; j++) { - c.inputs[j] = new Input(); - c.inputs[j].cell = c; - Zone.copyXY(randomPosInZone(g.groupInputZone), c.inputs[j]); - c.inputs[j].radius = g.cellInputRadius; - } - cellQTY = Math.round(g.outputQtyPerCell); - c.outputs = new Output[cellQTY]; - for (int j = 0; j < cellQTY; j++) { - c.outputs[j] = new Output(); - c.outputs[j].cell = c; - Zone.copyXY(randomPosInZone(g.groupOutputZone), c.outputs[j]); - c.outputs[j].radius = g.cellOutputRadius; - } - cells.add(c); - } - } + for (Organ org : egg.organs) { + organs.add(org); + org.init(this);// 每个新器官初始化,如果是Group类,它们会生成许多脑细胞 } - - for (Organ org : egg.organs) - organs.add(org.newCopy()); - } public boolean active(Env v) { - energy -= 20; - if (!alive) + frogEngery -= 1; + if (!alive) { + frogEngery -= 50;// 死了需要消耗更多的能量,退出生存竞争 return false; - if (energy < 0) { + } + if (frogEngery < 0) { // 如果能量小于0则死 alive = false; return false; } - - for (Organ o : organs) + // for (Cell cell1 : cells) {// 大脑主循环 + // if (cell1.energy > 0) + // cell1.energy--; + // for (Input input : cell1.inputs) { + // // TODO + // } + // } + for (Organ o : organs) { // 调用每个Organ的active方法 o.active(this); + } return alive; } - private static Zone randomPosInZone(Zone z) { - return new Zone(z.x - z.radius + z.radius * 2 * r.nextFloat(), z.y - z.radius + z.radius * 2 * r.nextFloat(), - 0); - } - - public void show(Graphics g) { + public void show(Graphics g) {// 显示青蛙的图象 if (!alive) return; g.drawImage(frogImg, x - 8, y - 8, 16, 16, null); diff --git a/core/src/main/java/com/github/drinkjava2/frog/brain/BrainPicture.java b/core/src/main/java/com/github/drinkjava2/frog/brain/BrainPicture.java index 78aa207..966622e 100644 --- a/core/src/main/java/com/github/drinkjava2/frog/brain/BrainPicture.java +++ b/core/src/main/java/com/github/drinkjava2/frog/brain/BrainPicture.java @@ -15,7 +15,6 @@ import javax.swing.JPanel; import com.github.drinkjava2.frog.Application; import com.github.drinkjava2.frog.Frog; -import com.github.drinkjava2.frog.brain.group.RandomConnectGroup; /** * BrainPicture show first frog's brain structure, for debug purpose only @@ -36,30 +35,30 @@ public class BrainPicture extends JPanel { this.setBounds(x, y, brainDispWidth + 1, brainDispWidth + 1); } - void drawZone(Graphics g, Zone z) { + public void drawZone(Graphics g, Zone z) { float rate = brainDispWidth / brainWidth; int x = Math.round(z.x * rate); int y = Math.round(z.y * rate); - int radius = Math.round(z.radius * rate); + int radius = Math.round(z.r * rate); g.drawRect(x - radius, y - radius, radius * 2, radius * 2); } - void drawCircle(Graphics g, Zone z) { + public void drawCircle(Graphics g, Zone z) { float rate = brainDispWidth / brainWidth; int x = Math.round(z.x * rate); int y = Math.round(z.y * rate); g.drawArc(x - 5, y - 5, 10, 10, 0, 360); } - void fillZone(Graphics g, Zone z) { + public void fillZone(Graphics g, Zone z) { float rate = brainDispWidth / brainWidth; int x = Math.round(z.x * rate); int y = Math.round(z.y * rate); - int radius = Math.round(z.radius * rate); + int radius = Math.round(z.r * rate); g.fillRect(x - radius, y - radius, radius * 2, radius * 2); } - void drawLine(Graphics g, Zone z1, Zone z2) { + public void drawLine(Graphics g, Zone z1, Zone z2) { float rate = brainDispWidth / brainWidth; int x1 = Math.round(z1.x * rate); int y1 = Math.round(z1.y * rate); @@ -68,11 +67,11 @@ public class BrainPicture extends JPanel { g.drawLine(x1, y1, x2, y2); } - void drawText(Graphics g, Zone z, String text) { + public void drawText(Graphics g, Zone z, String text) { float rate = brainDispWidth / brainWidth; int x = Math.round(z.x * rate); int y = Math.round(z.y * rate); - g.drawString(text, x - text.length() * 3, y); + g.drawString(text, x - text.length() * 3 - 2, y); } private static final Color[] rainbow = new Color[] { RED, ORANGE, YELLOW, GREEN, CYAN, BLUE, MAGENTA }; @@ -84,8 +83,10 @@ public class BrainPicture extends JPanel { return rainbow[nextColor++]; } - private static Color color(float i) { - if (i <= 1) + public static Color color(float i) { + if (i == 0) + return Color.black; + if (i == 1) return Color.RED; if (i <= 3) return Color.ORANGE; @@ -109,26 +110,7 @@ public class BrainPicture extends JPanel { g.setColor(Color.black); g.drawRect(0, 0, brainDispWidth, brainDispWidth); - for (Organ organ : frog.organs) { - g.setColor(Color.BLACK); - drawZone(g, organ); - if (organ.name != null) - drawText(g, organ, String.valueOf(organ.name)); - } - - for (RandomConnectGroup group : frog.cellGroups) { - if (!group.inherit) - g.setColor(Color.lightGray); // 如果是本轮随机生成的,灰色表示 - else - g.setColor(color(group.cellQty)); // 如果是继承的,彩虹色表示,颜色数越往后表示数量越多 - drawLine(g, group.groupInputZone, group.groupOutputZone); - drawZone(g, group.groupInputZone); - fillZone(g, group.groupOutputZone); - if (group.fat > 0) { - g.setColor(Color.BLACK); - drawCircle(g, group.groupOutputZone); // 如果胖了,表示激活过了,下次下蛋少不了这一组 - } - } - + for (Organ organ : frog.organs) + organ.drawOnBrainPicture(this); // each organ draw itself } } diff --git a/core/src/main/java/com/github/drinkjava2/frog/brain/Cell.java b/core/src/main/java/com/github/drinkjava2/frog/brain/Cell.java index 3889841..0321019 100644 --- a/core/src/main/java/com/github/drinkjava2/frog/brain/Cell.java +++ b/core/src/main/java/com/github/drinkjava2/frog/brain/Cell.java @@ -10,16 +10,25 @@ */ package com.github.drinkjava2.frog.brain; +import com.github.drinkjava2.frog.brain.group.Group; + /** - * Cell is a brain nerve cell, this is the basic unit of frog's brain + * Cell is the basic unit of frog's brain * * @author Yong Zhu * @since 1.0 */ public class Cell { - public int group; // this cell belong to which group - public Input[] inputs; // inputs of cell - public Output[] outputs; // outputs of cell - public long energy; + // this cell belong to frog's which organ + public Group group; + + // inputs of cell + public Input[] inputs; // 每个细胞有一组输入触突 + + // outputs of cell + public Output[] outputs; // 每个细胞有一组输出触突 + + // energy of cell, energy got from food + public long energy; // 每个细胞当前的能量值 } diff --git a/core/src/main/java/com/github/drinkjava2/frog/brain/Input.java b/core/src/main/java/com/github/drinkjava2/frog/brain/Input.java index de84b46..78f16b4 100644 --- a/core/src/main/java/com/github/drinkjava2/frog/brain/Input.java +++ b/core/src/main/java/com/github/drinkjava2/frog/brain/Input.java @@ -18,5 +18,9 @@ package com.github.drinkjava2.frog.brain; */ @SuppressWarnings("serial") public class Input extends Zone { - public Cell cell; + public Cell cell; // 这个输入触突属于哪个脑细胞 + + public Input(Zone z) { + super(z); + } } diff --git a/core/src/main/java/com/github/drinkjava2/frog/brain/Organ.java b/core/src/main/java/com/github/drinkjava2/frog/brain/Organ.java index 44cf425..f6d7a91 100644 --- a/core/src/main/java/com/github/drinkjava2/frog/brain/Organ.java +++ b/core/src/main/java/com/github/drinkjava2/frog/brain/Organ.java @@ -10,6 +10,9 @@ */ package com.github.drinkjava2.frog.brain; +import java.awt.Color; +import java.awt.Graphics; + import com.github.drinkjava2.frog.Frog; /** @@ -20,20 +23,23 @@ import com.github.drinkjava2.frog.Frog; * @author Yong Zhu * @since 1.0.4 */ -public class Organ extends Zone { +public abstract class Organ extends Zone { private static final long serialVersionUID = 1L; - public String name; // 显示在脑图上的器官名称 - public long fat = 0; // 如果细胞活跃多,fat值高,则保留(以及变异)的可能性大,反之则舍弃掉 - public int qtyLimit = 5; // 最多允许数量 - public boolean inherit = false; // 器官是继承来的,还是变异来的,后者淘汰率会高 + public String name; // 显示在脑图上的器官名称,可选 + + public long fat = 0; // 如果活跃多,fat值高,则保留(及变异)的可能性大,反之则很可能丢弃掉 + + public boolean allowBorrow() { // 是否允许在精子中将这个器官借出 + return false; + } /** If active in this organ's zone? */ public boolean outputActive(Frog f) { for (Cell cell : f.cells) { for (Output output : cell.outputs) { // if (cell.energy > 10 && this.nearby(output)) { - f.cellGroups[cell.group].fat++; - cell.energy -= 30; + cell.group.fat++; + cell.energy -= 1; return true; } } @@ -41,43 +47,49 @@ public class Organ extends Zone { return false; } + /** Set X, Y, Radius, name of current Organ */ + public Organ setXYRN(float x, float y, float r, String name) { + this.x = x; + this.y = y; + this.r = r; + this.name = name; + return this; + } + + /** Child class can override this method to drawing picture */ + public void drawOnBrainPicture(BrainPicture pic) {// 把自已这个器官在脑图上显示出来,子类可以重写这个方法 + Graphics g = pic.getGraphics();// border + g.setColor(Color.BLACK); // 缺省是黑色 + pic.drawZone(g, this); + if (this.name != null) + pic.drawText(g, this, String.valueOf(this.name)); + } + /** make a new copy of current organ */ - public Organ newCopy() { // 新建一份,用于从蛋复制到Frog + public Organ newCopy() { // 创建一个当前器官的副本 Organ newOrgan = null; try { newOrgan = this.getClass().newInstance(); copyXYR(this, newOrgan); newOrgan.name = this.name; - newOrgan.fat = this.fat; + newOrgan.fat = this.fat; return newOrgan; } catch (Exception e) { throw new UnknownError("Can not make new Organ copy for " + this); } } - /** Set X, Y, Radius of current Organ */ - public Organ setXYR(float x, float y, float radius) { - this.x = x; - this.y = y; - this.radius = radius; - return this; - } - - /** Set X, Y, Radius, name of current Organ */ - public Organ setXYRN(float x, float y, float radius, String name) { - this.x = x; - this.y = y; - this.radius = radius; - this.name = name; - return this; + /** Only call once when frog created , Child class can override this method */ + public void init(Frog f) { // 仅在Frog生成时这个方法会调用一次,缺省啥也不干,通常用于Group子类的初始化 } /** Only call once when frog created , Child class can override this method */ - public void init(Frog f) { + public Organ[] vary() { // 在下蛋时每个器官会调用这个方法,缺省返回自已的副本,Group类通常要覆盖这个方法 + return new Organ[] { newCopy() }; } /** Each loop step call active method, Child class can override this method */ - public void active(Frog f) { + public void active(Frog f) { // 每一步都会调用器官的active方法 ,缺省啥也不干 } } diff --git a/core/src/main/java/com/github/drinkjava2/frog/brain/Output.java b/core/src/main/java/com/github/drinkjava2/frog/brain/Output.java index 03816b4..b794b93 100644 --- a/core/src/main/java/com/github/drinkjava2/frog/brain/Output.java +++ b/core/src/main/java/com/github/drinkjava2/frog/brain/Output.java @@ -18,5 +18,9 @@ package com.github.drinkjava2.frog.brain; */ @SuppressWarnings("serial") public class Output extends Zone { - public Cell cell; + public Cell cell; // 这个输出触突属于哪个脑细胞 + + public Output(Zone z) { + super(z); + } } diff --git a/core/src/main/java/com/github/drinkjava2/frog/brain/Zone.java b/core/src/main/java/com/github/drinkjava2/frog/brain/Zone.java index 18e8e31..bd443cf 100644 --- a/core/src/main/java/com/github/drinkjava2/frog/brain/Zone.java +++ b/core/src/main/java/com/github/drinkjava2/frog/brain/Zone.java @@ -22,18 +22,19 @@ import com.github.drinkjava2.frog.Env; */ public class Zone implements Serializable { private static final long serialVersionUID = 1L; + public float x; public float y; - public float radius;// so width of the zone= radius*2 + public float r;// r为这个矩形区边长的一半 public Zone() { - // 空构造器不能省,FastJSON实例化时要用到 + // 空构造器不能省 } - public Zone(float x, float y, float radius) { + public Zone(float x, float y, float r) { this.x = x; this.y = y; - this.radius = radius; + this.r = r; if (this.x < 0) this.x = 0; if (this.y < 0) @@ -47,11 +48,11 @@ public class Zone implements Serializable { public Zone(Zone z) { this.x = z.x; this.y = z.y; - this.radius = z.radius; + this.r = z.r; } public boolean nearby(Zone z) { - float dist = radius + z.radius; + float dist = r + z.r; return (Math.abs(x - z.x) < dist && Math.abs(y - z.y) < dist); } @@ -71,6 +72,13 @@ public class Zone implements Serializable { public static void copyXYR(Zone from, Zone to) { to.x = from.x; to.y = from.y; - to.radius = from.radius; + to.r = from.r; + } + + public void setXYR(float x, float y, float r) { + this.x = x; + this.y = y; + this.r = r; } + } diff --git a/core/src/main/java/com/github/drinkjava2/frog/brain/group/Group.java b/core/src/main/java/com/github/drinkjava2/frog/brain/group/Group.java index 5cbe408..6328118 100644 --- a/core/src/main/java/com/github/drinkjava2/frog/brain/group/Group.java +++ b/core/src/main/java/com/github/drinkjava2/frog/brain/group/Group.java @@ -10,6 +10,7 @@ */ package com.github.drinkjava2.frog.brain.group; +import com.github.drinkjava2.frog.Frog; import com.github.drinkjava2.frog.brain.Organ; /** @@ -19,15 +20,32 @@ import com.github.drinkjava2.frog.brain.Organ; * * 为了与旧版CellGroup区分,新版的这个类命名为Group,它代表了一组分布于一个正方形内的细胞群,细胞数量、每个细胞的触突连接方式等参数由当前Group决定, * 可以说每一种Group代表了一种神经网络算法, 通过无数个Group随机的分布、进化、变异,达到最终脑结构适应环境的变化 - * CellGroup会参与遗传和进化,但是它生成的细胞不会参与遗传。 各个Group生成的细胞相加总和就是脑细胞总数。 + * Group会参与遗传和进化,但是它生成的细胞不会参与遗传。 各个Group生成的细胞相加总和就是脑细胞总数。 * - * Group在脑活动中不起作用,可以把Group比作播种机,把种子排列好后,就撒手不管了,在遗传过程中有一个fat参数,如果细胞活跃多,则保留及变异的可能性大,反之则舍弃掉。 - * Group是器官的一种,所以蛋里存放着所有Group的位置、大小、内部参数等信息,但是蛋里面不保存具体的细胞。这样通过控制有多少个"播种机",就可以控制大脑的结构了。 - * Group可以进行位置、大小变动、复制、分裂等多种形式的变异,原则上Group遗传的一下代与父代是同一个播种类型,但不排除也有可能突变成另一个类型的Group。 + * Group在脑活动中不起作用,可以把Group比作播种机,把种子排列好后,就撒手不管了,在遗传过程中有一个fat参数,如果细胞活跃多,则Group保留及变异的可能性大,反之则舍弃掉。 + * Group是器官的一种,所以蛋里存放着所有Group的位置、大小、内部参数等信息,但是蛋里面不保存具体的细胞。这样通过控制有多少个"播种机",就可以控制大脑的结构了,这样可以缩小蛋的尺寸。 + * 原则上Group遗传的一下代与父代是同一个播种(算法)类型,但不排除也有可能突变成另一个类型的Group。 * * @author Yong Zhu * @since 1.0 */ -public class Group extends Organ { +public abstract class Group extends Organ { private static final long serialVersionUID = 1L; + + @Override + public boolean allowBorrow() { // 是否允许在精子中将这个器官借出 + return true; + } + + /** Each loop step call active method, Child class can override this method */ + @Override + public void active(Frog f) { // 每一步都会调用器官的active方法 + f.frogEngery -= 3; // 每个器官运动都要消耗能量, 死了也要消耗能量 + if (!f.alive) + return; + if (f.frogEngery < 0) { // 如果能量小于0则死 + f.alive = false; + return; + } + } } diff --git a/core/src/main/java/com/github/drinkjava2/frog/brain/group/RandomConnectGroup.java b/core/src/main/java/com/github/drinkjava2/frog/brain/group/RandomConnectGroup.java index 263c459..c47ebe4 100644 --- a/core/src/main/java/com/github/drinkjava2/frog/brain/group/RandomConnectGroup.java +++ b/core/src/main/java/com/github/drinkjava2/frog/brain/group/RandomConnectGroup.java @@ -10,17 +10,25 @@ */ package com.github.drinkjava2.frog.brain.group; -import java.util.Random; +import java.awt.Color; +import java.awt.Graphics; +import com.github.drinkjava2.frog.Frog; +import com.github.drinkjava2.frog.brain.BrainPicture; +import com.github.drinkjava2.frog.brain.Cell; +import com.github.drinkjava2.frog.brain.Input; +import com.github.drinkjava2.frog.brain.Organ; +import com.github.drinkjava2.frog.brain.Output; import com.github.drinkjava2.frog.brain.Zone; +import com.github.drinkjava2.frog.util.RandomUtils; /** * RandomConnectGroup * - * 这是一个随机方式连接两端的Group,它是从旧版的CellGroup改造过来,这是一种最简单的神经元排列方式,只有 - * 它代表一组细胞,触突输入区和输出区分别位于Zone内的任意随机两点。至于是否合理则由frog的遗传进化来决定,不合理的RandomConnectGroup会被淘汰掉。 + * 这是一个随机方式连接两端的Group,它是从旧版的CellGroup改造过来,这是一种最简单的神经元排列方式,只有一组细胞,触突输入区和输出区分别位于Zone内的任意随机两点。 + * 至于是否合理则由frog的遗传进化来决定,不合理的RandomConnectGroup会被淘汰掉。 * - * (还没改造完成,在不破坏原有外在表现的基础上,要平滑将它改造成一个标准Group的子类,也是第一个子类 ) + * (还没改造完成,在不破坏原有外在表现的基础上,要平滑将它改造成一个标准Group的子类,也是第一个子类 ) * * @author Yong Zhu * @since 1.0 @@ -28,50 +36,58 @@ import com.github.drinkjava2.frog.brain.Zone; public class RandomConnectGroup extends Group { private static final long serialVersionUID = 1L; - // TODO need delete below fields, use grid replace - public Zone groupInputZone; // input distribute zone + public Zone inputZone; // 输入触突区 + public Zone outputZone; // 输出触突区 - public Zone groupOutputZone; // output distribute zone + @Override + public void init(Frog f) { + if (inputZone == null) + inputZone = RandomUtils.randomPosInZone(this); + if (outputZone == null) + outputZone = RandomUtils.randomPosInZone(this); - public float cellInputRadius; // input radius of each cell - public float cellOutputRadius; // output radius of each cell + Cell c = new Cell(); + Input in = new Input(inputZone); + in.cell = c; + c.inputs = new Input[] { in }; - public float inputQtyPerCell; // input qty per cell - public float outputQtyPerCell; // output qty per cell - // TODO need delete above fields + Output out = new Output(outputZone); + out.cell = c; + c.outputs = new Output[] { out }; - public float cellQty; // how many nerve cells in this CellGroup - - private static final Random r = new Random(); - - public RandomConnectGroup() { + c.group = this; + f.cells.add(c); + } + @Override + public Organ[] vary() { + if (fat <= 0) + if (RandomUtils.percent(30)) + return new Organ[] {}; + if (RandomUtils.percent(80)) + return new Organ[] { this }; + return new Organ[] { this, newRandomConnGroup(this) }; } - public RandomConnectGroup(RandomConnectGroup g) {// clone old CellGroup - groupInputZone = new Zone(g.groupInputZone); - groupOutputZone = new Zone(g.groupOutputZone); - cellInputRadius = g.cellInputRadius; - cellOutputRadius = g.cellOutputRadius; - cellQty = g.cellQty; - inputQtyPerCell = g.inputQtyPerCell; - outputQtyPerCell = g.outputQtyPerCell; - fat = g.fat; - inherit = g.inherit; + public static RandomConnectGroup newRandomConnGroup(Zone z) { + RandomConnectGroup newOne = new RandomConnectGroup(); + newOne.inputZone = RandomUtils.randomPosInZone(z); + newOne.outputZone = RandomUtils.randomPosInZone(z); + return newOne; } - public RandomConnectGroup(float brainWidth, int randomCellQtyPerGroup, int randomInputQtyPerCell, - int randomOutQtyPerCell) { - inherit = false; - groupInputZone = new Zone(r.nextFloat() * brainWidth, r.nextFloat() * brainWidth, - (float) (r.nextFloat() * brainWidth * .01)); - groupOutputZone = new Zone(r.nextFloat() * brainWidth, r.nextFloat() * brainWidth, - (float) (r.nextFloat() * brainWidth * .01)); - cellQty = 1 + r.nextInt(randomCellQtyPerGroup); - cellInputRadius = (float) (0.001 + r.nextFloat() * 2); - cellOutputRadius = (float) (0.001 + r.nextFloat() * 2); - inputQtyPerCell = 1 + r.nextInt(randomInputQtyPerCell); - outputQtyPerCell = 1 + r.nextInt(randomOutQtyPerCell); + /** Child class can override this method to drawing picture */ + public void drawOnBrainPicture(BrainPicture pic) {// 把自已这个器官在脑图上显示出来,子类可以重写这个方法 + Graphics g = pic.getGraphics();// border + g.setColor(Color.gray); // 缺省是灰色 + pic.drawZone(g, this); + pic.drawLine(g, inputZone, outputZone); + pic.drawZone(g, inputZone); + pic.fillZone(g, outputZone); + if (fat > 0) { + g.setColor(Color.red); + pic.drawCircle(g, outputZone); // 如果胖了,表示激活过了,下次下蛋少不了这一组 + } } } diff --git a/core/src/main/java/com/github/drinkjava2/frog/brain/organ/Eat.java b/core/src/main/java/com/github/drinkjava2/frog/brain/organ/Eat.java index 39fb62e..4d7d29d 100644 --- a/core/src/main/java/com/github/drinkjava2/frog/brain/organ/Eat.java +++ b/core/src/main/java/com/github/drinkjava2/frog/brain/organ/Eat.java @@ -15,24 +15,15 @@ import com.github.drinkjava2.frog.Frog; import com.github.drinkjava2.frog.brain.Organ; /** - * Move up frog 1 unit if outputs of nerve cells active in this zone + * Eat food at current x, y position */ -public class Eat extends Organ { +public class Eat extends Organ {// Eat这个类将食物转化为能量,能量小于0,则青蛙死掉 private static final long serialVersionUID = 1L; @Override public void active(Frog f) { - int x = f.x; - int y = f.y; - if (x < 0 || x >= Env.ENV_WIDTH || y < 0 || y >= Env.ENV_HEIGHT) {// 越界者死! - f.alive = false; - return; - } - - if (Env.foods[x][y]) { - Env.foods[x][y] = false; - f.energy = f.energy + 1000;// 吃掉food,能量境加 - } + if (Env.foundAndDeleteFood(f.x, f.y)) + f.frogEngery = f.frogEngery + 1000;// 如果青蛙的坐标与食物重合,吃掉food,能量境加 } } diff --git a/core/src/main/java/com/github/drinkjava2/frog/brain/organ/Eye.java b/core/src/main/java/com/github/drinkjava2/frog/brain/organ/Eye.java index 2a245c3..198924c 100644 --- a/core/src/main/java/com/github/drinkjava2/frog/brain/organ/Eye.java +++ b/core/src/main/java/com/github/drinkjava2/frog/brain/organ/Eye.java @@ -27,15 +27,11 @@ import com.github.drinkjava2.frog.brain.Zone; public class Eye extends Organ { private static final long serialVersionUID = 1L; - private static boolean hasFood(int x, int y) { - return x >= 0 && y >= 0 && x < Env.ENV_WIDTH && y < Env.ENV_HEIGHT && Env.foods[x][y]; - } - @Override public void active(Frog f) { // 第一个眼睛只能观察上、下、左、右四个方向有没有食物 - float qRadius = radius / 4; - float q3Radius = (float) (radius * .75); + float qRadius = r / 4; + float q3Radius = (float) (r * .75); Zone seeUp = new Zone(x, y + q3Radius, qRadius); Zone seeDown = new Zone(x, y - q3Radius, qRadius); Zone seeLeft = new Zone(x - q3Radius, y, qRadius); @@ -49,28 +45,28 @@ public class Eye extends Organ { int seeDist = 10; for (int i = 1; i < seeDist; i++) - if (hasFood(f.x, f.y + i)) { + if (Env.foundFood(f.x, f.y + i)) { seeFood = true; foodAtUp = true; break; } for (int i = 1; i < seeDist; i++) - if (hasFood(f.x, f.y - i)) { + if (Env.foundFood(f.x, f.y - i)) { seeFood = true; foodAtDown = true; break; } for (int i = 1; i < seeDist; i++) - if (hasFood(f.x - i, f.y)) { + if (Env.foundFood(f.x - i, f.y)) { seeFood = true; foodAtLeft = true; break; } for (int i = 1; i < seeDist; i++) - if (hasFood(f.x + i, f.y)) { + if (Env.foundFood(f.x + i, f.y)) { seeFood = true; foodAtRight = true; break; diff --git a/core/src/main/java/com/github/drinkjava2/frog/brain/organ/Hungry.java b/core/src/main/java/com/github/drinkjava2/frog/brain/organ/Hungry.java index 0e3363f..dcd05f1 100644 --- a/core/src/main/java/com/github/drinkjava2/frog/brain/organ/Hungry.java +++ b/core/src/main/java/com/github/drinkjava2/frog/brain/organ/Hungry.java @@ -23,11 +23,8 @@ public class Hungry extends Organ { @Override public void active(Frog f) { - for (Cell cell : f.cells) { - if (cell.energy > 0) - cell.energy--; - - if (f.energy < 10000 && cell.energy < 100) + for (Cell cell : f.cells) { + if (f.frogEngery < 10000 && cell.energy < 1000) for (Input input : cell.inputs) if (input.nearby(this)) // input zone near by hungry zone cell.energy += 2; diff --git a/core/src/main/java/com/github/drinkjava2/frog/egg/CellGroup.java b/core/src/main/java/com/github/drinkjava2/frog/egg/CellGroup.java new file mode 100644 index 0000000..6768e40 --- /dev/null +++ b/core/src/main/java/com/github/drinkjava2/frog/egg/CellGroup.java @@ -0,0 +1,14 @@ +/* + * 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.github.drinkjava2.frog.egg; + +public class CellGroup { +} diff --git a/core/src/main/java/com/github/drinkjava2/frog/egg/Egg.java b/core/src/main/java/com/github/drinkjava2/frog/egg/Egg.java index 8be97fd..9419561 100644 --- a/core/src/main/java/com/github/drinkjava2/frog/egg/Egg.java +++ b/core/src/main/java/com/github/drinkjava2/frog/egg/Egg.java @@ -13,12 +13,10 @@ package com.github.drinkjava2.frog.egg; import java.io.Serializable; import java.util.ArrayList; import java.util.List; -import java.util.Random; -import com.github.drinkjava2.frog.Env; import com.github.drinkjava2.frog.Frog; import com.github.drinkjava2.frog.brain.Organ; -import com.github.drinkjava2.frog.brain.Zone; +import com.github.drinkjava2.frog.brain.group.Group; import com.github.drinkjava2.frog.brain.group.RandomConnectGroup; import com.github.drinkjava2.frog.brain.organ.Eat; import com.github.drinkjava2.frog.brain.organ.Eye; @@ -27,6 +25,7 @@ import com.github.drinkjava2.frog.brain.organ.MoveDown; import com.github.drinkjava2.frog.brain.organ.MoveLeft; import com.github.drinkjava2.frog.brain.organ.MoveRight; import com.github.drinkjava2.frog.brain.organ.MoveUp; +import com.github.drinkjava2.frog.util.RandomUtils; /** * Egg is the static structure description of frog, can save as text file, to @@ -40,109 +39,12 @@ import com.github.drinkjava2.frog.brain.organ.MoveUp; */ public class Egg implements Serializable { private static final long serialVersionUID = 1L; - public int randomCellGroupQty = 30; // 随机生成多少个组 - public int randomCellQtyPerGroup = 3; // 每个组有多少个脑细胞 - public int randomInputQtyPerCell = 3;// 每个脑细胞有多少个输入触突 - public int randomOutQtyPerCell = 2; // 每个脑细胞有多少个输出触突 - public RandomConnectGroup[] cellGroups; + public List organs = new ArrayList<>(); - public List organs = new ArrayList(); + public List groups = new ArrayList<>(); - public Egg() { - // default constructor - } - - private static Random r = new Random(); - - /** Create a egg by join 2 eggs, x+y=zygote */ - public Egg(Egg x, Egg y) { - // 模拟XY染色体,不能做简单加法,会撑暴内存的,现在每次只随机加一个y的cellgroup进来,这也不太好,因为基因会越加越多,只好用用进废退原则来加大淘汰率。 - - // x里原来的CellGroup - cellGroups = new RandomConnectGroup[x.cellGroups.length + 1 + randomCellGroupQty]; - for (int i = 0; i < x.cellGroups.length; i++) { - RandomConnectGroup oldCellGroup = x.cellGroups[i]; - RandomConnectGroup cellGroup = new RandomConnectGroup(); - cellGroups[i] = cellGroup; - cellGroup.inherit = true; - cellGroup.groupInputZone = new Zone(oldCellGroup.groupInputZone); - cellGroup.groupOutputZone = new Zone(oldCellGroup.groupOutputZone); - cellGroup.cellQty = oldCellGroup.cellQty; - cellGroup.cellInputRadius = oldCellGroup.cellInputRadius; - cellGroup.cellOutputRadius = oldCellGroup.cellOutputRadius; - cellGroup.inputQtyPerCell = oldCellGroup.inputQtyPerCell; - cellGroup.outputQtyPerCell = oldCellGroup.outputQtyPerCell; - } - - // 从y里借一个CellGroup - RandomConnectGroup randomY = y.cellGroups[r.nextInt(y.cellGroups.length)]; - RandomConnectGroup cellGroup = new RandomConnectGroup(randomY); - cellGroups[x.cellGroups.length] = cellGroup; - - // 随机生成一批CellGroup - for (int i = 0; i < randomCellGroupQty; i++) - cellGroups[i + x.cellGroups.length + 1] = new RandomConnectGroup(Env.FROG_BRAIN_WIDTH, x.randomCellQtyPerGroup, - x.randomInputQtyPerCell, x.randomOutQtyPerCell); - - addInitialOrgans(); - } - - /** create a brand new Egg */ - public static Egg createBrandNewEgg() { // 无中生有,创建一个蛋,先有蛋,后有鸡 - Egg egg = new Egg(); - egg.cellGroups = new RandomConnectGroup[egg.randomCellGroupQty]; - for (int i = 0; i < egg.randomCellGroupQty; i++) - egg.cellGroups[i] = new RandomConnectGroup(Env.FROG_BRAIN_WIDTH, egg.randomCellQtyPerGroup, - egg.randomInputQtyPerCell, egg.randomOutQtyPerCell); - egg.addInitialOrgans(); - return egg; - } - - private static boolean percent(int percent) { - return r.nextInt(100) < percent; - } - - private static float vary(float f) { // 大部分时候不变,有极小机会变异,有极极小机会大变异,有极极极小机会大大大变异 - int i = r.nextInt(100); - if (i < 95) - return f; - float rate = .05f; - if (i > 97) - rate = .1f; - return (float) (f * ((1 - rate) + r.nextFloat() * rate * 2)); - } - - /** Create egg from frog */ - public Egg(Frog frog) { // 青蛙下蛋,蛋的基因生成遵循用进废退、随机变异两个原则 - List gpList = new ArrayList<>(); - for (int i = 0; i < frog.cellGroups.length; i++) { - if (frog.cellGroups[i].fat <= 0) { - if (!frog.cellGroups[i].inherit) - continue;// 从未激活过的神经元,并且就是本轮随机生成的,丢弃之 - if (percent(5)) - continue;// 继承下来的神经元,但是本轮并没用到, 扔掉又可惜,可以小概率丢掉 - } - RandomConnectGroup cellGroup = new RandomConnectGroup(); - RandomConnectGroup oldGp = frog.cellGroups[i]; - cellGroup.groupInputZone = new Zone(vary(oldGp.groupInputZone.x), vary(oldGp.groupInputZone.y), - vary(oldGp.groupInputZone.radius)); - cellGroup.groupOutputZone = new Zone(vary(oldGp.groupOutputZone.x), vary(oldGp.groupOutputZone.y), - vary(oldGp.groupOutputZone.radius)); - cellGroup.cellQty = Math.round(vary(oldGp.cellQty)); - cellGroup.cellInputRadius = vary(oldGp.cellInputRadius); - cellGroup.cellOutputRadius = vary(oldGp.cellOutputRadius); - cellGroup.inputQtyPerCell = Math.round(vary(oldGp.inputQtyPerCell)); - cellGroup.outputQtyPerCell = Math.round(vary(oldGp.outputQtyPerCell)); - cellGroup.inherit = true; - gpList.add(cellGroup); - } - cellGroups = gpList.toArray(new RandomConnectGroup[gpList.size()]); - addInitialOrgans(); - } - - /** Hard code add initial organs */ - public void addInitialOrgans() { // 这是硬编码添加初始器官, 器官的数量、位置、内部参数会参加遗传、变异 + public Egg() {// 无中生有,创建一个蛋,先有蛋,后有鸡 organs.add(new Hungry().setXYRN(300, 100, 100, "Hungry")); organs.add(new MoveUp().setXYRN(800, 100, 60, "Up")); organs.add(new MoveDown().setXYRN(800, 400, 60, "Down")); @@ -150,7 +52,44 @@ public class Egg implements Serializable { organs.add(new MoveRight().setXYRN(900, 250, 60, "Right")); organs.add(new Eat().setXYRN(0, 0, 0, "Eat")); organs.add(new Eye().setXYRN(100, 400, 100, "Eye")); - //organs.add(new Eye().setXYRN(100, 700, 100, "Eye")); + for (int i = 0; i <10; i++) { + organs.add( new RandomConnectGroup().setXYRN(500, 500, 500, null)); + } + + } + + /** Create egg from frog */ + public Egg(Frog frog) { // 青蛙下蛋,每个青蛙的器官会创建自已的副本或变异,可以是0或多个 + for (Organ organ : frog.organs) + for (Organ newOrgan : organ.vary()) + organs.add(newOrgan); + for (int i = 0; i <10; i++) { + organs.add( new RandomConnectGroup().setXYRN(500, 500, 500, null)); + } + } + + /** + * Create a egg by join 2 eggs, x+y=zygote 模拟X、Y 染色体合并,两个蛋生成一个新的蛋, X从Y里借一个器官, + * 不用担心器官会越来越多,因为通过用进废退原则来筛选,没用到的器官会在几代之后被自然淘汰掉 + * 器官不是越多越好,因为器官需要消耗能量,器官数量多了,在生存竞争中也是劣势 + * + */ + public Egg(Egg x, Egg y) { + // x里原来的organ + for (Organ organ : x.organs) + for (Organ newOrgan : organ.vary()) + organs.add(newOrgan); + + // 从y里借一个organ + int yOrganSize = y.organs.size(); + if (yOrganSize > 0) { + Organ o = y.organs.get(RandomUtils.nextInt(yOrganSize)); + if (o.allowBorrow()) + organs.add(o); + } + for (int i = 0; i <10; i++) { + organs.add( new RandomConnectGroup().setXYRN(500, 500, 500, null)); + } } } diff --git a/core/src/main/java/com/github/drinkjava2/frog/egg/EggTool.java b/core/src/main/java/com/github/drinkjava2/frog/egg/EggTool.java index ca6f278..508631b 100644 --- a/core/src/main/java/com/github/drinkjava2/frog/egg/EggTool.java +++ b/core/src/main/java/com/github/drinkjava2/frog/egg/EggTool.java @@ -23,10 +23,11 @@ import java.util.List; import com.github.drinkjava2.frog.Application; import com.github.drinkjava2.frog.Env; import com.github.drinkjava2.frog.Frog; +import com.github.drinkjava2.frog.brain.Organ; import com.github.drinkjava2.frog.util.FrogFileUtils; /** - * EggTool store public static methods of egg + * EggTool save eggs to disk * * @author Yong Zhu * @since 1.0 @@ -34,18 +35,30 @@ import com.github.drinkjava2.frog.util.FrogFileUtils; public class EggTool { /** + * Frogs which have higher energy lay eggs + * * 利用Java串行机制存盘。 能量多(也就是吃的更多,更fat)的Frog下蛋并存盘, 以进行下一伦测试,能量少的Frog被淘汰,没有下蛋的资格。 + * 用能量的多少来简化生存竟争模拟 */ public static void layEggs(Env env) { sortFrogsOrderByEnergyDesc(env); - System.out.print("First frog has " + env.frogs.get(0).cellGroups.length + " cellgroups, energy=" - + env.frogs.get(0).energy); - System.out.print(", Last frog energy=" + env.frogs.get(env.frogs.size() - 1).energy + ", "); + + Frog first = env.frogs.get(0); + Frog last = env.frogs.get(env.frogs.size() - 1); + + System.out.print("First frog has " + first.organs.size() + " organs, energy=" + first.frogEngery); + System.out.print(", Last frog has " + last.organs.size() + " organs, energy=" + last.frogEngery); + if (Env.DEBUG_MODE) + for (int i = 0; i < first.organs.size(); i++) { + Organ org = first.organs.get(i); + System.out.println("Organ(" + i + ")=" + org + ", fat=" + org.fat); + } + try { List newEggs = new ArrayList<>(); for (int i = 0; i < Env.EGG_QTY; i++) newEggs.add(new Egg(env.frogs.get(i))); - System.out.print("EggCellGroups=" + newEggs.get(0).cellGroups.length + ", "); + System.out.print("organs =" + newEggs.get(0).organs.size() + ", "); FileOutputStream fo = new FileOutputStream(Application.CLASSPATH + "eggs.ser"); ObjectOutputStream so = new ObjectOutputStream(fo); @@ -53,20 +66,19 @@ public class EggTool { so.close(); env.eggs = newEggs; - System.out - .println("Saved " + env.eggs.size() + " eggs to file '" + Application.CLASSPATH + "eggs.ser" + "'"); + System.out.println( + ", Saved " + env.eggs.size() + " eggs to file '" + Application.CLASSPATH + "eggs.ser" + "'"); } catch (IOException e) { System.out.println(e); } } - private static void sortFrogsOrderByEnergyDesc(Env env) { + private static void sortFrogsOrderByEnergyDesc(Env env) {// 按能量多少给青蛙排序 Collections.sort(env.frogs, new Comparator() { - public int compare(Frog a, Frog b) { - if (a.energy > b.energy) + if (a.frogEngery > b.frogEngery) return -1; - else if (a.energy == b.energy) + else if (a.frogEngery == b.frogEngery) return 0; else return 1; @@ -75,7 +87,6 @@ public class EggTool { } public static void deleteEggs() { - FrogFileUtils.deleteFile(Application.CLASSPATH + "eggs.json"); FrogFileUtils.deleteFile(Application.CLASSPATH + "eggs.ser"); } @@ -100,7 +111,7 @@ public class EggTool { + " new eggs to do test."); env.eggs = new ArrayList(); for (int i = 0; i < Env.EGG_QTY; i++) - env.eggs.add(Egg.createBrandNewEgg()); + env.eggs.add(new Egg()); } } diff --git a/core/src/main/java/com/github/drinkjava2/frog/util/RandomUtils.java b/core/src/main/java/com/github/drinkjava2/frog/util/RandomUtils.java new file mode 100644 index 0000000..6249053 --- /dev/null +++ b/core/src/main/java/com/github/drinkjava2/frog/util/RandomUtils.java @@ -0,0 +1,53 @@ +/* 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.github.drinkjava2.frog.util; + +import java.util.Random; + +import com.github.drinkjava2.frog.brain.Zone; + +/** + * Random Utilities used in this project + * + * @author Yong Zhu + * @since 1.0 + */ +public class RandomUtils { + private static final Random rand = new Random(); + + public static int nextInt(int i) { + return rand.nextInt(i); + } + + public static float nextFloat() { + return rand.nextFloat(); + } + + public static Zone randomPosInZone(Zone z) { + return new Zone(z.x - z.r + z.r * 2 * rand.nextFloat(), z.y - z.r + z.r * 2 * rand.nextFloat(), + z.r * rand.nextFloat() * .02f); + } + + public static boolean percent(int percent) { + return rand.nextInt(100) < percent; + } + + public static float vary(float f) { // 大部分时候不变,有极小机会变异,有极极小机会大变异,有极极极小机会大大大变异 + int i = rand.nextInt(100); + if (i < 95) + return f; + float rate = .05f; + if (i > 97) + rate = .1f; + return (float) (f * ((1 - rate) + rand.nextFloat() * rate * 2)); + } + +} diff --git a/eggs.ser b/eggs.ser new file mode 100644 index 0000000..29b6de0 Binary files /dev/null and b/eggs.ser differ