导语


“集智百科精选”是一个长期专栏,持续为大家推送复杂性科学相关的基本概念和资源信息。作为集智俱乐部的开源科学项目,集智百科希望打造复杂性科学领域最全面的百科全书,欢迎对复杂性科学感兴趣、热爱知识整理和分享的朋友加入,文末可以扫码报名加入百科志愿者!

↑↑↑扫码直达百科词条

图片

glider | 编译


目录

1. 人工生命Floy模拟程序

2. Floy模拟模型

2.1程序说明

2.2模型的规则

2.3模型的参数

3三维的Floy鸟群模拟模型

3.1程序说明

4相关wiki




1. 人工生命Floy模拟程序




根据Floy模拟模型制作的javascript程序:





2. Floy模拟模型




  • 名 称:人工生命
  • 作 者:Ariel Dolan
  • 源代码:文件:Floy.zip

2.1 程序说明


生命是什么?我们能不能造出生命来?看看上面的程序。


你看到了什么?一群苍蝇在飞舞?这是简简单单的动画么?这些家伙是“活”的么?我可以告诉你,这群飞舞的动物叫Floy,它既不是苍蝇也不是蝌蚪,而是人工生命!这就是一段简简单单的程序,而且代码的长度才有200多行!


单击Properties按钮,你可以改变这群Floy的属性,首先我们为了从简单开始把Floy的个数更改为1个看看,点击OK返回以后再按一下Continue按钮,一只Floy在围绕着中心转,也许你会说,这没什么,不就是一个运动的点么?如果更改Floy的个数为2个,那么这两只Floy就会互相追逐着在屏幕上飞行,注意两只Floy的行为就会产生混沌模式了,也就是它们的运动方式几乎是完全不确定的。逐渐增加到15只以上你就会看到一大群生物在那里乱飞了,这很像我们现实世界中的苍蝇行为。点击Strangers按钮,你会看到一只红色的Floy从屏幕的左上角飞进来,注意,它的到来可不寻常,它是一个外来的入侵者!本来相安无事的绿色Floy因为它的到来而变得行为异常起来,它们会突然转过头来进攻红色的入侵者,而红色的Floy会赶忙逃跑。继续点击Strangers按钮,就会有不断的入侵者加入进来,同时原有的Floy群体在减少。你会看到有两群苍蝇在那里追逐。绿色的Floy是本地居民,它会进攻入侵者最后将它杀死。如果幸运,你甚至会看到绿色的Floy们会使用诡计,它们先若无其事的在一起飞即使身边就有一只红色的入侵者,但是突然之间,它会转过头来进攻红色的,让它来不及逃跑。点击Default按钮可以恢复原状。


2.2 模型的规则


为什么这一群小点在这么简单的程序之下会有这样复杂的行为?它们是怎么实现的?注意,虽然这也是编程,但我们并不是按照传统的方法对所有Floy的集体行为在每一种可能的情况都编程,而是给每一个个体Floy制定简单的规则,这样Floy集体的复杂行为就会自然而然的涌现出来了!那么个体Floy的规则是什么呢?它是遵循下面的规律:

  • 首先找到和自己最近的几只(邻居数)的Floy作为自己的邻居;
  • 对于每个邻居如果它是自己的同类那么然后分别计算与这些邻居之间的距离;
    • 如果太近(距离小于碰撞距离)就朝向远离这个邻居的方向加速飞翔;
    • 如果较远(距离大于碰撞距离)就朝向靠近这个邻居的方向加速飞翔;
  • 如果邻居是自己的异类;
    • 如果这只Floy是绿色的则以非常快的加速度向对方进攻;
    • 如果这只Floy是红色的陌生者则当小于碰撞距离的时候以非常快的加速度逃离,并且自己的能量减小;当距离不是很大时,停止加速。


