Add history\011, yinyan_eatfood

This commit is contained in:
yong 2022-09-26 00:48:43 -06:00
parent 89548a4d75
commit b8733661b7
47 changed files with 3277 additions and 275 deletions

View File

@ -81,10 +81,10 @@ Frog: 这是人工生命的主体,目前起名叫青蛙(Frog),其实叫什
这个版本的目的是为了增加一点趣味性,显得青蛙还是有点"用处"的,省得让人以为这个项目不务正业,青蛙只会找食。这个版本青蛙的脑结构和找食版的青蛙基本相同,区别只是在于环境不同,也就是说它的表现随着环境而变化,这符合"通用人工智能"的概念,即信号感受器官是统一的(通常是眼睛)但能根据不同的环境完成不同的任务。走跷跷板演示是最后一个2维脑的版本今后这个项目将沉寂一段较长时间我将致力于将青蛙脑重构为3D金字塔形脑结构(见上文)因为这个项目的缺点已经很明显它不具备对2维图像的模式识别能力用随机试错的方式只能处理非常简单的、信号在视网膜的固定区域出现的图像信号。
青蛙的找食效率以及走跷跷板平衡的能力都没有优化到顶点,一些构想中的复杂的器官如“与门”、“或门”(不要怀疑大自然能否进化出这些复杂器官)等都没加上器官的用进废退、奖励信号的利用都没反映但我认为这些还不关键目前最急迫的任务应该是先进行3D脑结构建模让青蛙能具备2维图形的模式识别(和回忆)功能,这个大的架构重构是它能处理复杂图像信息的立足之本,它的图像识别能力和通常的用上千张图片来训练识别一个图片这种工作模式完全不同,它是一种通用的,可自动分类识别所有图像的模式,更符合动物脑的工作模式,记住并回忆出某个图像(或任意输入信号场景的组合),可能只需要这种场景重复出现过几次即可,它是一种无外界信号判定,自动分类的识别模式。
2019-09-09 开始3D脑的构建任务又回到原点找食从静止的青蛙要能进化到吃光所有食物。目前只是搭建空的3D框架还未涉及3D脑模型编程。新的工作存放在core3d目录原有的旧core目录保留相应地批处理文件也分为普通版run.bat和3d版run3d.bat,蛋文件也分为普通版eggs.ser和3d版eggs3d.ser。
脑的3D版要引入模式识别功能第一个编程任务是要用这个3D脑模拟出体全息存贮现象也就等价于模式识别功能用字母的点阵图像激活它的视觉区并同时激活一个随意指定的脑区作为字母识别区然后只激活视觉区的图像再检查这个随意指定的字母区是否会被激活而其它字母区基本不激活例如同时训练字母A、B、C、D一段时间然后只激活字母A的图像区即在视网膜区产生光子)其它三个BCD字母区应该没有A字母区活跃(即其它字母区收到的光子数或总能量少于A字母区),这是从从二维图像识别出对应的文字,比较好检测并利用检测结果来进行生存竟争,从大量随机生成的脑细胞播种器官中挑选出有用的器官,注意在同一个脑空间(cube),可以同时存在多个不同器官播种出来的脑细胞(cell。体全息存贮在物理上很难实现,因为光和材料受物理特性制约,但在虚拟脑中不存在这个物理限制,让虚拟的光子反射、拐弯、增强、拆分都可轻易模拟出。
反之如果对应A字母的脑区兴奋也要能激活一个模糊的A的像素点阵图像脑内某个区(与视网膜重合或下层),这不好检测但可以变通一下比如同时训练C和O两个字母然后只激活C字母区并检测O字母区是否有较强的信号也就是说C字母区会在脑内成像区生成一个C的像素点阵然后这个像素点阵又会倒过来激活与C图像相似的O字母区(以及所有类似C图像的其它脑区),这是第二步的编程任务,它反映了脑的联想机制。如果说第一步是信息的存贮,是体全息存贮原理的简单实现,这第二步就是信息的检索和联想,相比与普通的体全息存贮,它多了一个联想功能,这个联想功能以后会用到,由痛苦、愉快等奖惩机制来调节这个联想过程一直永无止歇地进行下去。
脑的3D版要引入模式识别功能第一个编程任务是要用这个3D脑模拟出体全息存贮现象也就等价于模式识别功能用字母的点阵图像激活它的视觉区并同时激活一个随意指定的脑区作为字母识别区然后只激活视觉区的图像再检查这个随意指定的字母区是否会被激活而其它字母区基本不激活。体全息存贮在物理上很难实现因为光和材料受物理特性制约但在虚拟脑中不存在这个物理限制让虚拟的光子反射、拐弯、增强、拆分都可轻易模拟出。
反之如果对应A字母的脑区兴奋也要能激活一个模糊的A的像素点阵图像脑内某个区(与视网膜重合或下层),这就是信息的检索和联想,这个联想功能以后会用到,由痛苦、愉快等奖惩机制来调节这个联想过程一直永无止歇地进行下去。
2019-11-11 字母的模式识别功能
这是个比较重要的更新,也是切换到3D脑的第一个正式版本更新它实现了ABCD四个字母的识别。测试时分别用ABCD四个字母并同时加上一个声音信号模拟体全息存贮。另外这个模式识别是双向的如果只单单激活听力区也会在视网膜区成像。如果要演示这点需要将LetterTester.java中的seeImage和hearSound两行注释互换一下并去除Cell.java中的59和60两行这两行代码的作用是阻止光子逆向传播到视网膜上)。以下是这个模式识别的截图,黑色的小点表示视网膜发出的视觉波信号,蓝色的表示耳朵发出的听力波信号:在它们交汇的地方,细胞象果冻一样,被波的载体(光子)砸出洞来,每当接收到新的光子,就有可能在旧的洞里砸出光子来并逆向传播,用红色小点来表示,最终在波源处逆向成像。这个工作原理在细胞级别将相关的信号关联起来,也是体全息存贮的模拟实现,可以在三维空间实现信息的高密度存贮:
这是切换到3D脑的第一个正式版本更新它实现了ABCD四个字母的识别。测试时分别用ABCD四个字母并同时加上一个声音信号模拟体全息存贮。另外这个模式识别是双向的如果只单单激活听力区也会在视网膜区成像。如果要演示这点需要将LetterTester.java中的seeImage和hearSound两行注释互换一下并去除Cell.java中的59和60两行这两行代码的作用是阻止光子逆向传播到视网膜上)。以下是这个模式识别的截图,黑色的小点表示视网膜发出的视觉波信号,蓝色的表示耳朵发出的听力波信号:在它们交汇的地方,细胞象果冻一样,被波的载体(光子)砸出洞来,每当接收到新的光子,就有可能在旧的洞里砸出光子来并逆向传播,用红色小点来表示,最终在波源处逆向成像。这个工作原理在细胞级别将相关的信号关联起来,也是体全息存贮的模拟实现,可以在三维空间实现信息的高密度存贮:
这个模式识别的原理比较简单,不需要任何高等数学知识,每个人都能看懂,而且它可能更符合人脑的工作模式,它可以进行图像到声音的关联,也可以实现声音到图像成像的逆关联,另外还有两个重要优点:1.它可以同时处理多维的信号,也就是说可以同时处理多个图片、声音等信号。 2.它的训练速度非常快,没有采用什么海量的大数据来进行训练,只要任意有关联的信号,哪怕信号只出现一两次,它都会自动将它们关联起来,这个关联是动物的条件反射能够建立的基础。
有了模式识别,以后的工作就好办了。今后将在这个模式识别基础上进行扩展,进行多参数优化自动生成器官、声音的编码、把小蛇引入到虚拟环境等等一系列更复杂有趣的任务。
2019-11-16 模式识别功能更新
@ -94,8 +94,8 @@ Frog: 这是人工生命的主体,目前起名叫青蛙(Frog),其实叫什
2019-11-26 优化了一下程序,用"对酒当歌人生几何"8个汉字来进行模式识别原理演示但容错性依然没有变形、变位后的文字识别率很差。以后要考虑借签算法中的侧抑制、卷积、分层等原理来提高它的容错性用图形化模拟的方式来实现。总体上算法和图形化模拟是两条路算法通常可以用模拟的方式来表达但不是所有模拟都可以归纳成算法因为模拟(或者说软件本身)有时会非常复杂,不容易总结出规律。也就是说模拟的表现力比算法更强,但模拟的缺点是资源消耗大。
2019-12-27 开始设立history目录给主要的版本直接在history目录下创建副本以方便运行。在history\003a_legs目录下依然是2维脑)尝试给青蛙加两条腿看它能不能自动学会走路。一条腿位于下方负责左右移动一条腿位于右侧负责上下移动每条腿有抬腿、落腿、转动和相应的感觉细胞。只有当腿落下且转动而且另一条脚抬起来时青蛙才会位移具体什么时候抬腿、什么时候转动腿完全由随机数决定。经过一段时间的生存汰淘之后青蛙会进化出会利用两条腿走路了但需要的时间非常长约几个小时之后才达到最高吃食率50%左右,走路风格也比较诡异,是小碎步而不是大踏步。但至少这是青蛙第一次利用两条腿来走路,还是有点意义的,这证明生命进化中就算神经元随机排布,进化出眼睛和腿也是非常简单自然的事。这个实验只给青蛙加了两条腿,但同理如果有四条或更多的腿它应该也是可以随机进化出来的。
![result7](result7_legs.gif)
2020-05-04 在进行3维脑改造过程中发现找食率很低发现自己也看不懂以前的程序怎么编的了所以在history目录下又添加一个003b_simple目录把2维脑简化一下去掉不重要的器官仔细分析它的逻辑。
2020-05-07 经过一番折腾和走弯路之后最后还是原汁原味地将2维脑003b目录的逻辑搬到了3维脑core目录里了实现了同样的找食率(~50%左右)。从现在开始可以专注于改进3D脑本身了。另外README_ENG.md和开发思路.md这两个不重要的文档不再提交到码云。
2020-05-04 在进行3维脑改造过程中发现找食率很低自己也看不懂以前的程序怎么编的了所以在history目录下又添加一个003b_simple目录把2维脑简化一下去掉不重要的器官好分析它的逻辑。
2020-05-07 经过一番折腾和走弯路之后最后还是原汁原味地将2维脑003b目录的逻辑搬到了3维脑core目录里了实现了同样的找食率(~50%左右)。从现在开始可以专注于改进3D脑本身了。
2020-06-26 小蛇吃青蛙
位于history\006目录下设定小蛇只能看到青蛙青蛙只能看到蛇(严格说是蛇的舌头)。可以看到小蛇会追着青蛙,而青蛙会躲开小蛇,当然也有躲不开被吃掉的。除了引入负值连线用蓝色线条来表示外,技术细节上倒没有什么突破,但这个实验有趣的地方在于它证实了就算是完全随机的排列脑细胞,在长期的优胜劣汰后,生命也会进化出捕食和逃避行为。即然可以进化出捕食和逃避行为,而生命进化又会向越来越复杂的方向进化,所以这个原理可以解释为意识的萌芽了。高等生命的意识,本质上也无非就是大自然随机运动产生的一种复杂现象而已。
![result8](result8_snake.gif)
@ -120,7 +120,7 @@ Frog: 这是人工生命的主体,目前起名叫青蛙(Frog),其实叫什
这个任务看起来和神经网络关系不大,但我觉得有可能利用这个算法来进行器官自动排布,所以还是有一定的意义的,任意复杂的形状生成,今后三维脑的细胞结构,都有可能从这个任务演化出来。
输出结果pama_1234编写)
![result10](result10_drawsnake.gif)
他的项目位于这里:[细胞画蛇](https://gitee.com/pama1234/cell-painting-snake), 有兴趣研究源码的可以看一看是基于processing软件编写的。
他的项目位于这里:[细胞画蛇](https://gitee.com/pama1234/cell-painting-snake), 有兴趣研究源码的可以看一看是基于processing编写的。
顺便我把我的构思也写在这里,我没有仔细研究他的代码,但估计思路应该大体一致:
1.与形状表达相关的基因采用多叉树结构数据结构树结构是单例只保存1份在内存中每个细胞分裂后端粒酶减一相当于从树结构往下走一级。
2.每个细胞分裂后,为了获得当前细胞的位置,但是又不能复制整个子树,所以要保存一个指针,指向树的当前节点,即子树的顶点。不再分裂的节点可以不保存节点指针。
@ -154,7 +154,7 @@ Frog: 这是人工生命的主体,目前起名叫青蛙(Frog),其实叫什
2021-11-08 成功的细胞分裂尝试
位于history\009目录下这次的细胞分裂算法采用自顶向下的策略也就是从单个细胞开始一个细胞分裂成8个(因为1个正方体切三刀正好是8个小正方体)这种方式来进行。这种方案不是从目标形状的局部开始填充,而是从毛胚、从目标的粗轮廓开始利用遗传算法细化,直到细化出每个细节。这种方案的优点是更接近生物实际,符合“从总体到局部”的正常逻辑,而且有高效的基因压缩存储率,为了说明存储率这点,大家可以看看下图左面这个树结构,猜一猜要存储它最少需要多少个字节?
![depth_tree](depth_tree.png)
答案是最少只需要一个整数7就可以表达这个树结构了。说一下原理首先所有树结构都可以用行号+深度的方式来表达,详见我的博客[基于前序遍历的无递归的树形结构](https://my.oschina.net/drinkjava2/blog/1818631),获取子树时可以避免递归访问,其次因为采用基因敲除的方式,只需要记录被敲除的树节点的行号和深度就就可以了,最后因为上例固定采用3叉树分形结构根据行号就可以算出它的深度,所以深度值也可以省略,最后只用一个行号7就可以表达这整棵树了。
答案是最少只需要一个整数7就可以表达这个树结构了。说一下原理首先所有树结构都可以用行号+深度的方式来表达,详见我的博客[基于前序遍历的无递归的树形结构](https://my.oschina.net/drinkjava2/blog/1818631)获取子树时可以避免递归访问其次因为采用基因敲除的方式只需要记录被敲除的树节点的行号和深度就就可以了最后因为固定采用3叉树分形结构所以深度值也可以省略只用一个行号7就可以表达这整棵树了。
下图是这个算法的动画可以看出它与上次的演示是不同的分裂模式是先有总体后有细节。项目中实际采用的是8叉树深度用细胞边长表示:
![result14](result14_wa3d.gif)
细胞分裂算法一方面可以利用来生成和优化物理形状(比方虚拟风叶、翅膀、受力结构等形状),另一方面它和神经网络的形成算法是有共通点的,因为众所周知心脏形状、血管网络、大脑神经网络都是由基因控制细胞分裂出来的。所以以后有可能利用这个算法来自动生成和优化神经网络触突三维空间分布结构。
@ -174,18 +174,28 @@ Frog: 这是人工生命的主体,目前起名叫青蛙(Frog),其实叫什
细胞分裂研究算法目前还不能结束(打脸自己),还要先解决这个缺色问题。多参数的进化,如果一旦某个参数被误删除就不能再进化回来,这种算法是不能接受的。
2022-01-15 多参数的细胞分裂继续改进:阴阳无极八卦阵算法
位于history\009c目录下采用了阴阳(黑白)节点算法阴节点基因会删除节点下所有节点是自顶向下的减材加工阳节点基因会保留节点下所有节点是自底向上的增材加工。利用遗传算法的大样本筛选把自顶向下和自底向上两个进化方向结合起来这样基本能解决误删分支后缺色不能补回这个问题。而且对于奖罚不再象以前一样要设成差距很大的值animal.java中awardAAAA()原来要设成8000, 现在设成20也不会产生缺色现象。这个版本是研究技术细节问题看不懂的同学们可以跳过。
考虑到这个算法的特点我给它起名“阴阳无极八卦阵算法“阴阳是指它有阴阳两种节点无极是指它的分裂阶数没有限制八卦阵是指它采用了多个8叉树结构每一维细胞参数都对应一个8叉树。
位于history\009c目录下采用了阴阳(黑白)节点算法阴节点基因会删除节点下所有节点是自顶向下的减材加工阳节点基因会保留节点下所有节点是自底向上的增材加工。利用遗传算法的大样本筛选把自顶向下和自底向上两个进化方向结合起来这样基本能解决误删分支后缺色不能补回这个问题。而且对于奖罚不再象以前一样要设成差距很大的值animal.java中awardAAAA()原来要设成8000, 现在设成20也不会产生缺色现象。考虑到这个算法的特点我给它起名“阴阳无极八卦阵算法“阴阳是指它有阴阳两种节点无极是指它的分裂阶数没有限制八卦阵是指它采用了多个8叉树结构每一维细胞参数都对应一个8叉树。
2022-07-22 树的生长演示
位于history\010目录演示一个树的生长。因为以前小鱼是手工指定一个三维像素点阵模板,有点作弊的嫌疑,所以这次演示是基于规则而不是模板来生成一棵树,这棵树的形状仅由以下规则决定:
位于history\010目录演示一个树的生长。这个演示是基于规则而不是模板来生成一棵树,这棵树的形状仅由以下规则决定:
1.树根位于底部中心
2.树的每个细胞水平方向四周不能有其它细胞
3.树的每个细胞正下方不能有细胞,但必须在斜下方至少有一个细胞
基于这三条规则,利用阴阳无极八卦阵分裂算法,树能够自动进化出来,但要耐心等一会儿。这个演示的意义是表示形状可以由规则来决定,不同的规则能进化出不同的形状,生物会通过改变自己的形状来适应环境规则。
这个项目下一个任务就要基于形状由规则决定这个原理制定模式识别奖惩规则然后利用分裂算法让细胞自动在空间上排布出不同细胞参数从而进化出具有初步模式识别功能的神经网络比方说识别出012 ,3数字。大自然生物的模式识别不是通过人为设计算法而就是这样无脑随机分裂细胞进化出来的。
基于这三条规则,利用阴阳无极八卦阵分裂算法,树能够自动进化出来。这个演示的意义是表示形状可以由规则来决定,不同的规则能进化出不同的形状,生物会通过改变自己的形状来适应环境规则。
![result17](result17_tree_grow.gif)
2022-09-25 用阴阳无极八卦阵完成找食任务
位于history\011目录这是利用阴阳无极八卦阵算法来完成最开始的“找食”这个任务即采用模拟细胞分裂再配合遗传算法生成一个简单功能的神经网络输入只有上下左右四种视觉细胞输出只有上下左右四种运动细胞。
为什么要使用阴阳无极八卦阵来生成神经网络,是因为它有几个特点:
1.传统神经网络因为手工调优参数太多,被类比为”炼丹法“,而采用阴阳无极八卦阵之后,算法将只有固定的这一个,调参完全交给计算机,手工只需要设计基因和细胞行为即可,以不变应万变。只要有利于生存的脑结构,就会被筛选出来,结构即逻辑。
2.有高效的信息存储方式(见先前8叉树的介绍),基因虽然是随机生成的,但是它采用树结构这种方式有非常高的信息压缩比,可以用少量的基因控制巨量细胞的生成。
3.更贴近实际生物的神经网络生成原理。大自然就是采用分裂+遗传算法来生成脑神经网络的,理论上我们可以应该照抄这个模式来创造出高智能的神经网络。
4.它的神经网络是三维的,结构组合方式是无穷的。同一个细胞可以由多个基因控制,一个基因可能控制零个或所有的细胞。
下面是实际运行结果最上面一层用4种颜色来表示眼睛细胞其余的细胞参数如运动细胞、轴突方向、轴突长度等用不同大小、颜色的圆来表示
![result18](result18_yinyan_eatfood.gif)
这个版本的目的是验证阴阳无极八卦阵算法的实用性和搭建初步框架。后面的任务将是进一步复杂化,生成有模式识别功能的神经网络三维结构。
## 运行方式 | Run
运行core或history各个子目录下的run.bat批处理文件即可启动运行history下有多个子目录按版本号顺序排列存放着这个项目演化过程中的主要历史版本供演示。
另外如果想要研究这个项目的早期版本可以结合gitk命令和参考"版本提交记录.md"的介绍用git reset命令回复到以前任一个版本例如用:

View File

@ -11,6 +11,7 @@
package com.gitee.drinkjava2.frog;
import static com.gitee.drinkjava2.frog.brain.Cells.GENE_NUMBERS;
import static com.gitee.drinkjava2.frog.util.RandomUtils.percent;
import java.awt.Graphics;
import java.awt.Image;
@ -20,8 +21,10 @@ import java.util.ArrayList;
import javax.imageio.ImageIO;
import com.gitee.drinkjava2.frog.brain.Cells;
import com.gitee.drinkjava2.frog.brain.Eye;
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;
@ -50,7 +53,7 @@ public abstract class Animal {// 这个程序大量用到public变量而不是ge
}
}
/** brain cells */
/** 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];
@ -91,11 +94,8 @@ public abstract class Animal {// 这个程序大量用到public变量而不是ge
public void initAnimal() { // 初始化animal,生成脑细胞是在这一步这个方法是在当前屏animal生成之后调用比方说有一千个青蛙分为500屏测试每屏只生成2个青蛙的脑细胞可以节约内存
geneMutation(); //有小概率基因突变
for (ArrayList<Integer> gene : genes) //基因多也要适当小扣点分防止基因无限增长
energy -= gene.size();
createCellsFromGene(); //根据基因分裂生成脑细胞
//RainBowFishJudge.judge(this); //外界对是否长得象彩虹鱼打分
TreeShapeJudge.judge(this);
}
private static final int MIN_ENERGY_LIMIT = Integer.MIN_VALUE + 5000;
@ -111,39 +111,47 @@ public abstract class Animal {// 这个程序大量用到public变量而不是ge
}
//如果改奖罚值就可能出现缺色这个要在基因变异算法从上到下从下到上和环境本身奖罚合理性上下功夫
public void awardAAAA() { changeEnergy(20);}
public void awardAAAA() { changeEnergy(2000);}
public void awardAAA() { changeEnergy(10);}
public void awardAA() { changeEnergy(5);}
public void awardA() { changeEnergy(2);}
public void penaltyAAAA() { changeEnergy(-20);}
public void penaltyAAAA() { changeEnergy(-2000);}
public void penaltyAAA() { changeEnergy(-10);}
public void penaltyAA() { changeEnergy(-5);}
public void penaltyA() { changeEnergy(-2);}
public void kill() { this.alive = false; changeEnergy(-500000); Env.clearMaterial(x, y, animalMaterial); } //kill是最大的惩罚
public void kill() { this.alive = false; changeEnergy(-5000000); Env.clearMaterial(x, y, animalMaterial); } //kill是最大的惩罚
//@formatter:on
public boolean active() {// 这个active方法在每一步循环都会被调用是脑思考的最小帧
// 如果能量小于0出界与非食物的点重合则判死
if (!alive) {
energy = MIN_ENERGY_LIMIT; // 死掉的青蛙确保淘汰出局
return false;
}
if (energy <= 0 || Env.outsideEnv(x, y) || Env.bricks[x][y] >= Material.KILL_ANIMAL) {
kill();
return false;
}
//energy -= 20;
// 依次调用每个cell的active方法
//for (Cell cell : cells)
// cell.organ.active(this, cell);
this.energys[0][0][Env.BRAIN_ZSIZE - 1] = 10;
// if(Env.closeToEdge(this))
// energys[0][0][0]=10;
Eye.active(this); //如看到食物给顶层细胞赋能量
Cells.active(this); //细胞之间互相传递能量
if (Food.foundAndAteFood(this.x, this.y)) { //如当前位置有食物就吃掉并获得奖励
this.awardAAAA();
this.ateFood++;
}
return alive;
}
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 */
@ -152,14 +160,15 @@ public abstract class Animal {// 这个程序大量用到public变量而不是ge
}
public void geneMutation() { //基因变异,注意这一个算法同时变异所有条基因目前最多允许64条基因
if(percent(50))
for (int g = 0; g < GENE_NUMBERS; g++) {//随机新增阴节点基因
if (RandomUtils.percent(10)) {
if (percent(20)) {
ArrayList<Integer> 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) {
if (Tree8Util.keep[i] > 0) {
count++;
if (count >= randomIndex && !gene.contains(-i)) {
gene.add(-i);
@ -169,16 +178,17 @@ public abstract class Animal {// 这个程序大量用到public变量而不是ge
}
}
}
if(percent(50))
for (int g = 0; g < GENE_NUMBERS; g++) {//随机新增阳节点基因
if (RandomUtils.percent(5)) {
if (RandomUtils.percent(20)) {
ArrayList<Integer> 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) {
if (Tree8Util.keep[i] <= 0) {
count++;
if (count >= randomIndex && !gene.contains(i)) {
gene.add(i);
@ -189,29 +199,9 @@ public abstract class Animal {// 这个程序大量用到public变量而不是ge
}
}
// for (int g = 0; g < GENE_NUMBERS; g++) {//随机变异将阳节点向上提升一级相当于单个细胞的自底向上扩散式生长
// if (RandomUtils.percent(3)) {
// ArrayList<Integer> gene = genes.get(g);
// int randomIndex = RandomUtils.nextInt(gene.size());
// if (randomIndex > 0 && gene.get(randomIndex) > 0) {//如基因是阳基因且节点不是顶节点
// int size = Tree8Util.TREE8[randomIndex][0];
// gene.remove(randomIndex); //先删除底层这个阳基因
// for (int i = randomIndex - 1; i > 0; i--) {
// if (Tree8Util.TREE8[i][0] > size) { //深度树只要大于size就是它的父节点
// if (!gene.contains(i))
// gene.add(i);
// int x = gene.indexOf(-i);//如果有阴节点也删除
// if (x > 0)
// gene.remove(x);
// break;
// }
// }
// }
// }
// }
if(percent(50))
for (int g = 0; g < GENE_NUMBERS; g++) {//随机变异删除一个基因这样可以去除无用的拉圾基因防止基因无限增大
if (RandomUtils.percent(10)) {
if (RandomUtils.percent(40)) {
ArrayList<Integer> gene = genes.get(g);
if (!gene.isEmpty())
gene.remove(RandomUtils.nextInt(gene.size()));
@ -225,7 +215,7 @@ public abstract class Animal {// 这个程序大量用到public变量而不是ge
ArrayList<Integer> gene = genes.get(g);
Tree8Util.knockNodesByGene(gene);//根据基因把要敲除的8叉树节点作个标记
for (int i = 0; i < Tree8Util.NODE_QTY; i++) {//再根据敲剩下的8叉树keep标记生成细胞参数
if (Tree8Util.keep[i] >= 0) {
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

View File

@ -132,15 +132,15 @@ public class Application {
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.setSelected(Cells.display_gene[i]);
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;
Cells.display_gene[i]=true;
else
Env.display_gene[i]=false;
Cells.display_gene[i]=false;
}
};
geneRadio.addActionListener(geneRadioAction);

View File

@ -33,7 +33,7 @@ public class Env extends JPanel {
public static final int FROG_EGG_QTY = 100; // 每轮下n个青蛙蛋可调只有最优秀的前n个青蛙们才允许下蛋
public static final int FROG_PER_EGG = 4; // 每个青蛙蛋可以孵出几个青蛙
public static final int FROG_PER_EGG = 6; // 每个青蛙蛋可以孵出几个青蛙
public static final int SCREEN = 1; // 分几屏测完
@ -45,7 +45,7 @@ public class Env extends JPanel {
public static final boolean BORN_AT_RANDOM_PLACE = true;// 孵出青蛙落在地图上随机位置而不是在蛋所在地
/** Frog's brain size */ // 脑细胞位于脑范围内是个三维结构在animal中用三维数组来表示
public static final int BRAIN_CUBE_SIZE = 16; //脑立方边长大小必须是2的幂数如4,8,16...原因参见8叉树算法
public static final int BRAIN_CUBE_SIZE = 4; //脑立方边长大小必须是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方向长度
@ -67,10 +67,10 @@ public class Env extends JPanel {
public static final int FROG_BRAIN_DISP_WIDTH = 400; // Frog的脑图在屏幕上的显示大小,可调
/** Steps of one test round */
public static final int STEPS_PER_ROUND = 20;// 每轮测试步数,可调
public static final int STEPS_PER_ROUND = 200;// 每轮测试步数,可调
public static int step;// 当前测试步数
public static final int FOOD_QTY = 1500; // 食物数量, 可调
public static final int FOOD_QTY = 3000; // 食物数量, 可调
// 以下是程序内部变量不要手工修改它们
public static final int TOTAL_FROG_QTY = FROG_EGG_QTY * FROG_PER_EGG; // 蛇的总数
@ -87,19 +87,15 @@ public class Env extends JPanel {
public static List<Egg> frog_eggs = new ArrayList<>(); // 这里存放新建或从磁盘载入上轮下的蛋每个蛋可能生成几个青蛙
public static EnvObject[] things = new EnvObject[]{};// 所有外界物体如食物字母测试工具都放在这个things里面
public static EnvObject[] things = new EnvObject[]{Food.FOOD};// 所有外界物体如食物字母测试工具都放在这个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() {
@ -263,8 +259,8 @@ public class Env extends JPanel {
if (SHOW_SPEED > 0 && step % SHOW_SPEED != 0) // 用是否跳帧画图的方式来控制速度
continue;
if (SHOW_SPEED < 0) // 如果speed小于0人为加入延迟
sleep(-SHOW_SPEED);
if (SHOW_SPEED<30) // 如果speed为1人为加入延迟
sleep((30-SHOW_SPEED));
// 开始画虚拟环境和青蛙和蛇
g.setColor(Color.white);

View File

@ -1,6 +1,7 @@
package com.gitee.drinkjava2.frog.brain;
import static java.awt.Color.BLACK;
import static com.gitee.drinkjava2.frog.brain.Cells.*;
import static java.awt.Color.RED;
import static java.awt.Color.WHITE;
import static java.lang.Math.cos;
@ -319,10 +320,10 @@ public class BrainPicture extends JPanel {
ArrayList<Integer> 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]) {//如果允许显示的话, 显示当前层级的节点
if (size == i && Cells.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));
}
@ -353,20 +354,46 @@ public class BrainPicture extends JPanel {
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)
long cell = a.cells[x][y][z];
if ((cell & Cells.EXIST) == 0) //只显示有效的细胞点
continue;
if (a.energys[x][y][z] > 1) { //用大红色圆形画出能量大于1的细胞格
setPicColor(Color.MAGENTA); //开始画出对应的细胞基因参数用不同颜色直径圆表示
drawCircle(x + 0.5f, y + 0.5f, z + 0.5f, 1.2f);
}
if (x >= xMask && y >= yMask && cell != 0)//画出细胞每个基因存在的细胞格子
for (int geneIndex = 0; geneIndex < Cells.GENE_NUMBERS; geneIndex++) {
if ((a.cells[x][y][z] & (1 << geneIndex)) != 0 && Env.display_gene[geneIndex]) {
if ((cell & (1 << geneIndex)) != 0 && Cells.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);
}
}
if (((cell & ZT) > 0)) { //如有轴突基因则从当前细胞到轴突端点画一条红线
int x_ = (int) ((cell & ZTX) >> ZTX0);
if (x_ == 0)
x_ = -1;
int y_ = (int) ((cell & ZTY) >> ZTY0);
if (y_ == 0)
y_ = -1;
int z_ = (int) ((cell & ZTZ) >> ZTZ0);
if (z_ == 0)
z_ = -1;
int zt_long = (int) ((cell & ZT_LONG) >> ZT_LONG0) + 1; //轴突长度, 大小为1~8
int xx = x + x_ * zt_long;
int yy = y + y_ * zt_long;
int zz = z + z_ * zt_long;
if (Env.insideBrain(xx, yy, zz)) {
setPicColor(Color.RED);
drawLine(x+.5f, y+.5f, z+.5f, xx+.5f, yy+.5f, zz+.5f);
}
}
}
}
}
Eye.drawEye(this);//画眼睛
setPicColor(Color.BLACK);
//BrainShapeJudge.show(this);//这行显示目标形状这个模子
drawCuboid(brain);// 把脑的框架画出来
setPicColor(BLACK); //把x,y,z坐标画出来

View File

@ -10,15 +10,131 @@
*/
package com.gitee.drinkjava2.frog.brain;
import com.gitee.drinkjava2.frog.Animal;
import com.gitee.drinkjava2.frog.Env;
import com.gitee.drinkjava2.frog.util.Logger;
import com.gitee.drinkjava2.frog.util.RandomUtils;
/**
* Cells代表不同的脑细胞参数对应每个参数细胞有不同的处理光子的行为
* Cells代表不同的脑细胞参数对应每个参数用8叉树算法生成不同的细胞
* 每个脑细胞用一个long来存储所以最多允许64个基因位, 有时一个参数由多个基因位决定每个基因位都由一个单独的阴阳8叉树控制多个基因就组成了一个8叉树阵列
* 基因+分裂算法=结构
* 基因+分裂算法+遗传算法=结构的进化
* 这个类里定义每个基因位的掩码, 脑结构的所有参数都要用基因来控制开始时可以有常量魔数但以后都放到基因里去自动进化
*
*
* @author Yong Zhu
* @since 10.0
*/
@SuppressWarnings("all")
public class Cells {
public static int GENE_NUMBERS = 1; //目前有多少条基因每个脑细胞用是一个long来存储所以最多允许64条基因每个基因控制一个细胞的参数
public static int GENE_NUMBERS = 0;
private static int zeros = 0;
public static boolean[] display_gene = new boolean[64]; //脑最多有64个基因这里用来控制哪些基因需要显示在脑图上
public static long EXIST = mask(1, true); // 细胞存在否,1为存在,0为不存在, true表示显示在脑图上
public static long MOVE_UP = mask(1, true); //如此点为1则此细胞如有能量青蛙向上移动
public static long MOVE_DOWN = mask(1, true); //如此点为1则此细胞如有能量青蛙向下移动
public static long MOVE_LEFT = mask(1, true); //如此点为1则此细胞如有能量青蛙向左移动
public static long MOVE_RIGHT = mask(1, true); //如此点为1则此细胞如有能量青蛙向右移动
public static long ZT_LONG = mask(3, false); //轴突长度
public static long ZT_LONG0 = zeros; //如果参数由多位组成用同名+0变量表示有几个0移位运算时用来去除0下同
public static long ZT = mask(1, true);//axon exist 轴突是否存在
public static long ZTX = mask(1, false);//axon x offset, 轴突x方向, 轴突方向由x,y,z三个方向的参数组合决定
public static long ZTX0 = zeros; //x方向暂定1位如果2位也可以角度更细但出结果的时间会比较长
public static long ZTY = mask(1, false); //轴突y方向
public static long ZTY0 = zeros;
public static long ZTZ = mask(1, false); //轴突z方向
public static long ZTZ0 = zeros;
public static long ZT_MINUS = mask(1, false);//细胞信号,1为正信号,0为负(抑制)信号
// public static long RANDOM_ACTIVE = mask(1, true);//这个基因控制细胞随机产生能量对结果影响不大会让静止的青蛙颤抖
// public static long ST_LONG = mask(2, false); //dendrite radius, 树突长度半径待后继版本用到
// public static long ST_LONG0 = zeros;
// public static long HAPPY = mask(1, false); //吃食奖励信号待后继版本用到
public static long mask(int n, boolean display) { //返回基因掩码高位由n个1组成低位是当前GENE_NUMBERS个0这个方法执行后GENE_NUMBERS会加n
for (int i = GENE_NUMBERS; i < GENE_NUMBERS + n; i++) {
display_gene[i] = display;
}
String one = "";
String zero = "";
for (int i = 1; i <= n; i++)
one += "1";
for (int i = 1; i <= GENE_NUMBERS; i++)
zero += "0";
zeros = GENE_NUMBERS;
GENE_NUMBERS += n;
if (GENE_NUMBERS >= 64) {//
System.out.println("目前基因位数不能超过64");
System.exit(-1);
}
return Long.parseLong(one + zero, 2); //"111000"这种字符串转换为长整
}
public static void active(Animal a) {//这个方法的功能是根据细胞的参数在细胞间传输能量即信息的载体)
for (int z = Env.BRAIN_CUBE_SIZE - 1; z >= 0; z--)
for (int y = Env.BRAIN_CUBE_SIZE - 1; y >= 0; y--)
for (int x = Env.BRAIN_CUBE_SIZE - 1; x >= 0; x--) {
long cell = a.cells[x][y][z];
if ((cell & EXIST) == 0) //如细胞不存在
continue;
// if ((cell & RANDOM_ACTIVE) > 0) //随机产生细胞能量会让青蛙颤抖
// if (RandomUtils.percent(5))
// a.energys[x][y][z] = 1;
float e = a.energys[x][y][z];
if (e > 0 && ((cell & ZT) > 0)) { //如有轴突基因则当前细胞如存在能量会输送到轴突端点处
int x_ = (int) ((cell & ZTX) >> ZTX0); //把掩码转为坐标偏移量因为暂定只有1位所以只有1,-1
if (x_ == 0)
x_ = -1;
int y_ = (int) ((cell & ZTY) >> ZTY0);
if (y_ == 0)
y_ = -1;
int z_ = (int) ((cell & ZTZ) >> ZTZ0);
if (z_ == 0)
z_ = -1;
int zt_long = (int) ((cell & ZT_LONG) >> ZT_LONG0) + 1; //轴突长度, 大小为1~8
int xx = x + x_ * zt_long;
int yy = y + y_ * zt_long;
int zz = z + z_ * zt_long;
if (Env.insideBrain(xx, yy, zz)) {
if (a.energys[xx][yy][zz] < 10) {
if ((cell & ZT_MINUS) > 0) //如果轴空是负信号
a.energys[xx][yy][zz]--;
else
a.energys[xx][yy][zz]++;
if (a.energys[x][y][z] > 0)
a.energys[x][y][z]--;
}
}
}
e = a.energys[x][y][z];
if ((e > 0) && (z < Env.BRAIN_ZSIZE - 1)) { //如当前细胞有能量且不和眼睛在同一层且有移动细胞则青蛙移动
if ((cell & MOVE_UP) > 0) {//向上y是减屏幕y的0点在上方
a.energys[x][y][z] = 0;
a.y++;
}
if ((cell & MOVE_DOWN) > 0) {
a.energys[x][y][z] = 0;
a.y--;
}
if ((cell & MOVE_LEFT) > 0) {
a.energys[x][y][z] = 0;
a.x--;
}
if ((cell & MOVE_RIGHT) > 0) {
a.energys[x][y][z] = 0;
a.x++;
}
}
}
}
}

View File

@ -0,0 +1,76 @@
/*
* Copyright 2018 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
* applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
* OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*/
package com.gitee.drinkjava2.frog.brain;
import java.awt.Color;
import com.gitee.drinkjava2.frog.Animal;
import com.gitee.drinkjava2.frog.Env;
import com.gitee.drinkjava2.frog.objects.Material;
/**
* Eye, if found food then set cell energy at top level
*
* @author Yong Zhu
* @since 1.0
*/
public class Eye {
/** First eye can only see if food nearby at 4 directions */
public static void active(Animal a) {// 眼睛只能观察上右四个方向有没有食物如发现食物就将最上层的四个边的细胞激活成1能量
int seeDist = 15; //眼睛能看多远
for (int i = 1; i < seeDist; i++)
if (Env.hasMaterial(a.x, a.y + i, Material.FOOD)) {//up
for (int x = 1; x < Env.BRAIN_XSIZE; x++)
a.energys[x][Env.BRAIN_YSIZE - 1][Env.BRAIN_ZSIZE - 1] = 1;
break;
}
for (int i = 1; i < seeDist; i++)
if (Env.hasMaterial(a.x, a.y - i, Material.FOOD)) { //down
for (int x = 0; x < Env.BRAIN_XSIZE - 1; x++)
a.energys[x][0][Env.BRAIN_ZSIZE - 1] = 1;
break;
}
for (int i = 1; i < seeDist; i++)
if (Env.hasMaterial(a.x - i, a.y, Material.FOOD)) { //left
for (int y = 1; y < Env.BRAIN_YSIZE; y++)
a.energys[0][y][Env.BRAIN_ZSIZE - 1] = 1;
break;
}
for (int i = 1; i < seeDist; i++)
if (Env.hasMaterial(a.x + i, a.y, Material.FOOD)) {//right
for (int y = 0; y < Env.BRAIN_YSIZE - 1; y++)
a.energys[Env.BRAIN_XSIZE - 1][y][Env.BRAIN_ZSIZE - 1] = 1;
break;
}
}
public static void drawEye(BrainPicture bp) {//在脑图中画出眼睛
bp.setPicColor(Color.RED);
for (int x = 1; x < Env.BRAIN_XSIZE; x++)
bp.drawCircle(x + 0.5f, Env.BRAIN_YSIZE - 0.5f, Env.BRAIN_ZSIZE - 0.5f, 1);
bp.setPicColor(Color.YELLOW);
for (int x = 0; x < Env.BRAIN_XSIZE - 1; x++)
bp.drawCircle(x + 0.5f, 0.5f, Env.BRAIN_ZSIZE - 0.5f, 1);
bp.setPicColor(Color.BLUE);
for (int y = 1; y < Env.BRAIN_YSIZE; y++)
bp.drawCircle(0.5f, y + 0.5f, Env.BRAIN_ZSIZE - 0.5f, 1);
bp.setPicColor(Color.GREEN);
for (int y = 0; y < Env.BRAIN_YSIZE - 1; y++)
bp.drawCircle(Env.BRAIN_XSIZE - 0.5f, y + 0.5f, Env.BRAIN_ZSIZE - 0.5f, 1);
}
}

View File

@ -59,7 +59,17 @@ public class Egg implements Serializable {
public Egg(Egg a, Egg b) {//两个蛋的基因混合, 生成一个新蛋
x = a.x;
y = a.y;
genes.addAll(a.genes); //TODO: 两个蛋的基因混合
genes.addAll(a.genes);
if (RandomUtils.percent(20)) //插入蛋B的基因到A蛋中
for (int i = 0; i < b.genes.size(); i++) {
if (RandomUtils.percent(2)) {
ArrayList<Integer> aGene = a.genes.get(i);
ArrayList<Integer> bGene = b.genes.get(i);
if (bGene.size() > 1) {//随机插入一个B的基因不用担心基因越来越多因为随机删除的速度大于增长的
aGene.add(bGene.get(RandomUtils.nextInt(bGene.size())));
}
}
}
}
}

View File

@ -50,7 +50,7 @@ public class FrogEggTool {
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);
Logger.info("Fist frog energy={}, gene size={}, Last frog energy={}", first.energy, getGeneSize(first), last.energy);
if (Env.SAVE_EGGS_FILE) {
FileOutputStream fo = new FileOutputStream(Application.CLASSPATH + "frog_eggs.ser");
ObjectOutputStream so = new ObjectOutputStream(fo);
@ -63,6 +63,16 @@ public class FrogEggTool {
}
}
private static String getGeneSize(Frog f) {//按genes类型汇总每种基因的个数
StringBuilder sb=new StringBuilder("[");
for (int i = 0; i < f.genes.size(); i++)
sb.append(f.genes.get(i).size()).append(",");
if(sb.length()>1)
sb.setLength(sb.length()-1);
sb.append("]");
return sb.toString();
}
private static void sortFrogsOrderByEnergyDesc() {// 按能量多少给青蛙排序
Collections.sort(Env.frogs, new Comparator<Animal>() {
public int compare(Animal a, Animal b) {

View File

@ -1,84 +0,0 @@
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<int[]> pointList = new ArrayList<>(); //pointList存放上面shape的所有有效点用来加快显示循环而不用遍历三维数组
static {
putPixiel("🐟");
}
public static void putPixiel(String str) {
byte[][] c = StringPixelUtils.getStringPixels(Font.SANS_SERIF, Font.PLAIN, Env.BRAIN_CUBE_SIZE, str); //要把frog二维像素变成立体的三维点放到points里和pointsList里供使用
int w = c.length;
int h = c[0].length;
for (int z = 0; z < 5; z++) {
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
if (c[x][y] > 0) {
int[] p = new int[]{C[0] + x, C[1] + y + 2, C[2] + z};
if (!Animal.outBrainRange(p[0], p[1], p[2])) {
shape[p[0]][p[1]][p[2]] = true;
pointList.add(p);
}
}
}
}
}
}
private static void judgeShape(Animal animal) {//检查animal的脑细胞是否位于brainShape的范围内
for (int x = 0; x < Env.BRAIN_CUBE_SIZE; x++) {
for (int y = 0; y < Env.BRAIN_CUBE_SIZE; y++) {
for (int z = 0; z < Env.BRAIN_CUBE_SIZE; z++) {
if ((animal.cells[x][y][z] & 1) != 0)
if (shape[x][y][z]) {
animal.awardAAAA();
} else {
animal.penaltyAAA();
}
}
}
}
}
public static void judgeColor(Animal animal) {//检查颜色与小鱼图像重合且呈斑马纹色彩分布是就加分否则扣分
int colorWidth = Env.BRAIN_CUBE_SIZE / Cells.GENE_NUMBERS; //每条彩带宽度
for (int x = 0; x < Env.BRAIN_CUBE_SIZE; x++)
for (int y = 0; y < Env.BRAIN_CUBE_SIZE; y++)
for (int z = 0; z < Env.BRAIN_CUBE_SIZE; z++)
for (int i = 1; i <= Cells.GENE_NUMBERS; i++)
if ((animal.cells[x][y][z] & (1 << i)) != 0) //如果色彩存
if ((animal.cells[x][y][z] & 1) != 0 && x >= (i - 1) * colorWidth && x < (i * colorWidth))
animal.awardAA();
else
animal.penaltyA();
}
public static void judge(Animal animal) {//检查animal的脑细胞是否位于brainShape的范围内
judgeShape(animal);
judgeColor(animal);
}
public static void show(BrainPicture pic) {// 在脑图上显示当前形状
for (int[] p : pointList)
pic.drawCircle(p[0] + 0.5f, p[1] + 0.5f, p[2] + 0.5f, 1);
}
}

View File

@ -1,52 +0,0 @@
package com.gitee.drinkjava2.frog.judge;
import com.gitee.drinkjava2.frog.Animal;
import com.gitee.drinkjava2.frog.Env;
/**
* 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] & 1) > 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 & 1) > 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();
}
}
}
}

View File

@ -33,6 +33,7 @@ import java.util.concurrent.BlockingQueue;
*/
@SuppressWarnings("all")
public class Logger {
private static final int LOGGER_STYLE = 0; //风格设定 0:不输出前缀, 1:输出时间行号等前缀
private static final String LEV_EL = "debug";
private static final int LEVEL_INT;
private static final BlockingQueue<String> LOG_LIST = new ArrayBlockingQueue<>(256);
@ -123,6 +124,8 @@ public class Logger {
}
private static String generateMsg(String levelStr, String msg, Object... params) {
if(LOGGER_STYLE==0)
return formatMsg(msg+ LINE_SEPARATOR, null, params);
StackTraceElement stack = Thread.currentThread().getStackTrace()[4];
String s = "{} [{}][{}#{} {}] - " + msg + LINE_SEPARATOR;
Object[] args = new Object[5 + params.length];

View File

@ -29,7 +29,7 @@ public class Tree8Util {
public static int[][] TREE8 = new int[NODE_QTY][4]; //八叉数用数组表示第一维是深度树的行号第二维是一个整数数组,内容是深度树表示的八叉树细胞的size, x, y, z值
public static byte[] keep = new byte[NODE_QTY]; //这里临时记录树的敲除记录大于等于0的值表示要keep, 负数节点表示要敲除
public static byte[] keep = new byte[NODE_QTY]; //这里临时记录树的敲除记录大于0的值表示要keep, 小于等于0表示要敲除
private static byte[] KEEP = new byte[NODE_QTY]; //这里保存初值为0的数组常量可以用System.arraycopy(KEEP, 0, keep, 0, NODE_QTY)快速清空enable数组
@ -64,8 +64,8 @@ public class Tree8Util {
public static void knockNodesByGene(List<Integer> gene) {//根据基因把要敲除的8叉树节点作敲除或保留标记
System.arraycopy(KEEP, 0, keep, 0, NODE_QTY);//清空keep数组
keepNodeQTY = NODE_QTY;
for (int g : gene) {//g基因用带符号的8叉数的行号表示负数表示阴节点要敲除0或正数表示是阳节点要保留
keepNodeQTY = 0;
for (int g : gene) {//g基因用带符号的8叉数的行号表示负数表示阴节点要敲除正数表示是阳节点要保留
int gLine = Math.abs(g); //基因对应节点的行号
int size = Tree8Util.TREE8[gLine][0]; //size是基因对应节点的细胞立方体边长
for (int line = gLine; line < Tree8Util.NODE_QTY; line++) {//从这个g节点开始往下找节点
@ -73,13 +73,13 @@ public class Tree8Util {
break;
else {//否则就是g的子节点
if (g < 0) { //g是阴节点
Tree8Util.keep[line]--;
if (Tree8Util.keep[line] == -1) //如果正好是-1表示这个节点从保留状态转为删除状态
if (Tree8Util.keep[line] == 1) //如果是1表示这个节点将从保留状态转为删除状态
keepNodeQTY--;
Tree8Util.keep[line]=0;
} else if (g > 0) { //g是阳节点
Tree8Util.keep[line]++;
if (Tree8Util.keep[line] == 0) //如果正好是0表示这个节点从删除状态又转回了保留状态
if (Tree8Util.keep[line] == 0) //如果是0表示这个节点将从删除状态转为保留状态
keepNodeQTY++;
Tree8Util.keep[line]=1;
}
}
}

View File

@ -159,7 +159,7 @@ public abstract class Animal {// 这个程序大量用到public变量而不是ge
int randomIndex = RandomUtils.nextInt(Tree8Util.keepNodeQTY);
int count = -1;
for (int i = 0; i < Tree8Util.NODE_QTY; i++) {
if (Tree8Util.keep[i] >= 0) {
if (Tree8Util.keep[i] > 0) {
count++;
if (count >= randomIndex && !gene.contains(-i)) {
gene.add(-i);
@ -171,14 +171,14 @@ public abstract class Animal {// 这个程序大量用到public变量而不是ge
}
for (int g = 0; g < GENE_NUMBERS; g++) {//随机新增阳节点基因
if (RandomUtils.percent(5)) {
if (RandomUtils.percent(10)) {
ArrayList<Integer> 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) {
if (Tree8Util.keep[i] <= 0) {
count++;
if (count >= randomIndex && !gene.contains(i)) {
gene.add(i);
@ -189,27 +189,6 @@ public abstract class Animal {// 这个程序大量用到public变量而不是ge
}
}
// for (int g = 0; g < GENE_NUMBERS; g++) {//随机变异将阳节点向上提升一级相当于单个细胞的自底向上扩散式生长
// if (RandomUtils.percent(3)) {
// ArrayList<Integer> gene = genes.get(g);
// int randomIndex = RandomUtils.nextInt(gene.size());
// if (randomIndex > 0 && gene.get(randomIndex) > 0) {//如基因是阳基因且节点不是顶节点
// int size = Tree8Util.TREE8[randomIndex][0];
// gene.remove(randomIndex); //先删除底层这个阳基因
// for (int i = randomIndex - 1; i > 0; i--) {
// if (Tree8Util.TREE8[i][0] > size) { //深度树只要大于size就是它的父节点
// if (!gene.contains(i))
// gene.add(i);
// int x = gene.indexOf(-i);//如果有阴节点也删除
// if (x > 0)
// gene.remove(x);
// break;
// }
// }
// }
// }
// }
for (int g = 0; g < GENE_NUMBERS; g++) {//随机变异删除一个基因这样可以去除无用的拉圾基因防止基因无限增大
if (RandomUtils.percent(10)) {
ArrayList<Integer> gene = genes.get(g);
@ -225,7 +204,7 @@ public abstract class Animal {// 这个程序大量用到public变量而不是ge
ArrayList<Integer> gene = genes.get(g);
Tree8Util.knockNodesByGene(gene);//根据基因把要敲除的8叉树节点作个标记
for (int i = 0; i < Tree8Util.NODE_QTY; i++) {//再根据敲剩下的8叉树keep标记生成细胞参数
if (Tree8Util.keep[i] >= 0) {
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

View File

@ -319,7 +319,7 @@ public class BrainPicture extends JPanel {
ArrayList<Integer> 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]) {//如果允许显示的话, 显示当前层级的节点

View File

@ -18,7 +18,7 @@ package com.gitee.drinkjava2.frog.brain;
*/
@SuppressWarnings("all")
public class Cells {
public static int GENE_NUMBERS = 1; //目前有多少条基因每个脑细胞用是一个long来存储所以最多允许64条基因每个基因控制一个细胞的参数
public static int GENE_NUMBERS = 4; //目前有多少条基因每个脑细胞用是一个long来存储所以最多允许64条基因每个基因控制一个细胞的参数
}

View File

@ -50,7 +50,7 @@ public class FrogEggTool {
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);
Logger.info("Fist frog energy={}, gene size={}, Last frog energy={}", first.energy, getGeneSize(first), last.energy);
if (Env.SAVE_EGGS_FILE) {
FileOutputStream fo = new FileOutputStream(Application.CLASSPATH + "frog_eggs.ser");
ObjectOutputStream so = new ObjectOutputStream(fo);
@ -63,6 +63,16 @@ public class FrogEggTool {
}
}
private static String getGeneSize(Frog f) {//按genes类型汇总每种基因的个数
StringBuilder sb=new StringBuilder("[");
for (int i = 0; i < f.genes.size(); i++)
sb.append(f.genes.get(i).size()).append(",");
if(sb.length()>1)
sb.setLength(sb.length()-1);
sb.append("]");
return sb.toString();
}
private static void sortFrogsOrderByEnergyDesc() {// 按能量多少给青蛙排序
Collections.sort(Env.frogs, new Comparator<Animal>() {
public int compare(Animal a, Animal b) {

View File

@ -29,7 +29,7 @@ public class Tree8Util {
public static int[][] TREE8 = new int[NODE_QTY][4]; //八叉数用数组表示第一维是深度树的行号第二维是一个整数数组,内容是深度树表示的八叉树细胞的size, x, y, z值
public static byte[] keep = new byte[NODE_QTY]; //这里临时记录树的敲除记录大于等于0的值表示要keep, 负数节点表示要敲除
public static byte[] keep = new byte[NODE_QTY]; //这里临时记录树的敲除记录大于0的值表示要keep, 小于等于0表示要敲除
private static byte[] KEEP = new byte[NODE_QTY]; //这里保存初值为0的数组常量可以用System.arraycopy(KEEP, 0, keep, 0, NODE_QTY)快速清空enable数组
@ -64,8 +64,8 @@ public class Tree8Util {
public static void knockNodesByGene(List<Integer> gene) {//根据基因把要敲除的8叉树节点作敲除或保留标记
System.arraycopy(KEEP, 0, keep, 0, NODE_QTY);//清空keep数组
keepNodeQTY = NODE_QTY;
for (int g : gene) {//g基因用带符号的8叉数的行号表示负数表示阴节点要敲除0或正数表示是阳节点要保留
keepNodeQTY = 0;
for (int g : gene) {//g基因用带符号的8叉数的行号表示负数表示阴节点要敲除正数表示是阳节点要保留
int gLine = Math.abs(g); //基因对应节点的行号
int size = Tree8Util.TREE8[gLine][0]; //size是基因对应节点的细胞立方体边长
for (int line = gLine; line < Tree8Util.NODE_QTY; line++) {//从这个g节点开始往下找节点
@ -73,13 +73,13 @@ public class Tree8Util {
break;
else {//否则就是g的子节点
if (g < 0) { //g是阴节点
Tree8Util.keep[line]--;
if (Tree8Util.keep[line] == -1) //如果正好是-1表示这个节点从保留状态转为删除状态
if (Tree8Util.keep[line] == 1) //如果是1表示这个节点将从保留状态转为删除状态
keepNodeQTY--;
Tree8Util.keep[line]=0;
} else if (g > 0) { //g是阳节点
Tree8Util.keep[line]++;
if (Tree8Util.keep[line] == 0) //如果正好是0表示这个节点从删除状态又转回了保留状态
if (Tree8Util.keep[line] == 0) //如果是0表示这个节点将从删除状态转为保留状态
keepNodeQTY++;
Tree8Util.keep[line]=1;
}
}
}

View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,11 @@
## 2022-09-25 阴阳无极八卦阵在初级炼丹法中的运用
位于history\011目录这是利用阴阳无极八卦阵算法来完成最开始的“找食”这个任务即采用模拟细胞分裂再配合遗传算法生成一个简单功能的神经网络输入只有上下左右四种视觉细胞输出只有上下左右四种运动细胞。
为什么要使用阴阳无极八卦阵来生成神经网络,是因为它有几个优点:
1.传统神经网络因为手工调优参数太多,如同炼丹一样复杂,常被类比为”炼丹法“,而采用阴阳无极八卦阵之后,调参这个工作可以完全交给计算机,手工只需要设计基因和细胞行为即可,以不变应万变,算法今后将不再是重点。脑结构交给计算机去生成,只要有利于生存的脑结构,就会被筛选出来,结构即逻辑。
2.有高效的信息存储方式(见先前8叉树的介绍),基因虽然是随机生成的,但是它采用树结构这种方式是高度压缩的信息存储方式,可以用少量的基因控制巨量细胞的生成。
3.更贴近实际生物的神经网络生成模式,大自然就是采用分裂+遗传算法来生成脑神经网络的,理论上我们可以照抄这个模式来创造出高度智能的神经网络。
4.它的神经网络是三维的,没有传统的手工设计算法神经网格层数限制。同一个细胞可以有许多个基因同时控制,一个基因可能控制零到所有的细胞,组合方式是无穷的。
运行时最上面一层脑细胞用4种颜色来表示四个方向的眼睛细胞其余的细胞参数如运动细胞、轴突方向、轴突长度等用不同大小、颜色的圆来表示
这个版本的目的是验证阴阳无极八卦阵算法是否能生成简单神经网络。今后的任务将是进一步复杂化,利用阴阳无极八卦阵来生成有模式识别功能的神经网络三维结构。

View File

@ -0,0 +1 @@
mvn clean

View File

@ -0,0 +1 @@
mvn eclipse:clean

View File

@ -0,0 +1,2 @@
call mvn eclipse:eclipse
call pause

View File

@ -0,0 +1,102 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.gitee.drinkjava2</groupId>
<artifactId>frog011</artifactId>
<packaging>jar</packaging>
<version>11.0</version>
<name>frog</name>
<description>当前目标是由分裂算法来自动排列脑细胞和触突参数,以实现模式识别功能</description>
<url>https://gitee.com/drinkjava2/jsqlbox/frog</url>
<issueManagement>
<system>gitee Issue</system>
<url>https://gitee.com/drinkjava2/jsqlbox/frog/issues</url>
</issueManagement>
<licenses>
<license>
<name>The Apache Software License, Version 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
</license>
</licenses>
<developers>
<developer>
<name>Yong Zhu</name>
<email>yong9981@gmail.com</email>
<url>https://gitee.com/drinkjava2/</url>
</developer>
</developers>
<scm>
<connection>scm:git@gitee.com:drinkjava2/frog.git</connection>
<developerConnection>scm:git@gitee.com:drinkjava2/frog.git</developerConnection>
<url>git@gitee.com:drinkjava2/frog.git</url>
</scm>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
<!-- Release on Java8 -->
<version.java>1.8</version.java>
<version.javadoc>6</version.javadoc>
<version.compiler-plugin>3.3</version.compiler-plugin>
<version.war-plugin>2.6</version.war-plugin>
<version.clean-plugin>3.0.0</version.clean-plugin>
<version.resources-plugin>2.7</version.resources-plugin>
<version.surefire-plugin>2.19</version.surefire-plugin>
<version.jar-plugin>2.6</version.jar-plugin>
<version.source-plugin>2.4</version.source-plugin>
<version.javadoc-plugin>2.10.3</version.javadoc-plugin>
<version.gpg-plugin>1.6</version.gpg-plugin>
</properties>
<dependencies>
<!--dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency-->
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${version.compiler-plugin}</version>
<configuration>
<source>${version.java}</source>
<target>${version.java}</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.1.2</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<useUniqueVersions>false</useUniqueVersions>
<classpathPrefix>lib/</classpathPrefix>
<mainClass>com.gitee.drinkjava2.frog.Application</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
</profiles>
</project>

View File

@ -0,0 +1,3 @@
call mvn clean compile
cd target\classes
java -classpath ".;*" com.gitee.drinkjava2.frog.Application

View File

@ -0,0 +1,2 @@
mvn clean package
java -jar target/frog-*.jar

View File

@ -0,0 +1,229 @@
/*
* 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 static com.gitee.drinkjava2.frog.util.RandomUtils.percent;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.FileInputStream;
import java.util.ArrayList;
import javax.imageio.ImageIO;
import com.gitee.drinkjava2.frog.brain.Cells;
import com.gitee.drinkjava2.frog.brain.Eye;
import com.gitee.drinkjava2.frog.egg.Egg;
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>结构, 每一条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<ArrayList<Integer>> genes = new ArrayList<>(); // 基因是多个数列有点象多条染色体
static {
try {
FROG_IMAGE = ImageIO.read(new FileInputStream(Application.CLASSPATH + "frog.png"));
} catch (Exception e) {
e.printStackTrace();
}
}
/** brain cells暂定一个空间只有一个细胞以后可以考虑扩展为一个空间充许多个细胞用多个三维数组表示分别由各自的基因控制分裂 */
public long[][][] cells = new long[Env.BRAIN_CUBE_SIZE][Env.BRAIN_CUBE_SIZE][Env.BRAIN_CUBE_SIZE];
public float[][][] energys = new float[Env.BRAIN_CUBE_SIZE][Env.BRAIN_CUBE_SIZE][Env.BRAIN_CUBE_SIZE];
public int x; // animal在Env中的x坐标
public int y; // animal在Env中的y坐标
public long energy = 1000000000; // 青蛙的能量为0则死掉
public boolean alive = true; // 设为false表示青蛙死掉了将不参与计算和显示以节省时间
public int ateFood = 0; // 青蛙曾吃过的食物总数下蛋时如果两个青蛙能量相等可以比数量
public int no; // 青蛙在Env.animals中的序号从1开始 会在运行期写到当前brick的最低位可利用Env.animals.get(no-1)快速定位青蛙
public int animalMaterial;
public Image animalImage;
public Animal(Egg egg) {// x, y 是虑拟环境的坐标
for (int i = 0; i < GENE_NUMBERS; i++) {
genes.add(new ArrayList<>());
}
int i = 0;
for (ArrayList<Integer> 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(); //有小概率基因突变
createCellsFromGene(); //根据基因分裂生成脑细胞
}
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(10);}
public void awardAA() { changeEnergy(5);}
public void awardA() { changeEnergy(2);}
public void penaltyAAAA() { changeEnergy(-2000);}
public void penaltyAAA() { changeEnergy(-10);}
public void penaltyAA() { changeEnergy(-5);}
public void penaltyA() { changeEnergy(-2);}
public void kill() { this.alive = false; changeEnergy(-5000000); Env.clearMaterial(x, y, animalMaterial); } //kill是最大的惩罚
//@formatter:on
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;
}
this.energys[0][0][Env.BRAIN_ZSIZE - 1] = 10;
// if(Env.closeToEdge(this))
// energys[0][0][0]=10;
Eye.active(this); //如看到食物给顶层细胞赋能量
Cells.active(this); //细胞之间互相传递能量
if (Food.foundAndAteFood(this.x, this.y)) { //如当前位置有食物就吃掉并获得奖励
this.awardAAAA();
this.ateFood++;
}
return alive;
}
public void show(Graphics g) {// 显示当前动物
if (!alive)
return;
g.drawImage(animalImage, x - 8, y - 8, 16, 16, null);// 减去坐标保证嘴巴显示在当前x,y处
}
/** Check if x,y,z out of animal's brain range */
public static boolean outBrainRange(int x, int y, int z) {// 检查指定坐标是否超出animal脑空间界限
return x < 0 || x >= Env.BRAIN_XSIZE || y < 0 || y >= Env.BRAIN_YSIZE || z < 0 || z >= Env.BRAIN_ZSIZE;
}
public void geneMutation() { //基因变异,注意这一个算法同时变异所有条基因目前最多允许64条基因
if(percent(50))
for (int g = 0; g < GENE_NUMBERS; g++) {//随机新增阴节点基因
if (percent(20)) {
ArrayList<Integer> 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;
}
}
}
}
}
if(percent(50))
for (int g = 0; g < GENE_NUMBERS; g++) {//随机新增阳节点基因
if (RandomUtils.percent(20)) {
ArrayList<Integer> 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;
}
}
}
}
}
if(percent(50))
for (int g = 0; g < GENE_NUMBERS; g++) {//随机变异删除一个基因这样可以去除无用的拉圾基因防止基因无限增大
if (RandomUtils.percent(40)) {
ArrayList<Integer> 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<Integer> 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;
}
}
}

View File

@ -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(Cells.display_gene[i]);
geneRadio.setName(""+i);
ActionListener geneRadioAction = new ActionListener() {
public void actionPerformed(ActionEvent e) {
int i= Integer.parseInt(geneRadio.getName());
if (geneRadio.isSelected())
Cells.display_gene[i]=true;
else
Cells.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();
}
}

View File

@ -0,0 +1,308 @@
package com.gitee.drinkjava2.frog;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JPanel;
import com.gitee.drinkjava2.frog.brain.Cells;
import com.gitee.drinkjava2.frog.egg.Egg;
import com.gitee.drinkjava2.frog.egg.FrogEggTool;
import com.gitee.drinkjava2.frog.objects.EnvObject;
import com.gitee.drinkjava2.frog.objects.Food;
import com.gitee.drinkjava2.frog.objects.Material;
import com.gitee.drinkjava2.frog.util.Logger;
import com.gitee.drinkjava2.frog.util.RandomUtils;
/**
* Env is the living space of frog. draw it on JPanel
*
* @author Yong Zhu
* @since 1.0
*/
@SuppressWarnings("all")
public class Env extends JPanel {
private static final long serialVersionUID = 1L;
/** Speed of test */
public static int SHOW_SPEED = 1000; // 测试速度-1000~1000,可调, 数值越小速度越慢
public static final int FROG_EGG_QTY = 100; // 每轮下n个青蛙蛋可调只有最优秀的前n个青蛙们才允许下蛋
public static final int FROG_PER_EGG = 6; // 每个青蛙蛋可以孵出几个青蛙
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 = 4; //脑立方边长大小必须是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 = 200;// 每轮测试步数,可调
public static int step;// 当前测试步数
public static final int FOOD_QTY = 3000; // 食物数量, 可调
// 以下是程序内部变量不要手工修改它们
public static final int TOTAL_FROG_QTY = FROG_EGG_QTY * FROG_PER_EGG; // 蛇的总数
public static final int FROG_PER_SCREEN = TOTAL_FROG_QTY / SCREEN; // 每屏显示几个青蛙这个数值由其它常量计算得来
public static int current_screen = 0;
public static boolean pause = false; // 暂停按钮按下将暂停测试
public static int[][] bricks = new int[ENV_WIDTH][ENV_HEIGHT];// 组成环境的材料见Material.java
public static List<Frog> frogs = new ArrayList<>(); // 这里存放所有待测的青蛙可能分几次测完由FROG_PER_SCREEN大小来决定
public static List<Egg> frog_eggs = new ArrayList<>(); // 这里存放新建或从磁盘载入上轮下的蛋每个蛋可能生成几个青蛙
public static EnvObject[] things = new EnvObject[]{Food.FOOD};// 所有外界物体如食物字母测试工具都放在这个things里面
public static boolean show_split_detail = false; //是否显示脑分裂的细节过程即从一个细胞开始分裂分裂而不是只显示分裂的最终结果
static {
Logger.info("唵缚悉波罗摩尼莎诃!"); // 杀生前先打印往生咒见码云issue#IW4H8
Logger.info("脑图快捷键: T:顶视 F前视 L:左视 R:右视 X:斜视 方向键:剖视 空格:暂停 鼠标:缩放旋转平移");
if (DELETE_FROG_EGGS)
FrogEggTool.deleteEggs();
}
public Env() {
super();
this.setLayout(null);// 空布局
this.setBounds(1, 1, ENV_WIDTH, ENV_HEIGHT);
}
public static boolean insideBrain(int x, int y) {// 如果指定点在边界内
return !(x < 0 || y < 0 || x >= BRAIN_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;
if (SHOW_SPEED<30) // 如果speed为1人为加入延迟
sleep((30-SHOW_SPEED));
// 开始画虚拟环境和青蛙和蛇
g.setColor(Color.white);
g.fillRect(0, 0, this.getWidth(), this.getHeight()); // 先清空虚拟环境
g.setColor(Color.BLACK);
drawWorld(g);// 画整个虚拟环境
for (int j = 0; j < FROG_PER_SCREEN; j++) { // 显示青蛙
Frog f = frogs.get(current_screen * FROG_PER_SCREEN + j);
f.show(g);
}
if (SHOW_FIRST_ANIMAL_BRAIN) {// 在showAnimal上画一个红圈
Animal showAnimal = getShowAnimal();
if (showAnimal != null) {
g.setColor(Color.red);
g.drawArc(showAnimal.x - 15, showAnimal.y - 15, 30, 30, 0, 360);
g.setColor(Color.BLACK);
}
}
if (DRAW_BRAIN_AFTER_STEPS > 0 && step % DRAW_BRAIN_AFTER_STEPS == 0)
Application.brainPic.drawBrainPicture();
Graphics g2 = this.getGraphics();
g2.drawImage(buffImg, 0, 0, this);
}
if (SHOW_FIRST_ANIMAL_BRAIN)
Application.brainPic.drawBrainPicture();
checkIfPause();
for (int j = 0; j < FROG_PER_SCREEN; j++) {
Frog f = frogs.get(current_screen * FROG_PER_SCREEN + j);
}
StringBuilder sb = new StringBuilder("Round: ");
sb.append(round).append(", screen:").append(current_screen).append(", speed:").append(Env.SHOW_SPEED).append(", ").append(", 用时: ").append(System.currentTimeMillis() - time0)
.append("ms, ");
sb.append(foodAtedCount());
Application.mainFrame.setTitle(sb.toString());
for (EnvObject thing : things)// 去除食物陷阱等物体
thing.destory();
}
round++;
FrogEggTool.layEggs(); //能量高的青蛙才有权下蛋
} while (true);
}
}

View File

@ -8,25 +8,22 @@
* 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;
package com.gitee.drinkjava2.frog;
import com.gitee.drinkjava2.frog.egg.Egg;
import com.gitee.drinkjava2.frog.objects.Material;
/**
* Cube has x,y,z value
*
* @author Yong Zhu
* @since 2021-11-07
* Frog is child class of Animal, Frog's name is Sam
* Frog是Animal的一个子类
*
* @since 1.0
*/
public class Cube {
public int x;//x,y,z是cube的左下前点坐标
public int y;
public int z;
public int size;//size是cube的边长
public class Frog extends Animal {
public Cube(int x, int y, int z, int size) {
this.x = x;
this.y = y;
this.z = z;
this.size = size;
public Frog(Egg egg) {
super(egg);
animalMaterial = Material.FROG_TAG;
animalImage = Animal.FROG_IMAGE;
}
}

View File

@ -0,0 +1,475 @@
package com.gitee.drinkjava2.frog.brain;
import static java.awt.Color.BLACK;
import static com.gitee.drinkjava2.frog.brain.Cells.*;
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维结构进行有针对性的改进
* 可以用鼠标进行平移缩放旋转以及tflr,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,rx键盘命令
@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<Integer> 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 && Cells.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--) {
long cell = a.cells[x][y][z];
if ((cell & Cells.EXIST) == 0) //只显示有效的细胞点
continue;
if (a.energys[x][y][z] > 1) { //用大红色圆形画出能量大于1的细胞格
setPicColor(Color.MAGENTA); //开始画出对应的细胞基因参数用不同颜色直径圆表示
drawCircle(x + 0.5f, y + 0.5f, z + 0.5f, 1.2f);
}
if (x >= xMask && y >= yMask && cell != 0)//画出细胞每个基因存在的细胞格子
for (int geneIndex = 0; geneIndex < Cells.GENE_NUMBERS; geneIndex++) {
if ((cell & (1 << geneIndex)) != 0 && Cells.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);
}
}
if (((cell & ZT) > 0)) { //如有轴突基因则从当前细胞到轴突端点画一条红线
int x_ = (int) ((cell & ZTX) >> ZTX0);
if (x_ == 0)
x_ = -1;
int y_ = (int) ((cell & ZTY) >> ZTY0);
if (y_ == 0)
y_ = -1;
int z_ = (int) ((cell & ZTZ) >> ZTZ0);
if (z_ == 0)
z_ = -1;
int zt_long = (int) ((cell & ZT_LONG) >> ZT_LONG0) + 1; //轴突长度, 大小为1~8
int xx = x + x_ * zt_long;
int yy = y + y_ * zt_long;
int zz = z + z_ * zt_long;
if (Env.insideBrain(xx, yy, zz)) {
setPicColor(Color.RED);
drawLine(x+.5f, y+.5f, z+.5f, xx+.5f, yy+.5f, zz+.5f);
}
}
}
}
}
Eye.drawEye(this);//画眼睛
setPicColor(Color.BLACK);
drawCuboid(brain);// 把脑的框架画出来
setPicColor(BLACK); //把x,y,z坐标画出来
drawText(Env.BRAIN_CUBE_SIZE, 0, 0, "x", 2);
drawText(0, Env.BRAIN_CUBE_SIZE, 0, "y", 2);
drawText(0, 0, Env.BRAIN_CUBE_SIZE, "z", 2);
setPicColor(RED);
drawLine(0, 0, 0, Env.BRAIN_CUBE_SIZE, 0, 0);
drawLine(0, 0, 0, 0, Env.BRAIN_CUBE_SIZE, 0);
drawLine(0, 0, 0, 0, 0, Env.BRAIN_CUBE_SIZE);
g.setColor(Color.black);
if (note != null) // 全局注释
g.drawString(note, 30, 55);
this.getGraphics().drawImage(buffImg, 0, 0, this);// 利用缓存避免画面闪烁这里输出缓存图片
}
public static void setNote(String note) {
Application.brainPic.note = note;
}
// getters & setters
public float getScale() {
return scale;
}
public void setScale(float scale) {
this.scale = scale;
}
public float getxAngle() {
return xAngle;
}
public void setxAngle(float xAngle) {
this.xAngle = xAngle;
}
public float getyAngle() {
return yAngle;
}
public void setyAngle(float yAngle) {
this.yAngle = yAngle;
}
public float getzAngle() {
return zAngle;
}
public void setzAngle(float zAngle) {
this.zAngle = zAngle;
}
public void setPicColor(Color color) {
this.picColor = color;
}
public Color getPicColor() {
return picColor;
}
public int getxOffset() {
return xOffset;
}
public void setxOffset(int xOffset) {
this.xOffset = xOffset;
}
public int getyOffset() {
return yOffset;
}
public void setyOffset(int yOffset) {
this.yOffset = yOffset;
}
}

View File

@ -0,0 +1,140 @@
/*
* Copyright 2018 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
* applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
* OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*/
package com.gitee.drinkjava2.frog.brain;
import com.gitee.drinkjava2.frog.Animal;
import com.gitee.drinkjava2.frog.Env;
import com.gitee.drinkjava2.frog.util.Logger;
import com.gitee.drinkjava2.frog.util.RandomUtils;
/**
* Cells代表不同的脑细胞参数对应每个参数用8叉树算法生成不同的细胞
* 每个脑细胞用一个long来存储所以最多允许64个基因位, 有时一个参数由多个基因位决定每个基因位都由一个单独的阴阳8叉树控制多个基因就组成了一个8叉树阵列
* 基因+分裂算法=结构
* 基因+分裂算法+遗传算法=结构的进化
* 这个类里定义每个基因位的掩码, 脑结构的所有参数都要用基因来控制开始时可以有常量魔数但以后都放到基因里去自动进化
*
*
* @author Yong Zhu
* @since 10.0
*/
@SuppressWarnings("all")
public class Cells {
public static int GENE_NUMBERS = 0;
private static int zeros = 0;
public static boolean[] display_gene = new boolean[64]; //脑最多有64个基因这里用来控制哪些基因需要显示在脑图上
public static long EXIST = mask(1, true); // 细胞存在否,1为存在,0为不存在, true表示显示在脑图上
public static long MOVE_UP = mask(1, true); //如此点为1则此细胞如有能量青蛙向上移动
public static long MOVE_DOWN = mask(1, true); //如此点为1则此细胞如有能量青蛙向下移动
public static long MOVE_LEFT = mask(1, true); //如此点为1则此细胞如有能量青蛙向左移动
public static long MOVE_RIGHT = mask(1, true); //如此点为1则此细胞如有能量青蛙向右移动
public static long ZT_LONG = mask(3, false); //轴突长度
public static long ZT_LONG0 = zeros; //如果参数由多位组成用同名+0变量表示有几个0移位运算时用来去除0下同
public static long ZT = mask(1, true);//axon exist 轴突是否存在
public static long ZTX = mask(1, false);//axon x offset, 轴突x方向, 轴突方向由x,y,z三个方向的参数组合决定
public static long ZTX0 = zeros; //x方向暂定1位如果2位也可以角度更细但出结果的时间会比较长
public static long ZTY = mask(1, false); //轴突y方向
public static long ZTY0 = zeros;
public static long ZTZ = mask(1, false); //轴突z方向
public static long ZTZ0 = zeros;
public static long ZT_MINUS = mask(1, false);//细胞信号,1为正信号,0为负(抑制)信号
// public static long RANDOM_ACTIVE = mask(1, true);//这个基因控制细胞随机产生能量对结果影响不大会让静止的青蛙颤抖
// public static long ST_LONG = mask(2, false); //dendrite radius, 树突长度半径待后继版本用到
// public static long ST_LONG0 = zeros;
// public static long HAPPY = mask(1, false); //吃食奖励信号待后继版本用到
public static long mask(int n, boolean display) { //返回基因掩码高位由n个1组成低位是当前GENE_NUMBERS个0这个方法执行后GENE_NUMBERS会加n
for (int i = GENE_NUMBERS; i < GENE_NUMBERS + n; i++) {
display_gene[i] = display;
}
String one = "";
String zero = "";
for (int i = 1; i <= n; i++)
one += "1";
for (int i = 1; i <= GENE_NUMBERS; i++)
zero += "0";
zeros = GENE_NUMBERS;
GENE_NUMBERS += n;
if (GENE_NUMBERS >= 64) {//
System.out.println("目前基因位数不能超过64");
System.exit(-1);
}
return Long.parseLong(one + zero, 2); //"111000"这种字符串转换为长整
}
public static void active(Animal a) {//这个方法的功能是根据细胞的参数在细胞间传输能量即信息的载体)
for (int z = Env.BRAIN_CUBE_SIZE - 1; z >= 0; z--)
for (int y = Env.BRAIN_CUBE_SIZE - 1; y >= 0; y--)
for (int x = Env.BRAIN_CUBE_SIZE - 1; x >= 0; x--) {
long cell = a.cells[x][y][z];
if ((cell & EXIST) == 0) //如细胞不存在
continue;
// if ((cell & RANDOM_ACTIVE) > 0) //随机产生细胞能量会让青蛙颤抖
// if (RandomUtils.percent(5))
// a.energys[x][y][z] = 1;
float e = a.energys[x][y][z];
if (e > 0 && ((cell & ZT) > 0)) { //如有轴突基因则当前细胞如存在能量会输送到轴突端点处
int x_ = (int) ((cell & ZTX) >> ZTX0); //把掩码转为坐标偏移量因为暂定只有1位所以只有1,-1
if (x_ == 0)
x_ = -1;
int y_ = (int) ((cell & ZTY) >> ZTY0);
if (y_ == 0)
y_ = -1;
int z_ = (int) ((cell & ZTZ) >> ZTZ0);
if (z_ == 0)
z_ = -1;
int zt_long = (int) ((cell & ZT_LONG) >> ZT_LONG0) + 1; //轴突长度, 大小为1~8
int xx = x + x_ * zt_long;
int yy = y + y_ * zt_long;
int zz = z + z_ * zt_long;
if (Env.insideBrain(xx, yy, zz)) {
if (a.energys[xx][yy][zz] < 10) {
if ((cell & ZT_MINUS) > 0) //如果轴空是负信号
a.energys[xx][yy][zz]--;
else
a.energys[xx][yy][zz]++;
if (a.energys[x][y][z] > 0)
a.energys[x][y][z]--;
}
}
}
e = a.energys[x][y][z];
if ((e > 0) && (z < Env.BRAIN_ZSIZE - 1)) { //如当前细胞有能量且不和眼睛在同一层且有移动细胞则青蛙移动
if ((cell & MOVE_UP) > 0) {//向上y是减屏幕y的0点在上方
a.energys[x][y][z] = 0;
a.y++;
}
if ((cell & MOVE_DOWN) > 0) {
a.energys[x][y][z] = 0;
a.y--;
}
if ((cell & MOVE_LEFT) > 0) {
a.energys[x][y][z] = 0;
a.x--;
}
if ((cell & MOVE_RIGHT) > 0) {
a.energys[x][y][z] = 0;
a.x++;
}
}
}
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,76 @@
/*
* Copyright 2018 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
* applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
* OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*/
package com.gitee.drinkjava2.frog.brain;
import java.awt.Color;
import com.gitee.drinkjava2.frog.Animal;
import com.gitee.drinkjava2.frog.Env;
import com.gitee.drinkjava2.frog.objects.Material;
/**
* Eye, if found food then set cell energy at top level
*
* @author Yong Zhu
* @since 1.0
*/
public class Eye {
/** First eye can only see if food nearby at 4 directions */
public static void active(Animal a) {// 眼睛只能观察上右四个方向有没有食物如发现食物就将最上层的四个边的细胞激活成1能量
int seeDist = 15; //眼睛能看多远
for (int i = 1; i < seeDist; i++)
if (Env.hasMaterial(a.x, a.y + i, Material.FOOD)) {//up
for (int x = 1; x < Env.BRAIN_XSIZE; x++)
a.energys[x][Env.BRAIN_YSIZE - 1][Env.BRAIN_ZSIZE - 1] = 1;
break;
}
for (int i = 1; i < seeDist; i++)
if (Env.hasMaterial(a.x, a.y - i, Material.FOOD)) { //down
for (int x = 0; x < Env.BRAIN_XSIZE - 1; x++)
a.energys[x][0][Env.BRAIN_ZSIZE - 1] = 1;
break;
}
for (int i = 1; i < seeDist; i++)
if (Env.hasMaterial(a.x - i, a.y, Material.FOOD)) { //left
for (int y = 1; y < Env.BRAIN_YSIZE; y++)
a.energys[0][y][Env.BRAIN_ZSIZE - 1] = 1;
break;
}
for (int i = 1; i < seeDist; i++)
if (Env.hasMaterial(a.x + i, a.y, Material.FOOD)) {//right
for (int y = 0; y < Env.BRAIN_YSIZE - 1; y++)
a.energys[Env.BRAIN_XSIZE - 1][y][Env.BRAIN_ZSIZE - 1] = 1;
break;
}
}
public static void drawEye(BrainPicture bp) {//在脑图中画出眼睛
bp.setPicColor(Color.RED);
for (int x = 1; x < Env.BRAIN_XSIZE; x++)
bp.drawCircle(x + 0.5f, Env.BRAIN_YSIZE - 0.5f, Env.BRAIN_ZSIZE - 0.5f, 1);
bp.setPicColor(Color.YELLOW);
for (int x = 0; x < Env.BRAIN_XSIZE - 1; x++)
bp.drawCircle(x + 0.5f, 0.5f, Env.BRAIN_ZSIZE - 0.5f, 1);
bp.setPicColor(Color.BLUE);
for (int y = 1; y < Env.BRAIN_YSIZE; y++)
bp.drawCircle(0.5f, y + 0.5f, Env.BRAIN_ZSIZE - 0.5f, 1);
bp.setPicColor(Color.GREEN);
for (int y = 0; y < Env.BRAIN_YSIZE - 1; y++)
bp.drawCircle(Env.BRAIN_XSIZE - 0.5f, y + 0.5f, Env.BRAIN_ZSIZE - 0.5f, 1);
}
}

View File

@ -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
}
}

View File

@ -0,0 +1,75 @@
/*
* Copyright 2018 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
* applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
* OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*/
package com.gitee.drinkjava2.frog.egg;
import java.io.Serializable;
import java.util.ArrayList;
import com.gitee.drinkjava2.frog.Animal;
import com.gitee.drinkjava2.frog.Env;
import com.gitee.drinkjava2.frog.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<ArrayList<Integer>> 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<Integer> gene : a.genes) {//下蛋就是把动物的基因拷贝到新蛋里并有可能变异
ArrayList<Integer> g = new ArrayList<>();
g.addAll(gene);
genes.add(g);
}
}
/**
* Create a egg by join 2 eggs, x+y=zygote 模拟XY 染色体合并两个蛋生成一个新的蛋
*/
public Egg(Egg a, Egg b) {//两个蛋的基因混合, 生成一个新蛋
x = a.x;
y = a.y;
genes.addAll(a.genes);
if (RandomUtils.percent(20)) //插入蛋B的基因到A蛋中
for (int i = 0; i < b.genes.size(); i++) {
if (RandomUtils.percent(2)) {
ArrayList<Integer> aGene = a.genes.get(i);
ArrayList<Integer> bGene = b.genes.get(i);
if (bGene.size() > 1) {//随机插入一个B的基因不用担心基因越来越多因为随机删除的速度大于增长的
aGene.add(bGene.get(RandomUtils.nextInt(bGene.size())));
}
}
}
}
}

View File

@ -0,0 +1,122 @@
/*
* 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={}, gene size={}, Last frog energy={}", first.energy, getGeneSize(first), 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 String getGeneSize(Frog f) {//按genes类型汇总每种基因的个数
StringBuilder sb=new StringBuilder("[");
for (int i = 0; i < f.genes.size(); i++)
sb.append(f.genes.get(i).size()).append(",");
if(sb.length()>1)
sb.setLength(sb.length()-1);
sb.append("]");
return sb.toString();
}
private static void sortFrogsOrderByEnergyDesc() {// 按能量多少给青蛙排序
Collections.sort(Env.frogs, new Comparator<Animal>() {
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<Egg>) 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.");
}
}
}

View File

@ -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(); // 每个步长都会调用一次这个方法
}

View File

@ -0,0 +1,66 @@
/* 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 int food_ated = 0;
@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
}
}
}
@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);
}
}
@Override
public void active() {
//食物除了被吃它自己没有什么活动
}
public static boolean foundAndAteFood(int x, int y) {// 如果x,y有食物将其清0返回true
if (Env.hasMaterial(x, y, Material.FOOD)) {
food_ated++;
Env.clearMaterial(x, y, Material.FOOD);//在环境里清除FOOD
return true;
}
return false;
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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();
}
}
}
}
}

View File

@ -0,0 +1,196 @@
/*
* Copyright 2021 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
* applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
* OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*/
package com.gitee.drinkjava2.frog.util;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
/**
* Usually a logger tool is used like below:
* Logger log = LoggerFactory.getLogger(Xxxx.class);
* log.info("some msg");
*
* But to simplify, in this project directly use static method:
* Logger.info("some msg");
*
* @Description: 简版控制台日志打印从码云上合并来 https://gitee.com/drinkjava2/frog/pulls/4
* @author 栾成翔
* @Date: 2021/12/07
*/
@SuppressWarnings("all")
public class Logger {
private static final int LOGGER_STYLE = 0; //风格设定 0:不输出前缀, 1:输出时间行号等前缀
private static final String LEV_EL = "debug";
private static final int LEVEL_INT;
private static final BlockingQueue<String> 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<String, Integer> 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) {
if(LOGGER_STYLE==0)
return formatMsg(msg+ LINE_SEPARATOR, null, params);
StackTraceElement stack = Thread.currentThread().getStackTrace()[4];
String s = "{} [{}][{}#{} {}] - " + msg + LINE_SEPARATOR;
Object[] args = new Object[5 + params.length];
args[0] = FORMAT.format(System.currentTimeMillis());
args[1] = levelStr;
args[2] = stack.getClassName();
args[3] = stack.getMethodName();
args[4] = stack.getLineNumber();
Throwable throwable = null;
if (params.length > 0) {
final Object lastEntry = params[params.length - 1];
if (lastEntry instanceof Throwable) {
throwable = (Throwable) lastEntry;
System.arraycopy(params, 0, args, 5, params.length - 1);
} else {
System.arraycopy(params, 0, args, 5, params.length);
}
}
return formatMsg(s, throwable, args);
}
private static String formatMsg(String msg, Throwable throwable, Object... params) {
StringBuilder sb = new StringBuilder();
int s;
int i = 0;
for (Object o : params) {
s = msg.indexOf(DELIM_STR, i);
if (s > -1) {
sb.append(msg, i, s).append(o);
i = s + 2;
}
}
sb.append(msg, i, msg.length());
if (null != throwable) {
sb.append(throwable).append(LINE_SEPARATOR);
StackTraceElement[] stack = throwable.getStackTrace();
for (StackTraceElement element : stack) {
sb.append(TAB).append(element).append(LINE_SEPARATOR);
}
}
return sb.toString();
}
private static void outPutConsole(String msg) {
try {
OUTPUT_STREAM.write(msg.getBytes(StandardCharsets.UTF_8));
OUTPUT_STREAM.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
private static String getLevelStr(int levelInt) {
switch (levelInt) {
case 1:
return "DEBUG";
case 2:
return "INFO";
case 3:
return "WARN";
case 4:
return "ERROR";
default:
throw new IllegalStateException("Level " + levelInt + " is unknown.");
}
}
}

View File

@ -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;
}
}

View File

@ -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<String, byte[][]> 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("===============");
}
*/
}

View File

@ -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, 小于等于0表示要敲除
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<Integer> gene) {//根据基因把要敲除的8叉树节点作敲除或保留标记
System.arraycopy(KEEP, 0, keep, 0, NODE_QTY);//清空keep数组
keepNodeQTY = 0;
for (int g : gene) {//g基因用带符号的8叉数的行号表示负数表示阴节点要敲除正数表示是阳节点要保留
int gLine = Math.abs(g); //基因对应节点的行号
int size = Tree8Util.TREE8[gLine][0]; //size是基因对应节点的细胞立方体边长
for (int line = gLine; line < Tree8Util.NODE_QTY; line++) {//从这个g节点开始往下找节点
if (line > gLine && Tree8Util.TREE8[line][0] >= size) //如果除了第一个节点外边长大于等于size说明节点不是g的子节点退出
break;
else {//否则就是g的子节点
if (g < 0) { //g是阴节点
if (Tree8Util.keep[line] == 1) //如果是1表示这个节点将从保留状态转为删除状态
keepNodeQTY--;
Tree8Util.keep[line]=0;
} else if (g > 0) { //g是阳节点
if (Tree8Util.keep[line] == 0) //如果是0表示这个节点将从删除状态转为保留状态
keepNodeQTY++;
Tree8Util.keep[line]=1;
}
}
}
}
}
}

BIN
result18_yinyan_eatfood.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB