Done draw frog3D, delete unused Gene method

This commit is contained in:
Yong 2021-10-13 14:46:51 -06:00
parent 0868c262a1
commit 237ccc059d
16 changed files with 441 additions and 181 deletions

View File

@ -143,11 +143,18 @@ Frog: 这是人工生命的主体,目前起名叫青蛙(Frog),其实叫什
位于history\005a1目录下演示用5个声母和5个韵母的组合来关联到25个图像的识别这样可以减少声音输入区的数量。它的另一个目的是演示体全息存贮的工作模式可以同时处理多个方向的信号。这个演示分辨率极差只有约一半的识别率但我不打算继续改进了。
![result12](result12_letter_test2.png)
2021-10-13 失败的细胞分裂尝试
这次本来想模仿生物细胞的分裂从一个细胞开始分裂出任意指定的三维形状并设计了split、goto等基因命令但是做来做去做不出结果细胞们就象跳蚤一样乱跑不听使唤最终还是决定放弃细胞分裂这个算法太难了。细胞分裂的优点是更“象”生物而且估计可以利用分形原理缩小基因的长度基因相当于一种自带循环和条件判断的计算机语言。
最终生成三维形状这个目标还是借助简单遗传算法完成,通过细胞在相邻位置随机生成,并在基因里记录每个细胞的坐标的方式来实现,基因命令被删得只剩一个了,就是随机生成细胞。
用遗传算法来生成任意形状,就好象一个画家在画画,但是画什么根本不知道,只知道听从旁边人打分,画的好就打高分,画的不好就打低分,这样一直循环下去,最终画的内容只由打分的人决定。目前速度上还有改进余地,比如让新细胞有更多变异率。
![result13](result13_frog3d.gif)
生成三维形状目的是为生成三维脑结构做准备,神经网络空间上应该是三维的,这样实现模式识别会很方便,而早期随机连线结构损失了空间位置信息,三维全息演示问题则是它是手工设计的,优化困难。
## 运行方式 | Run
运行history各个子目录下的run.bat批处理文件即可启动运行history下有多个子目录按时间和版本号顺序按列存放着这个项目演化过程中的主要历史版本供演示。
另外如果想要研究这个项目的早期版本可以结合gitk命令和参考"版本提交记录.md"的介绍用git reset命令回复到以前任一个版本例如用:
git reset --hard ae34b07e 可以转回到以前一个分组测试的找食版本。
git reset --hard ae34b07e 可以转回到以前一个分组测试的找食版本。 码云上通常是大版本提交跑出结果才会更新github上则是日常提交。
## 重要参数 | Parameters
在Env.java类中以下有以下可调整参数请手工修改这些参数进行不同的测试前5个参数很重要:
@ -171,5 +178,6 @@ FOOD_QTY食物的数量食物越多则Frog的生存率就越高
欢迎发issue、评论等方式提出建议或加入开发组。
## 关注我 | About Me
[Gitee](https://gitee.com/drinkjava2)
[Github](https://github.com/drinkjava2)
微信:yong99819981(如想长期关注本项目、或参与项目实际编码开发,请加我并留言"人工生命群")
微信:yong99819981(如想长期关注本项目、或参与开发,请加我并留言"人工生命群")

View File

@ -133,6 +133,13 @@ The basic principle of these two modes is based on the back propagation of signa
Located in the history\005a1 directory, the demonstration uses a combination of 5 initials and 5 finals to associate with the recognition of 25 images, which can reduce the number of voice input areas. Its other purpose is to demonstrate that the working mode of volume holographic storage can process signals in multiple directions at the same time. The resolution of this demo is very poor, only about half of the recognition rate, but I do not intend to continue to improve.
![result12](result12_letter_test2.png)
2021-10-13 Failed cell division attempts
This time I wanted to imitate the division of biological cells, starting from a cell to divide into any specified 3D shape, and designing split, goto and other genetic commands, but doing it and doing it cant produce results, and the cells run around like fleas. I didn't listen to my orders, and finally decided to give up. The algorithm of cell division is too difficult. The advantage of cell division is that it is more "like" organisms, and it is estimated that the length of genes can be reduced by the principle of fractal. Genes are equivalent to a computer language with loops and conditional judgments.
Finally, the goal of generating three-dimensional shapes is accomplished with the help of simple genetic algorithms. The cells are randomly generated in adjacent positions and the coordinates of each cell are recorded in the gene. Only one gene command is deleted, which is random generate cell.
Using genetic algorithms to generate arbitrary shapes is like a painter who is drawing, but he doesnt know what he is painting. He just listens to the scores of the people next to him. If he paints well, he scores a high score, and if he paints badly, he scores a low score. The loop continues, and the final content of the painting is only determined by the person who scores it. There is still room for improvement in speed, such as allowing new cells to have more mutation rates.
![result13](result13_frog3d.gif)
The purpose of generating three-dimensional shapes is to prepare for the generation of three-dimensional brain structures. The neural network space should be three-dimensional, so that it will be very convenient to realize pattern recognition. However, the early random connection structure lost the spatial position information. The problem of the three-dimensional holographic demonstration is that it is Hand-designed, optimization is difficult.
## License
[Apache 2.0] (http://www.apache.org/licenses/LICENSE-2.0)

View File

@ -23,6 +23,7 @@ import com.gitee.drinkjava2.frog.brain.Cell;
import com.gitee.drinkjava2.frog.brain.Cells3D;
import com.gitee.drinkjava2.frog.egg.Egg;
import com.gitee.drinkjava2.frog.gene.Gene;
import com.gitee.drinkjava2.frog.judge.BrainShapeJudge;
import com.gitee.drinkjava2.frog.objects.Material;
import com.gitee.drinkjava2.frog.util.RandomUtils;
@ -38,7 +39,7 @@ import com.gitee.drinkjava2.frog.util.RandomUtils;
public abstract class Animal {// 这个程序大量用到public变量而不是getter/setter主要是为了编程方便和简洁但缺点是编程者需要小心维护各个变量
public static BufferedImage FROG_IMAGE;
public static BufferedImage snakeImage;
transient public ArrayList<String> gene = new ArrayList<>(); // Animal的基因只保存一份这是人工生命与实际生物每个细胞都保留一份基因的最大不同
transient public ArrayList<Long> gene = new ArrayList<>(); // Animal的基因只保存一份这是人工生命与实际生物每个细胞都保留一份基因的最大不同
static {
try {
@ -49,9 +50,9 @@ public abstract class Animal {// 这个程序大量用到public变量而不是ge
}
/** brain cells */
public List<Cell> cells = new ArrayList<>();
public Cells3D cells3D = new Cells3D();
public List<Cell> cells = new ArrayList<>();
public Cells3D cells3D = new Cells3D(this);
public int x; // animal在Env中的x坐标
public int y; // animal在Env中的y坐标
@ -65,7 +66,6 @@ public abstract class Animal {// 这个程序大量用到public变量而不是ge
public Animal(Egg egg) {// x, y 是虑拟环境的坐标
this.gene.addAll(egg.gene); //动物的基因是蛋的基因的拷贝
Gene.mutation(this); //但是有小概率基因突变
if (Env.BORN_AT_RANDOM_PLACE) { //是否随机出生在地图上?
x = RandomUtils.nextInt(Env.ENV_WIDTH);
y = RandomUtils.nextInt(Env.ENV_HEIGHT);
@ -86,64 +86,55 @@ public abstract class Animal {// 这个程序大量用到public变量而不是ge
private static final int MIN_ENERGY_LIMIT = Integer.MIN_VALUE + 20000;
private static final int MAX_ENERGY_LIMIT = Integer.MAX_VALUE - 20000;
public void bigAward() {//energy大小是环境对animal唯一的奖罚也是animal唯一的下蛋竟争标准调用下面6个方法来进行不同程度的奖罚
energy += 5000;
if (energy > MAX_ENERGY_LIMIT)
energy = MAX_ENERGY_LIMIT;
}
//energy大小是环境对animal唯一的奖罚也是animal唯一的下蛋竟争标准调用下面6个方法来进行不同程度的奖罚
public void normalAward() {
energy += 50;
if (energy > MAX_ENERGY_LIMIT)
energy = MAX_ENERGY_LIMIT;
}
//@formatter:off 下面几行是重要的奖罚方法会经常调整或注释掉集中放在一起不要格式化为多行
// public void bigAward() { energy += 5000; if (energy > MAX_ENERGY_LIMIT)energy = MAX_ENERGY_LIMIT; }
public void normalAward() { energy += 50; if (energy > MAX_ENERGY_LIMIT)energy = MAX_ENERGY_LIMIT; }
//public void tinyAward() { energy += 1; if (energy > MAX_ENERGY_LIMIT)energy = MAX_ENERGY_LIMIT; }
//public void bigPenalty() { energy -= 5000; if (energy < MIN_ENERGY_LIMIT)energy = MIN_ENERGY_LIMIT; }
public void normalPenalty() { energy -= 100; if (energy < MIN_ENERGY_LIMIT)energy = MIN_ENERGY_LIMIT; }
//public void tinyPenalty() { energy -= 1 ; if (energy < MIN_ENERGY_LIMIT)energy = MIN_ENERGY_LIMIT; }
public void kill() {this.alive = false; this.energy = MIN_ENERGY_LIMIT; Env.clearMaterial(x, y, animalMaterial); } //kill是最大的惩罚
//@formatter:on
public void tinyAward() {
energy++;
if (energy > MAX_ENERGY_LIMIT)
energy = MAX_ENERGY_LIMIT;
}
// public void initAnimal() { // 初始化animal,生成脑细胞是在这一步
// new Cell(this, Env.BRAIN_XSIZE / 2, Env.BRAIN_YSIZE / 2, Env.BRAIN_ZSIZE / 2, 0, 0, 10);//第一个细胞生成于脑的中心它的基因指针指向起始0行
// int oldCellsQTY, newCellsQTY, start = 0, end = 0;
// do {
// oldCellsQTY = this.cells.size();
// for (end = start; end < gene.size()-1; end++) {
// if(Gene.toCode(gene.get(end))==Gene.NEW_CELL)break;
// }
//
// for (int i = 0; i < cells.size(); i++) {//重要开始对每一个细胞调用基因这门语言启动细胞的分裂,这个分裂是在一个时间周期内完成以后要改进为利用图形卡的加速功能并发执行以加快分裂速度
// Gene.run(this, cells.get(i));
// }
// start=end;
// newCellsQTY = this.cells.size();
// //Application.brainPic.drawBrainPicture(); //慢动作放映生成每个细胞过程
// } while (oldCellsQTY != newCellsQTY && newCellsQTY < Env.CELLS_MAX_QTY && start<cells.size()); //直到所有细胞都停止分裂或细胞分裂超过CELLS_LIMIT个才停止
// BrainShapeJudge.judge(this); //重要对细胞的形状是否符合模子的形状进行能量奖励或扣分
// this.energy -= gene.size() / 2; //基因长的如果和基因短的同样形状要扣分
// if (newCellsQTY > Env.CELLS_MAX_QTY) //如果细胞分裂到达极限值CELLS_LIMIT才停止说明很可能有无限循环分裂的癌细胞存在这个生物应扣分淘汰掉
// this.energy = MIN_ENERGY_LIMIT;
// Gene.mutation(this); //有小概率基因突变
// }
public void bigPenalty() {
energy -= 5000;
if (energy < MIN_ENERGY_LIMIT)
energy = MIN_ENERGY_LIMIT;
}
public void normalPenalty() {
energy -= 50;
if (energy < MIN_ENERGY_LIMIT)
energy = MIN_ENERGY_LIMIT;
}
public void tinyPenalty() {
energy--;
if (energy < MIN_ENERGY_LIMIT)
energy = MIN_ENERGY_LIMIT;
}
public void initAnimal() { // 初始化animal,生成脑细胞是在这一步
Cell cell = new Cell(this, Env.BRAIN_XSIZE / 2, Env.BRAIN_YSIZE / 2, Env.BRAIN_ZSIZE / 2, 0, 0, 0); //第一个细胞生成于脑的中心它的基因语言指针指向起始0行位置
this.cells.add(cell);
int oldCellsQTY;
int newCellsQTY;
do {
oldCellsQTY = this.cells.size();
Gene.run(this, cell); //重要开始调用基因这门语言启动细胞的分裂,这个分裂是在一个时间周期内完成以后要改进为利用图形卡的加速功能并发执行以加快分裂速度
newCellsQTY = this.cells.size();
} while (oldCellsQTY != newCellsQTY && newCellsQTY < Env.CELLS_MAX_QTY); //直到所有细胞都停止分裂或细胞分裂超过CELLS_LIMIT个才停止
if (newCellsQTY > Env.CELLS_MAX_QTY) //如果细胞分裂到达极限值CELLS_LIMIT才停止说明很可能有无限循环分裂的癌细胞存在这个生物应扣分淘汰掉
this.energy = MIN_ENERGY_LIMIT;
public void initAnimal() { // 初始化animal,生成脑细胞是在这一步
Gene.run(this); //运行基因语言生成细胞
BrainShapeJudge.judge(this); //重要对细胞的形状是否符合模子的形状进行能量奖励或扣分
Gene.mutation(this); //有小概率基因突变
}
public boolean active() {// 这个active方法在每一步循环都会被调用是脑思考的最小帧
// 如果能量小于0出界与非食物的点重合则判死
if (!alive) {
energy =MIN_ENERGY_LIMIT; // 死掉的青蛙也要消耗能量确保淘汰出局
energy = MIN_ENERGY_LIMIT; // 死掉的青蛙确保淘汰出局
return false;
}
if (energy < 0 || Env.outsideEnv(x, y) || Env.bricks[x][y] >= Material.KILL_ANIMAL) {
energy = MIN_ENERGY_LIMIT;
kill();
return false;
}
@ -160,14 +151,15 @@ public abstract class Animal {// 这个程序大量用到public变量而不是ge
g.drawImage(animalImage, x - 8, y - 8, 16, 16, null);// 减去坐标保证嘴巴显示在当前x,y处
}
public void kill() {// 杀死当前动物
this.alive = false;
Env.clearMaterial(x, y, animalMaterial);
}
/** 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 Cell getOneRandomCell() {
if (cells.isEmpty())
return null;
return cells.get(RandomUtils.nextInt(cells.size()));
}
}

View File

@ -110,7 +110,6 @@ public class Application {
mainFrame.add(label);
mainFrame.setVisible(true);
mainFrame.setTitle("这是一个空框架用随机生成基因的方式来实现细胞3D分裂还没开始做");
env.run();
}

View File

@ -9,9 +9,9 @@ import java.util.List;
import javax.swing.JPanel;
import com.gitee.drinkjava2.frog.brain.Cells3D;
import com.gitee.drinkjava2.frog.egg.Egg;
import com.gitee.drinkjava2.frog.egg.FrogEggTool;
import com.gitee.drinkjava2.frog.gene.Gene;
import com.gitee.drinkjava2.frog.objects.EnvObject;
import com.gitee.drinkjava2.frog.objects.Food;
import com.gitee.drinkjava2.frog.objects.Material;
@ -37,16 +37,16 @@ public class Env extends JPanel {
public static final int FROG_PER_EGG = 4; // 每个青蛙蛋可以孵出几个青蛙
public static final boolean BORN_AT_RANDOM_PLACE = true;// 孵出动物落在地图上随机位置而不是在蛋所在地
public static final int SCREEN = 1; // 分几屏测完
public static final boolean BORN_AT_RANDOM_PLACE = true;// 孵出动物落在地图上随机位置而不是在蛋所在地
/** Frog's brain size */ // 脑细胞位于脑范围内是个三维结构在animal中用一个List<Cell>来存贮表示的同时也用一个Cell3D动态数组来表示
public static final int BRAIN_XSIZE = 20; // 脑在X方向长度
public static final int BRAIN_YSIZE = 20; // 脑在Y方向长度
public static final int BRAIN_ZSIZE = 20; // 脑在Z方向长度
public static final int BRAIN_XSIZE = 100; // 脑在X方向长度取值最大为1000
public static final int BRAIN_YSIZE = 100; // 脑在Y方向长度取值最大为1000
public static final int BRAIN_ZSIZE = 100; // 脑在Z方向长度取值最大为1000
public static final int CELLS_MAX_QTY = 100; //脑细胞总数不能超过这个值
public static final int CELLS_MAX_QTY = 4000; //脑细胞总数不能超过这个值
/** SHOW first animal's brain structure */
public static boolean SHOW_FIRST_ANIMAL_BRAIN = true; // 是否显示脑图在Env区的右侧
@ -137,21 +137,6 @@ public class Env extends JPanel {
return false;
}
public static boolean foundAndAteFrog(int x, int y) {// 如果x,y有青蛙将其杀死返回true
if (x < 0 || y < 0 || x >= ENV_WIDTH || y >= ENV_HEIGHT)
return false;// 如果出界返回false;
int frogNo = Env.bricks[x][y] & Material.FROG_TAG;
if (frogNo > 0) {
Frog f = frogs.get(frogNo - 1);
if (f.alive) {
Env.frog_ated++;
f.kill();
return true;
}
}
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;
@ -307,6 +292,11 @@ public class Env extends JPanel {
checkIfPause();
for (int j = 0; j < FROG_PER_SCREEN; j++) {
Frog f = frogs.get(current_screen * FROG_PER_SCREEN + j);
if (j == 0) {
System.out.println("======== cells: "+f.cells.size()+" =========");
//Gene.printGene(f);
}
f.cells=null; // 清空frog脑细胞所占用的内存
f.cells3D=null;
}

View File

@ -19,6 +19,7 @@ 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.judge.BrainShapeJudge;
import com.gitee.drinkjava2.frog.util.ColorUtils;
/**
@ -244,6 +245,36 @@ public class BrainPicture extends JPanel {
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) {
drawText(px1, py1, pz1, text, 12);
}
@ -301,8 +332,14 @@ public class BrainPicture extends JPanel {
drawLine(0, 0, 0, 0, 1, 0);
drawLine(0, 0, 0, 0, 0, 1);
for (Cell cell : a.cells) {
setPicColor(ColorUtils.grayColor(cell.energy));// 用灰度表示活跃度
setPicColor(Color.gray);
BrainShapeJudge.show(this);//这行显示脑形状这个模子
for (Cell cell : a.cells) { //这里开始画出细胞
if (cell.color != null)
setPicColor(cell.color);
else
setPicColor(ColorUtils.grayColor(cell.energy));// 用灰度级表示细胞能量大小
drawCell(cell);
}

View File

@ -10,6 +10,8 @@
*/
package com.gitee.drinkjava2.frog.brain;
import java.awt.Color;
import com.gitee.drinkjava2.frog.Animal;
/**
@ -29,7 +31,8 @@ import com.gitee.drinkjava2.frog.Animal;
public class Cell { //cell数量非常庞大不需要序列化
public int x; //x,y,z 是细胞的中心点在脑中的位置
public int y;
public int z;
public int z;
public Color color; //这个颜色只是用于调试
public int geneIndex; //指向青蛙基因单例中的行号每个细胞的基因都相同但是不同的是在基因链中的行号
@ -47,9 +50,15 @@ public class Cell { //cell数量非常庞大不需要序列化
this.geneIndex = geneIndex;
this.splitCount = splitCount;
this.splitLimit = splitLimit;
animal.cells.add(this);
animal.cells3D.putCell(this, animal.cells.size()); //在cell3D中登记cell序号
animal.normalAward(); //TODO: 调试用待删
if (!Animal.outBrainRange(x, y, z)) {
Cell c= animal.cells3D.getCell(x, y, z);
if(c!=null)
animal.normalPenalty();
else {
animal.cells.add(this);
animal.cells3D.putCell(this, animal.cells.size()); //在cell3D中登记cell序号
}
}
}
public void split(Animal animal, int direction) {//细胞在一个或多个方向上分裂克隆有6对应立方体的6个面)7(包含本身点)27(包含立方体侧边项点方向)等选项这里先采用6个方向的方案
@ -59,23 +68,23 @@ public class Cell { //cell数量非常庞大不需要序列化
if ((direction & 1) > 0) {//
zz++;
clone(animal, xx, yy, zz); //简单在指定隔壁位置克隆暂不采用推开其它细胞的高运算量方案这个要等图型卡加速用上后再考虑推开其它细胞
}
} else
if ((direction & 0b10) > 0) {//
zz--;
clone(animal, xx, yy, zz);
}
} else
if ((direction & 0b100) > 0) {//
xx--;
clone(animal, xx, yy, zz);
}
} else
if ((direction & 0b1000) > 0) {//
xx++;
clone(animal, xx, yy, zz);
}
} else
if ((direction & 0b10000) > 0) {//
yy--;
clone(animal, xx, yy, zz);
}
} else
if ((direction & 0b100000) > 0) {//
yy++;
clone(animal, xx, yy, zz);
@ -85,7 +94,12 @@ public class Cell { //cell数量非常庞大不需要序列化
public void clone(Animal animal, int xx, int yy, int zz) {//在指定坐标克隆当前细胞
if (Animal.outBrainRange(xx, yy, zz))
return;
new Cell(animal, xx, yy, zz, geneIndex, splitCount, splitLimit);
if (animal.cells3D.existCell(xx, yy, zz)) {
// animal.tinyPenalty();
} else {
if(splitCount<splitLimit)
new Cell(animal, xx, yy, zz, geneIndex, splitCount, splitLimit);
}
}
public void act() {

View File

@ -22,20 +22,22 @@ import com.gitee.drinkjava2.frog.Env;
* @since 1.0
*/
public class Cells3D {
private Animal animal;
public int[][][] cells = new int[Env.BRAIN_XSIZE][][]; // 为了节约内存先只初始化三维数组的x维另两维用到时再分配
public Cells3D() {
public Cells3D(Animal animal) {
this.animal=animal;
}
/** check if cell exist at position (x,y,z) */
public boolean ifExistCell(Animal animal, int x, int y, int z) {// 返回指定脑坐标的cell 如果不存在返回null
public boolean existCell(int x, int y, int z) {// 返回指定脑坐标的cell 如果不存在返回null
if (cells[x] == null || cells[x][y] == null)
return false;
return cells[x][y][z]>0; //arrayIndex为0时是空为1时表示animal.cells[0];
}
/** Get a cell at position (x,y,z), if not exist, return null */
public Cell getCell(Animal animal, int x, int y, int z) {// 返回指定脑坐标的cell 如果不存在返回null
public Cell getCell(int x, int y, int z) {// 返回指定脑坐标的cell 如果不存在返回null
if (cells[x] == null || cells[x][y] == null)
return null;
int arrayIndex=cells[x][y][z]; //arrayIndex为0时是空为1时表示animal.cells[0];

View File

@ -35,7 +35,7 @@ public class Egg implements Serializable {
// gene is a language similar like BASIC created by random
// 基因是随机生成的一种类似Basic语言的字符串符列保存在蛋中和实际生物每个细胞都要保存一份基因不同程序中每个细胞仅保存着基因的指针和当前细胞位于基因链中的行号并不需要保存基因的副本这样可以极大地减少内存占用
public ArrayList<String> gene =new ArrayList<>();
public ArrayList<Long> gene =new ArrayList<>();
public Egg() {// 无中生有创建一个蛋先有蛋后有蛙d
x = RandomUtils.nextInt(Env.ENV_WIDTH);

View File

@ -10,9 +10,11 @@
*/
package com.gitee.drinkjava2.frog.gene;
import java.awt.Color;
import java.util.List;
import com.gitee.drinkjava2.frog.Animal;
import com.gitee.drinkjava2.frog.Env;
import com.gitee.drinkjava2.frog.brain.Cell;
import com.gitee.drinkjava2.frog.util.RandomUtils;
@ -22,127 +24,140 @@ import com.gitee.drinkjava2.frog.util.RandomUtils;
* Gene是一个仿造大自然随机生成的语言它采用类似BASIC的语法,只有少数几个字键字这个类里定义语言的关键字常量和对这些关键字的解析行为,
* 这个语言主要的作用是实现分裂造形包括 身体结构造形不重要但先实现这个)和脑细胞结构造形(重点)
*
* Gene语法的每行由若干部分组成第一部分是关键字占2格第二四等部分是可选内容由关键字决定:
* Gene语法的每行由一个long编码表示高32位是关键字低32位是参数由关键字决定:
*
* 10100 表示跳转到100行语句执行, 即10(=GOTO) + 100(行号)
* 110 表示结束执行停止细胞分裂, 即11(结束) + 0(参数)
* 10 + 100 表示跳转到100行语句执行, 即10(=GOTO) + 100(行号)
* 11 + 0 表示结束执行停止细胞分裂, 即11(结束) + 0(参数)
*
* @author Yong Zhu
* @since 2021-09-16
*/
public class Gene {// NOSONAR
private static int index = 9; //关键字是一个两位数字字符从10开始依次往下排关键字没有可读性调试时要用printGene方法将关键字转为可读的语句
public static final int FIRST_KEYWORD = 10;
public static final int GOTO = nextKeyword(); //GOTO关键字=10
public static final int END = nextKeyword(); //结束执行=11
public static final int SPLIT = nextKeyword(); //执行细胞分裂 分裂方向由第二部分的数值决定一个细胞有可能同时在多个方向分裂出多个细胞有6个或27个方向等
public static final int SPLIT_LIMIT = nextKeyword(); //细胞分裂寿命, 0表示可以无限分裂
public static final int IF = nextKeyword(); //IF关键字暂没用到
private static int index = 9; //关键字是一个两位数字字符从10开始依次往下排关键字没有可读性调试时要用printGene方法将关键字转为可读的语句
public static String[] TEXT = new String[20]; //这里存放关键字的文字解释供打印输出用
//下面这些基因关键字是失败的尝试从2021-10-13这个提交开始不再使用
// public static final int GOTO = nextKeyword("GOTO"); //GOTO关键字=10
// public static final int END = nextKeyword("END"); //结束执行=11
// public static final int SPLIT = nextKeyword("SPLIT"); //执行细胞分裂 分裂方向由第二部分的数值决定一个细胞有可能同时在多个方向分裂出多个细胞有6个或27个方向等
// public static final int SPLIT_LIMIT = nextKeyword("SPLIT_LIMIT"); //细胞分裂寿命, 0表示可以无限分裂
public static final int NEW_CELL = nextKeyword("NEW_CELL"); //在新位置新创建一个细胞而不是由其它细胞分裂出来
public static final int FIRST_KEYWORD = NEW_CELL;
public static final int LAST_KEYWORD = index; //最后一个关键字
public static String[] TEXT = new String[LAST_KEYWORD + 1]; //这里存放关键字的文字解释供打印输出用
static {
TEXT[GOTO] = "GOTO";
TEXT[END] = "END";
TEXT[SPLIT] = "SPLIT";
TEXT[SPLIT_LIMIT] = "SPLIT_LIMIT";
TEXT[IF] = "IF";
static private int nextKeyword(String explain) {
index++;
TEXT[index] = explain;
return index;
}
static private int nextKeyword() {
return ++index;
static final StringBuilder sb = new StringBuilder(); //用来将方向转为可读的英文缩写
public static int toCode(Long gene) {
return (int) (gene >> 32);
}
public static int toParam(Long gene) {
return (int) (gene & 0xffffffffL);
}
public static long toGene(int code, int param) {
long code_ = code;
return (code_ << 32) + param;
}
public static void printGene(Animal animal) {
int i = 0;
for (String s : animal.gene) {
int code = Integer.parseInt(s.substring(0, 2));
String paramStr = s.substring(2);
for (long gene : animal.gene) {
int code = toCode(gene);
int param = toParam(gene);
String paramStr = "" + param;
System.out.println(i++ + " " + TEXT[code] + " " + paramStr);
}
}
//execute gene language
public static void run(Animal animal, Cell cell) { //对于给定的细胞由基因这个细胞所处的行号细胞的分裂寿命细胞已分裂的次数以及细胞所处的身体坐标以及细胞周围是否有细胞包围来决定它的下一步分裂行为
if (cell.geneIndex < 0 || cell.geneIndex >= animal.gene.size())
return;
String oneLine = animal.gene.get(cell.geneIndex);
int code = Integer.parseInt(oneLine.substring(0, 2));
if (code == END) {//如果是END, 结束分裂参数就不需要解读了
cell.geneIndex = -1;//改为-1,以后直接跳过这个细胞不再执行上面的Integer.parseInt
return;
}
int param; //每行基因分为代码和参数两个部分参数暂定为一个整数
try {
param = Integer.parseInt(oneLine.substring(2));
} catch (NumberFormatException e) { //除了END等关键字外下面的code都需要参数, 如果参数不是整数扣除青蛙能量
animal.bigPenalty();
return;
}
if (code == GOTO) {
if (param < 0 || param >= animal.gene.size()) {//行号太大太小都不行
animal.bigPenalty();
return;
public static void run(Animal animal) { //对于给定的细胞由基因这个细胞所处的行号细胞的分裂寿命细胞已分裂的次数以及细胞所处的身体坐标以及细胞周围是否有细胞包围来决定它的下一步分裂行为
for (int i = 0; i < animal.gene.size(); i++) {
long gene = animal.gene.get(i);
int code = toCode(gene);
int param = toParam(gene);
if (code == NEW_CELL) { //新建一个细胞
int x = param / 1000000;
int y = (param - x * 1000000) / 1000;
int z = param % 1000;
Cell c = new Cell(animal, x, y, z, i, 0, 10);
c.color = Color.RED;
}
cell.geneIndex = param;
} else if (code == SPLIT_LIMIT) {//重定义细胞寿命
cell.splitLimit = param;
cell.geneIndex++;
} else if (code == SPLIT) { //执行细胞分裂
cell.geneIndex++;
if (param < 0 || param > 63) //如果是分裂的话param应该随机生成落在0~63之内每个二进制的一个位代表1个分裂方向共有上下左右前后6个方向
return;
cell.split(animal, param);//cell在参数代表的方向进行分裂克隆可以同时在多个方向克隆出多个细胞
}
}
private static int randomParam(Animal animal, int code) {//根据基因code生成一个随机合理参数
private static Long randomGeneCode(Animal animal) {//生成一个随机的基因行
long code = RandomUtils.nextInt(LAST_KEYWORD - FIRST_KEYWORD) + FIRST_KEYWORD;
int param = randomGeneParam(animal, code);
return (code << 32) + param;
}
public static int randomGeneParam(Animal animal, long code) {//根据基因code生成一个随机合理参数
int param = 0;
if (code == GOTO) {
param = RandomUtils.nextInt(animal.gene.size());
} else if (code == SPLIT_LIMIT) {//细胞寿命
param = RandomUtils.nextInt(60); // 细胞寿命60这个参数以后要写在基因里
} else if (code == SPLIT) { //细胞分裂
param = RandomUtils.nextInt(64);
if (code == NEW_CELL) { //新的细胞坐标位置参数用一个整数表示, 值为 X*1000000 + Y*1000 + Z
if (animal.cells.isEmpty()) { //如果animal没有细胞位置取脑的中间
return Env.BRAIN_XSIZE / 2 * 1000000 + Env.BRAIN_YSIZE / 2 * 1000 + Env.BRAIN_ZSIZE / 2;
} else { //如果脑细胞已有了随机取一个脑细胞返回它的一个相邻坐标位置
Cell c = animal.getOneRandomCell();
int x = c.x;
int y = c.y;
int z = c.z;
x += RandomUtils.nextInt(5) - 2;
y += RandomUtils.nextInt(5) - 2;
z += RandomUtils.nextInt(5) - 2;
return x * 1000000 + y * 1000 + z;
}
}
return param;
}
private static String randomGene(Animal animal) {//生成一个随机的基因行
int code = RandomUtils.nextInt(LAST_KEYWORD + 1 - FIRST_KEYWORD) + FIRST_KEYWORD;
return "" + code + randomParam(animal, code);
}
public static void mutation(Animal animal) {//基因随机突变分为新增删除拷贝改变参数改变等情况
List<Long> genes = animal.gene;
public static void mutation(Animal animal) {//基因随机突变分为新增删除拷贝改变参数改变等情况
List<String> genes = animal.gene;
if (RandomUtils.percent(10)) {
genes.add(toGene(NEW_CELL, randomGeneParam(animal, NEW_CELL)));
return;
}
if (RandomUtils.percent(5)) //新增5这个魔数以后要写在基因里,成为基因的一部分下同
genes.add(RandomUtils.nextInt(genes.size()), randomGene(animal));
float percent = 5; //percent这个魔数以后要写在基因里,成为基因的一部分
if (RandomUtils.percent(percent * 3))
genes.add(RandomUtils.nextInt(genes.size()), randomGeneCode(animal));
if (genes.size() > 0 && RandomUtils.percent(3)) //删除
if (genes.size() > 0 && RandomUtils.percent(percent)) //删除
genes.remove(RandomUtils.nextInt(genes.size()));
if (genes.size() > 0 && RandomUtils.percent(3)) //改变
genes.set(RandomUtils.nextInt(genes.size()), randomGene(animal));
if (genes.size() > 0 && RandomUtils.percent(percent)) //改变
genes.set(RandomUtils.nextInt(genes.size()), randomGeneCode(animal));
if (genes.size() > 0 && RandomUtils.percent(5)) { //改变参数
if (genes.size() > 0 && RandomUtils.percent(percent)) { //改变参数
int index = RandomUtils.nextInt(genes.size());
String gene = genes.get(index);
int code = Integer.parseInt(gene.substring(0, 2));
int param = randomParam(animal, code);
genes.set(index, "" + code + param);
long gene = genes.get(index);
long code = toCode(gene);
int param = randomGeneParam(animal, code); //参数是与code相关的不同的code其合理参数范围是不一样的
genes.set(index, (code << 32) + param);
}
if (genes.size() > 0 && RandomUtils.percent(1)) { //批量拷贝,一次拷贝不超过基因长度的1/3
genes.addAll(RandomUtils.nextInt(genes.size()), genes.subList(0, RandomUtils.nextInt(genes.size() / 3)));
if (genes.size() > 0 && RandomUtils.percent(percent)) { //批量拷贝,一次拷贝不超过基因长度的1/2
genes.addAll(RandomUtils.nextInt(genes.size()), genes.subList(0, RandomUtils.nextInt(genes.size() / 2)));
}
if (genes.size() > 0 && RandomUtils.percent(percent)) { //批量删除
int start = RandomUtils.nextInt(genes.size());
int end = RandomUtils.nextInt(genes.size());
if (start > end) {
int tmp = start;
start = end;
end = tmp;
}
genes.subList(start, end).clear();
}
// if (genes.size() > 0 && RandomUtils.percent(1)) { //批量删除一次删除不超过基因长度的1/5
// genes.subList(0, RandomUtils.nextInt(genes.size() / 5)).clear();
// }
}
}

View File

@ -0,0 +1,71 @@
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.Cell;
import com.gitee.drinkjava2.frog.util.Point3D;
import com.gitee.drinkjava2.frog.util.StringPixelUtils;
/**
* execute method be called after animal's initAnimal method
*
* 这个类的judge方法在动物的初始化后被调用根据脑细胞群的三维结构形状来对动物进行奖罚即加减它的能量值这是一个临时类只是用来检验细胞三维成形功能以后可能改名或删除
* 这个类的show方法在绘脑图时调用在脑图里显示脑细胞群的三维形状用空心圆来表示这个三维形状就像是一个模子细胞长在这个模子里的有奖否则扣分
*/
public class BrainShapeJudge {//NOSONAR
private static Point3D C = new Point3D(Env.BRAIN_XSIZE / 2, Env.BRAIN_YSIZE / 2, Env.BRAIN_ZSIZE / 2);
private static boolean[][][] shape = new boolean[Env.BRAIN_XSIZE][Env.BRAIN_YSIZE][Env.BRAIN_ZSIZE];
private static List<Point3D> pointList = new ArrayList<>(); //pointList存放上面shape的所有有效点用来加快显示循环而不用遍历三维数组
static {
putPixiel("FROG");
}
private static void putPixiel(String str) {
byte[][] c = StringPixelUtils.getStringPixels(Font.SANS_SERIF, Font.PLAIN, 12, 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) {
Point3D p = new Point3D(C.x + x, C.y + y, C.z + z);
if (!Animal.outBrainRange(p.x, p.y, p.z)) {
shape[p.x][p.y][p.z] = true;
pointList.add(p);
}
}
}
}
}
for (int z = 0; z < 2; z++)
for (int x = 0; x < 33; x++) { //再划一条线把字连起来目前细胞不能隔空分裂只能在紧邻位置分裂
Point3D p = new Point3D(C.x + x, C.y, C.z + z);
if (!Animal.outBrainRange(p.x, p.y, p.z)) {
shape[p.x][p.y][p.z] = true;
pointList.add(p);
}
}
}
public static void judge(Animal animal) {//检查animal的脑细胞是否位于brainShape的范围内
for (Cell c : animal.cells) {
if (shape[c.x][c.y][c.z]) {
animal.normalAward();
} else {
animal.normalPenalty();
}
}
}
public static void show(BrainPicture pic) {// 在脑图上显示当前形状
for (Point3D p : pointList)
pic.drawCircle(p.x, p.y, p.z, 1);
}
}

View File

@ -0,0 +1,31 @@
/*
* 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;
/**
* Point has x,y,z value
*
* @author Yong Zhu
* @since 2021-10-01
*/
@SuppressWarnings("all")
public class Point3D {
public int x;
public int y;
public int z;
public Point3D(int x, int y, int z) {
this.x = x;
this.y = y;
this.z = z;
}
}

View File

@ -0,0 +1,88 @@
/* Copyright 2018-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
* applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
* OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*/
package com.gitee.drinkjava2.frog.util;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.util.HashMap;
import java.util.Map;
/**
* StringPixelUtils used to get pixel array from a given string
*
* 根据给定的字体和字符串返回它的像素点阵lettersMap[0][0]是左下角素
*
* @author Yong Zhu
* @since 2.0.2
*/
public class StringPixelUtils {
private static final Map<String, byte[][]> lettersMap = new HashMap<>();
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() - 3;
int strWidth = fm.stringWidth(s);
g2d.drawString(s, 0, fm.getAscent() - fm.getLeading() - 1);
byte[][] b = new byte[strWidth][strHeight];
for (int y = 0; y < strHeight; y++)
for (int x = 0; x < strWidth; x++)
if (bi.getRGB(x, y) == -1)
b[x][strHeight - y - 1] = 1;
else
b[x][strHeight - y - 1] = 0;
lettersMap.put(key, b);
return b;
}
//*- 这个是测试输出平时不需要用
public static void main(String[] args) {
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();
}
}
//*/
}

View File

@ -23,7 +23,7 @@ public class Systemout {
System.out.print(obj);
}
public static void printdln(Object obj) {
public static void println(Object obj) {
if (allowPrint)
System.out.println(obj);
}

BIN
result13_frog3d.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

View File

@ -224,3 +224,9 @@ T:顶视 F前视 L:左视 R:右视 X:斜视 方向键:剖视 空格:
2021-08-13 演示同时处理多个方向的信号
位于history\005a1目录下演示用5个声母和5个韵母的组合来关联到25个图像的识别这样可以减少声音输入区的数量。它的另一个目的是演示体全息存贮的工作模式可以同时处理多个方向的信号。这个演示分辨率极差只有约一半的识别率但我不打算继续改进了。
![result12](result12_letter_test2.png)
2021-10-13 失败的细胞分裂尝试
这次本来想模仿生物细胞的分裂从一个细胞开始分裂出任意指定的三维形状并设计了split、goto等基因命令但是做来做去做不出结果细胞们就象跳蚤一样乱跑不听使唤最终还是决定放弃细胞分裂这个算法太难了。细胞分裂的优点是更“象”生物而且估计可以利用分形原理缩小基因的长度基因相当于一种自带循环和条件判断的计算机语言。
最终生成三维形状这个目标还是借助简单遗传算法完成,通过细胞在相邻位置随机生成,并在基因里记录每个细胞的坐标的方式来实现,基因命令被删得只剩一个了,就是随机生成细胞。
用遗传算法来生成任意形状,就好象一个画家在画画,但是画什么根本不知道,只知道听从旁边人打分,画的好就打高分,画的不好就打低分,这样一直循环下去,最终画的内容只由打分的人决定。目前速度上还有改进余地,比如让新细胞有更多变异率。
![result13](result13_frog3d.gif)