总体来说,Floy的规则就是选择自己的加速度,并且这种选择仅仅跟自己的邻居有关,也就是说规则都是局部的。而这个程序的关键是要体现整体行为的涌现,也就是说虽然微观上每个Floy个体的规则都是简单的局部的,但是在集体涌现出来的现象却是复杂的并且是不可预测的。人工生命的精髓就是:其实生命现象本身不过是由微观的简单相互作用规律涌现出来的集体复杂行为。


2.3 模型的参数


进一步,你可以探索更改参数对这群Floy的影响。你可以点击Behavior按钮,Floy的个性会改变,有温顺的也有疯狂的。它们的行为完全是由几个简单参数制约的,再次点击Properties按钮,那里面的参数制约了每个Floy个体的行为自然也就制约了Floy整体的涌现规律。其中各个参数的意义:加速度就是指每只苍蝇进行加速靠近或者远离的最小加速度值;中心吸引度是为了让Floy能够围绕屏幕中心旋转而让它们的加速度都有一个指向中心的分量;碰撞距离定义多少像素距离是叫做碰撞,从而Floy们选择远离还是靠近;最大速度是为了Floy们的行为不至于太疯狂也就是速度固定一个上限;反弹速度是当Floy撞击墙壁的时候反弹回来的速度;间隔时间是控制整个程序快慢的参数;初始速度是每只苍蝇在开始的初速度;自由重组邻居的概率:为了让Floy们的行为中有一定的随机不确定性,它们会以自由重组邻居的概率重新随机的选择自己的邻居(正常情况下是选择距离最小的若干Floy作为自己的邻居),如果这个概率指定为0那么Floy的行为就没那么有趣了;邻居数目定义了每只Floy可以有多少邻居。


接下来我们能干什么?也许你觉得这个简单程序太没趣了,应该把它改为三维的动画。没问题,这个要求还算容易满足。你只要点击这里就能看到三维的Floy。但是因为Java本身的限制,动画不如用Maya之类的三维软件作的好看,但是这个程序却反映了规律的本质,有兴趣的朋友完全可以把这个动画用Maya作一遍,这样你可以给每个Floy赋予贴图裁质,那样也许更有趣。


也许三维的Floy仍然不够有趣,我们能不能再想出点疯狂的?


进化!首先让每只Floy都拥有自己的个性,然后让Floy能够进行有性繁殖,并且后代Floy能够继承父母的特性,没问题,请看能够进化的Floy。





3. 三维的Floy鸟群模拟模型




  • 名 称:3d的Floy
  • 作 者:Ariel Dolan
  • 源代码:Floy3d.zip


3.1 程序说明


如果二维的Floy还看得不够过瘾,那么请看三维Floy程序,这里,每个Floy遵循的规则和前面叙述的基本Floy的规则一样,其中每只Floy都有两个邻居。


根据3D Floy模拟模型制作的javascript程序:

也许三维的Floy仍然不够有趣,我们能不能再想出点疯狂的?进化!首先让每只Floy都拥有自己的个性,然后让Floy能够进行有性繁殖,并且后代Floy能够继承父母的特性,没问题,请看期待后续进化的Floy





4. 相关wiki




  • 人工生命:
    https://wiki.swarma.org/index.php/%E4%BA%BA%E5%B7%A5%E7%94%9F%E5%91%BD
  • 生命游戏:
    https://wiki.swarma.org/index.php/%E5%BA%B7%E5%A8%81%E7%9A%84%E7%94%9F%E5%91%BD%E6%B8%B8%E6%88%8F_Conway%27s_Game_of_Life
  • Boid模型:
    https://wiki.swarma.org/index.php/%E9%B8%9F%E7%BE%A4%E7%AE%97%E6%B3%95_Boids


代码(2DFloy代码如下


// Global configuration for all Floysconst DEFAULT_CONFIG = {    NF: 15,           // Number of Floys    REVDIST: 200,     // Reverse distance    ACC: 0.3,         // Acceleration    ACCTOMID: 0.1,    // Acceleration to middle    MAXSPEED: 5.0,    // Maximum speed    BOUNCESPEED: 0.8// Bounce speed    NUMNB: 2,         // Number of neighbors to consider    V0: 4.0,          // Initial velocity    KICK: 0.05,       // Kick strength    TYPE: 0,          // Floy type    MARGIN: 30,       // Margin for initialization    SLEEP: 10         // Animation sleep time};// Floy class implementationclass Floy {    // Static configuration that can be modified at runtime    static config = { ...DEFAULT_CONFIG };    // Calculate distance between two Floys - matching Java version exactly    getDistance(other) {        const dx = this.x - other.x;        const dy = this.y - other.y;        return dx * dx + dy * dy; // Return squared distance like Java version    }    // Randomize velocity - matching Java version    randomize() {        this.vx = (Math.random() * Floy.config.V0 * 2 - Floy.config.V0);        this.vy = (Math.random() * Floy.config.V0 * 2 - Floy.config.V0);    }    constructor(x, y, canvas) {        this.x = x;        this.y = y;        this.xtail = x;        this.ytail = y;        this.vx = 0;  // Changed from dx to vx to match Java version        this.vy = 0;  // Changed from dy to vy to match Java version        this.canvas = canvas;        this.neighbors = new Array(Floy.config.NUMNB).fill(null);        this.color = '#ffffff';        this.type = 0;        this.energy = 100;        this.xc = x;        this.yc = y;
        // Initialize velocity using randomize method        this.randomize();    }    // Get neighbors - improved version matching Java implementation    getNeighbors(allFloys) {        // First pass: find closest neighbors        const sortedFloys = allFloys            .filter(floy => floy !== this)            .sort((a, b) => this.getDistance(a) - this.getDistance(b));
        // Initialize neighbors array if needed        if (!this.neighbors) {            this.neighbors = new Array(Floy.config.NUMNB).fill(null);        }
        // Set initial neighbors        for (let i = 0; i < Floy.config.NUMNB; i++) {            this.neighbors[i] = sortedFloys[i] || null;        }        // Second pass: check neighbors of neighbors (exact Java implementation)        for (let i = 0; i < Floy.config.NUMNB; i++) {            if (!this.neighbors[i]) continue;            const ng = this.neighbors[i];
            for (let j = 0; j < Floy.config.NUMNB; j++) {                if (!ng.neighbors[j]) continue;
                // Check if neighbor's neighbor is closer                if (this.getDistance(ng.neighbors[j]) < this.getDistance(ng)) {                    this.neighbors[i] = ng.neighbors[j];                }                // Prioritize aggressive Floys                if (ng.neighbors[j].type === 1) {                    this.neighbors[i] = ng.neighbors[j];                }            }        }        // Calculate center of neighbors        let xc = 0, yc = 0;        let validNeighbors = 0;
        for (let i = 0; i < Floy.config.NUMNB; i++) {            if (this.neighbors[i]) {                // If neighbor is self, replace with first neighbor's neighbor                if (this.neighbors[i] === this && this.neighbors[0]) {                    this.neighbors[i] = this.neighbors[0];                }                xc += this.neighbors[i].x;                yc += this.neighbors[i].y;                validNeighbors++;            }        }
        // Update center coordinates        if (validNeighbors > 0) {            this.xc = xc / validNeighbors;            this.yc = yc / validNeighbors;        } else {            this.xc = this.x;            this.yc = this.y;        }    }    // Process movement - exactly matching Java version's implementation    process() {        this.xtail = this.x;        this.ytail = this.y;        let rev = -1;        for (let i = 0; i < Floy.config.NUMNB; i++) {            if (!this.neighbors[i]) continue;            const d = this.getDistance(this.neighbors[i]);            if (d === 0) {                rev = 0;            } else if (d < Floy.config.REVDIST) {                if (this.neighbors[i].type !== this.type) {                    if (this.type === 1) {                        this.energy--;                        rev = -30;                    } else {                        rev = 30;                    }                } else {                    rev = -1;                }            } else {                if (this.type === 1) {                    if (this.neighbors[i].type === 1) {                        rev = 1;                    } else {                        rev = 0;                    }                } else if (this.neighbors[i].type === 1) {                    rev = 20;                } else {                    rev = 1;                }            }            // Apply acceleration exactly as in Java version            if (this.x < this.neighbors[i].x) {                this.vx += Floy.config.ACC * rev;            } else {                this.vx -= Floy.config.ACC * rev;            }            if (this.y < this.neighbors[i].y) {                this.vy += Floy.config.ACC * rev;            } else {                this.vy -= Floy.config.ACC * rev;            }        }        // Speed limits        if (this.vx > Floy.config.MAXSPEED) this.vx = Floy.config.MAXSPEED;        if (this.vx < -Floy.config.MAXSPEED) this.vx = -Floy.config.MAXSPEED;        if (this.vy > Floy.config.MAXSPEED) this.vy = Floy.config.MAXSPEED;        if (this.vy < -Floy.config.MAXSPEED) this.vy = -Floy.config.MAXSPEED;        // Boundary handling        if (this.x < 0) {            this.vx = Floy.config.BOUNCESPEED;            if (document.getElementById('joySound')) {                document.getElementById('joySound').play().catch(e => {});            }        }        if (this.x > this.canvas.width) {            this.vx = -Floy.config.BOUNCESPEED;            if (document.getElementById('joySound')) {                document.getElementById('joySound').play().catch(e => {});            }        }        if (this.y < 0) {            this.vy = Floy.config.BOUNCESPEED;            if (document.getElementById('joySound')) {                document.getElementById('joySound').play().catch(e => {});            }        }        if (this.y > this.canvas.height) {            this.vy = -Floy.config.BOUNCESPEED;            if (document.getElementById('joySound')) {                document.getElementById('joySound').play().catch(e => {});            }        }        // Center attraction for normal type        if (this.type === 0) {            if (this.x < this.canvas.width/2this.vx += Floy.config.ACCTOMID;            if (this.x > this.canvas.width/2this.vx -= Floy.config.ACCTOMID;            if (this.y < this.canvas.height/2this.vy += Floy.config.ACCTOMID;            if (this.y > this.canvas.height/2this.vy -= Floy.config.ACCTOMID;        }        // Update position        this.x += this.vx;        this.y += this.vy;        // Update color        this.updateColor();    }    // Update color    updateColor() {        // Default white        this.color = '#ffffff';        // Aggressive Floy is magenta        if (this.type === 1) {            this.color = '#FF00FF';        }        // Energy depletion turns green        if (this.energy < 1) {            this.color = '#00FF00';            this.type = 0;        }    }    // Draw the Floy - enhanced version with tail and energy indicator    draw(ctx) {        // 设置颜色        ctx.strokeStyle = this.color;        ctx.fillStyle = this.color;        // 计算尾巴长度和角度        const dx = this.x - this.xtail;        const dy = this.y - this.ytail;        const tailLength = Math.sqrt(dx * dx + dy * dy);        const extendedTailLength = tailLength * 3// 延长尾巴长度        // 计算尾巴的起点,使尾巴更长        const tailStartX = this.x - (dx / tailLength) * extendedTailLength;        const tailStartY = this.y - (dy / tailLength) * extendedTailLength;        // 绘制尾巴(线段)        ctx.beginPath();        ctx.moveTo(tailStartX, tailStartY);        ctx.lineTo(this.x, this.y);        ctx.lineWidth = 1;        ctx.stroke();        // 绘制主体(小圆点)        ctx.beginPath();        ctx.arc(this.x, this.y, 30, Math.PI * 2);        ctx.fill();        // 对于异类 Floy,绘制能量指示器        if (this.type === 1) {            ctx.beginPath();            ctx.fillStyle = `rgba(25500, ${this.energy / 100})`;            ctx.arc(this.x, this.y, 50, Math.PI * 2);            ctx.fill();        }    }}

代码可上下滑动查看



作者简介

glider,集智百科重度爱好者

本词条由集智俱乐部众包生产,难免存在纰漏和问题,欢迎大家留言反馈,一经采纳,可以获得对应的积分奖励噢!



加入我们


亲爱的社区伙伴与知识探索者:

我们诚挚邀请热爱知识分享的您,加入集智百科词条编写志愿团队!无论您是领域专家,还是对特定主题充满热忱的学习者,这里都有您的舞台。通过编写百科词条,您将为全球读者传递权威知识,同时获得专家指导个人能力跃升的双重成长。


📝 志愿者职责

创作新词条:覆盖复杂系统、人工智能等前沿领域

迭代经典内容:更新现有词条,守护知识的准确性与时效性

质量守护者:参与内容校对审核,共建精品知识库


🌟 我们期待您

集智读书会成员(需完成共创任务并获得退费资格)

拥有清晰表达复杂概念的写作能力

对特定领域有深度研究或强烈兴趣

具备信息检索与整合素养

怀揣责任感与协作精神,愿为知识共享赋能


🎁 您将收获

百科积分(支持兑换集智俱乐部周边:文化衫、复杂科学知识卡等)

集智俱乐部创始人张江教授亲自指导写作

科研助理晋升通道:表现优异者可加入张江教授科研团队

图片

加入张江组发起的任何一期读书会并完成共创任务即可 让我们一起,用知识连接世界!



参考资料

[1] FLOY模型的原始网站:https://mybiosoftware.com/tag/floys

[2] 作者网站:http://www.aridolan.com/

[3] 动态滑翔机机图片:

https://www.wikiwand.com/en/articles/Glider_(Conway%27s_Game_of_Life)



「大模型时代下的Agent建模与仿真」读书会


集智俱乐部联合山东工商学院副教授高德华、天津大学教授薛霄、北京师范大学教授张江、国防科技大学博士研究生曾利共同发起「大模型时代下的Agent建模与仿真」读书会。读书会自2025年7月8日开始,每周二晚上7:30-9:30进行,预计持续分享8周左右。扫码加入Agent建模与仿真的前沿探索之旅,一起共学、共创、共建、共享「大模型时代下的Agent建模与仿真」社区,共同畅想大模型时代人工社会的未来图景!


核心问题

Agent建模与仿真是什么,核心技术发生了怎样的演变?
大模型时代,Agent建模与仿真会给复杂系统理论带来哪些突破?
大模型如何赋能Agent实现自主思考与动态适应?
大模型驱动的Agent交互会涌现出什么新型的社会现象?
Agent建模与仿真如何改变金融、心理、管理、军事等领域的研究范式?

你将收获
梳理Agent建模与仿真的历史发展脉络与方法论;
掌握一套理解、分析、控制、预测复杂系统的计算实验框架;
掌握基于多主体强化学习的复杂系统优化方法;
领略领域前沿学者的研究体系与科研路径。


图片

详情请见:大模型时代下的Agent建模与仿真:共探人工社会未来图景


推荐阅读

1. 什么是康威生命游戏 | 集智百科
2. 蚁群算法 | 集智百科
3. 什么是人工生命 | 集智百科
4. 涌现动力学如何用来分析复杂系统? | 新课上线
5. 集智学园精品课程免费开放,解锁系统科学与 AI 新世界

6. 高考分数只是张入场券,你的科研冒险在这里启航!

7. 重整化群与非线性物理,寻找复杂系统跨尺度的分析方法丨新课发布



点击“阅读原文”,报名读书会

内容中包含的图片若涉及版权问题,请及时与我们联系删除