Javascript 俄罗斯方块 游戏代码

news2024/11/25 10:35:49

本俄罗斯方块代码采用 JavaScript 脚本代码写成,简单易懂;

全代码采用静态类及静态变量成员组成;

全脚本通过实现代码全局配置 OLSFK.Options = {...}

定义方块起始坐标及定义各自的旋转点;

从初始化俄罗斯方块界面开始,再监听键盘事件;以及左右,向下及旋转动作判断,重新渲染方块位置;

判断是否消行,以及相应的加级判断,执行速度,加分操作来执行;

最后以判断是否当前级别大于所定义的最大级别来判断是否结束;

代码说明讲解

OLSFK.Options = { //相关参数
    width:12,//界面横向方块数
    height:20,//界面纵向方块数
    boxWidth : '16px',
    curLevel:1,
    speed : 1000, //setInterval,setTimeout
    direct : { //可以设定是A S D W, 还是← ↓ → 
        Down: 40 , /*run speed*/
        Left: 37,
        Right: 39,
        Rotate: 38
    },
    Move:true,//是否正在移动
    Eventing:false,
    Levels: {
        1:1000,
        2:900,
        3:800,
        4:700,
        5:600,
        6:500,
        7:400,
        8:300,
        9:200,
        10:100
    },
    curBlock:4, //当前移动的图形名称
    nextBlock:0,
    GampMap:new Object(),
    Timer:null,
    deline:0,
    Score:0,
    Deling:false,
    Start:false,
    lineNum:10, //删除几行了,加级
    ScoreNum:40 //消一行加分
}

direct 表示 使用键盘方位键来操作方块的移动方向;

使用哪种方向键按自由喜欢配置,比如字母键的A, S, D, W; 或右边小数字键盘的数字键各自的键盘编码;

比如 上(旋转)、下、左、右 方向键的编码分别为:38、40、37、39;

Levels:表示级别配置,本配置共分为10级,每个级别所对应的下落速度,即定时执行间隔;

curBlock:表示当前活动的方块;

nextBlock:表示接下来执行的方块索引,并显示界面右上角的预览框中;

GampMap:用于保存在根据定义行列数形成的游戏表格中保存每个格的数据信息;

OLSFK.Options.GampMap[x+'_'+y] = 0;

对象表格为: id: "box_"+x+"_"+y;

初始化数据为 ‘0’;  表示该表格还未占用;当有占用时,设置值为 ‘1’;

Timer:为定时执行器;setTimeout 定时执行方块下落的的频率;定时时间越小,速度越快;

Deling:当正在执行消行操作时,下次暂不显示并下落;

lineNum:表示消超过 10 行,加一级;

ScoreNum:表示每消一行所加的分数;

OLSFK.ReItems = function (cur){ //key旋转点
    
    switch (cur)
    {
        case 1:
            OLSFK.Items[1] = {//长块 LongBlock
                                1:{x:4,y:0},
                                2:{x:5,y:0},
                                3:{x:6,y:0},
                                4:{x:7,y:0},
                                5:{x:5,y:0} //旋转点
                            };
            break;
                        //....
        }
}

该方法用于恢复方块的初始设置;

OLSFK.Next = { //key旋转点
    //长块 LongBlock
    1: {
        1:{x:0,y:1},
        2:{x:1,y:1},
        3:{x:2,y:1},
        4:{x:3,y:1}
    },
        //...
}

为不了不与游戏方块的设置冲突,独立出来下次随机方块的对象配置;

OLSFK.Items = { //key旋转点
    //长块 LongBlock
    1: {
        1:{x:4,y:0},
        2:{x:5,y:0},
        3:{x:6,y:0},
        4:{x:7,y:0},
        5:{x:5,y:0}
    },
    //方块Box
    2: {
        1:{x:4,y:0},
        2:{x:5,y:0},
        3:{x:4,y:1},
        4:{x:5,y:1},
        5:{x:0,y:0}
    },
    //凸块 TuBlock
    3: {
        1:{x:4,y:1},
        2:{x:5,y:0},
        3:{x:5,y:1},
        4:{x:6,y:1},
        5:{x:5,y:1}
    },
    //L块 LBlock
    4: {
        1:{x:5,y:0},
        2:{x:5,y:1},
        3:{x:5,y:2},
        4:{x:6,y:2},
        5:{x:5,y:2}
    },
    5: { //反向L块 FLBlock
        1:{x:5,y:2},
        2:{x:6,y:2},
        3:{x:6,y:1},
        4:{x:6,y:0},
        5:{x:6,y:2}
    },
    //Z块 ZBlock
    6: {
        1:{x:4,y:0},
        2:{x:5,y:0},
        3:{x:5,y:1},
        4:{x:6,y:1},
        5:{x:5,y:0}
    },
    7: {//反向Z块 FZBlock
        1:{x:4,y:1},
        2:{x:5,y:1},
        3:{x:5,y:0},
        4:{x:6,y:0},
        5:{x:5,y:1}
    }
}

方块共分为:长条块,方块,凸块(T块),L块,反L块,Z块,反Z块几种;

共7种方块,以1,2,3,4,5,6,7 索引键表示,方块是四个小块组成,每块都有各自的坐标,1-4表示组成该块的初始坐标位置,5表示旋转点;

OLSFK.Init = function() { //初始化界面
    //...
}

俄罗斯方块的界面初始化方法;将在 window.onload 中调用执行;

var w = OLSFK.Options.width;
    var h = OLSFK.Options.height;
    var total = w * h;

    var x=0,y=0;
    for (var i=0; i<total; i++)
    {
        
        OLSFK.Options.GampMap[x+'_'+y] = 0;

        Lib.Tag('SPAN',{id:"box_"+x+"_"+y,name:"cbox",style:{
            width:OLSFK.Options.boxWidth,
            height:OLSFK.Options.boxWidth,
            border:"2px outset #669",
            background:"#ddd",
            float:"left",
            overflow:"hidden"
        },innerHTML:"&nbsp;",className:"cssbox"},back);

        var end = i + 1;
        x++;
        if (end >= w && end % w == 0)
        {
            x=0;
            y++;
            Lib.Tag('DIV',{style:{
                clear:"both"
            }},back);
        }

    }

通过设置的 Options.width, Options.height 列数与行数,以及设置的小方格宽度,初始化了一个宽:Options.width列,高为 Options.height 的表格界面出来;

Lib.Tag 用于创建标签对象;

Lib.Tag = function(TAG,json,pnode) {
    //...
}

TAG为标签名,比如: div, span 等;

json为设置标签样式 style;

pnode 是该创建所在的父容器;

OLSFK.Init = function() {} 还创建主游戏区域旁边的下次随机方块预览区,当前级别,及分数,以及操作“开始”,“暂停”按钮等;

游戏初始入口点

window.onload = function() {
    if (window.isIE)
    {
        document.attachEvent("onkeydown",function(e) {
            if (OLSFK.Options.Start)
            {
                var E = OLSFK.KeyCode();
                OLSFK.EventFunc(E);
            }

        });

        document.attachEvent("onkeyup",function(e) {
            if (!OLSFK.Options.Move && OLSFK.Options.Start)
            {
                OLSFK.Options.Move = true;
                OLSFK.Options.Eventing = false;

                OLSFK.Options.Timer = setTimeout(function() {
                    OLSFK.play();
                }, OLSFK.Options.Levels[OLSFK.Options.curLevel]);
            }
        });
    } else {
        document.addEventListener("keydown",function(e) {

            if (OLSFK.Options.Start)
            {
                var E = OLSFK.KeyCode();
                OLSFK.EventFunc(E);
            }
            
        },false);

        document.addEventListener("keyup",function(e) {
            if (!OLSFK.Options.Move && OLSFK.Options.Start)
            {
                OLSFK.Options.Move = true;
                OLSFK.Options.Eventing = false;

                OLSFK.Options.Timer = setTimeout(function() {
                    OLSFK.play();
                }, OLSFK.Options.Levels[OLSFK.Options.curLevel]);
            }
        },false);
    }
    OLSFK.Init();
}

主要是监听键盘事件,根据 键盘事件 返回的按钮编码与 OLSFK.Options.direct 设置方向键匹配来操作方块的移动,旋转等;

keydown 用于操作下落方块的移动方向,旋转等;并重新绘制方块位置;

keyup 后继续按本级速度向下落;

OLSFK.Options.Levels[OLSFK.Options.curLevel]

表示当前级别对应的速度,即定时器间隔执行时间(毫秒);

OLSFK.EventFunc = function(code) {
    switch (code)
    {
        case OLSFK.Options.direct.Left: //LEFT
            if (!OLSFK.Options.Deling)
            {
                clearTimeout(OLSFK.Options.Timer);
                OLSFK.Options.Eventing = true;
                OLSFK.Options.Move = false;
                OLSFK.Left();
            }

            break;
            //...
        }
}

该方法是 监听 keydown 事件执行的动作;code 为按键 编码;

当判断未在消行动作时,清除定时器,OLSFK.Options.Eventing 设置为事件中 true,OLSFK.Options.Move 为 false 表示停止移动;

进入 向左移动方法 OLSFK.Left();

OLSFK.Left = function() {
    var block = OLSFK.Items[OLSFK.Options.curBlock];

    if (block)
    {
        var flag = true;
        for (var i=1; i<=4; i++)
        {
            var x = block[i].x;
            var y = block[i].y;

            if (x-1 < 0)
            {
                flag = false;
                break;
            }
            
            if (OLSFK.Options.GampMap[(x-1)+'_'+y] == 1 && !OLSFK.isMe(x-1,y))
            {
                flag = false;
                break;
            }
        }

        if (flag)
        {
            for (var i=1; i<=4; i++) //清除图形
            {
                var itm = block[i];
                var box = Lib.Getid('box_'+itm.x+'_'+itm.y);

                box.style.background = '#ddd';
                OLSFK.Options.GampMap[itm.x+'_'+itm.y] = 0;
            }

            for (var i=1; i<=5; i++)
            {
                var x = block[i].x;
                var y = block[i].y;

                OLSFK.Items[OLSFK.Options.curBlock][i] = {x:(x-1),y:y};
            }
            
            OLSFK.draw();
        } 
    }
}

var block = OLSFK.Items[OLSFK.Options.curBlock]; 表示获取当前移动方块;

if (OLSFK.Options.GampMap[(x-1)+'_'+y] == 1 && !OLSFK.isMe(x-1,y))
{
    flag = false;
    break;
}

判断该方块四个小方块左边是否有被占用的方块,也即: OLSFK.Options.GampMap[(x-1)+'_'+y] 为 1; 并且该位置块不属于方块自己的;

当左边方向无占用格时,清除当前方块四个小方块位置,重新绘制方块新坐标位置;并重置 相应的 OLSFK.Options.GameMap [x+y] 相应格的值;

当按钮 keyup 时,继承正常向下落;

OLSFK.isMe 代码:

OLSFK.isMe = function(x,y) {
    var block = OLSFK.Items[OLSFK.Options.curBlock];

    if (block)
    {
        for (var i=1; i<=4; i++)
        {
            if (block[i].x == x && block[i].y == y)
            {
                return true;
            }
        }
    }

    return false;
}

即指定的 x,y (参数值) 是否还在当前方块四个坐标内;

OLSFK.Right () 与 Left() 一样;

旋转方块代码;

OLSFK.Rotate = function() {
    var block = OLSFK.Items[OLSFK.Options.curBlock];

    if (block)
    {
        var flag = true;

        var R = block[5];
        for (var i=1; i<=4; i++)
        {
            var x = block[i].x;
            var y = block[i].y;

            if (R.x == x && R.y == y)
            {
                
            } else {
                var nson = new Object();

                nson.x = R.x + R.y - y;
                nson.y = R.y - R.x + x;

                if ( nson.x < 0 || nson.y < 0 || nson.x >= OLSFK.Options.width || nson.y >= OLSFK.Options.height )
                {
                    flag = false;
                    break;
                }

                if (OLSFK.Options.GampMap[nson.x+'_'+nson.y] == 1 && !OLSFK.isMe(nson.x,nson.y))
                {
                    flag = false;
                    break;
                }
            }
        }

        if (flag)
        {
            for (var i=1; i<=4; i++) //清除图形
            {
                var itm = block[i];
                var box = Lib.Getid('box_'+itm.x+'_'+itm.y);

                box.style.background = '#ddd';
                OLSFK.Options.GampMap[itm.x+'_'+itm.y] = 0;
            }

            var Pnt = 1;

            for (var i=1; i<=4; i++)
            {
                var x = block[i].x;
                var y = block[i].y;
                
                if (R.x == x && R.y == y)
                {
                    Pnt = i;
                } else {
                    var nson = new Object();

                    nson.x = R.x + R.y - y;
                    nson.y = R.y - R.x + x;
                    OLSFK.Items[OLSFK.Options.curBlock][i] = {x:nson.x,y:nson.y};
                }
            }

            OLSFK.Items[OLSFK.Options.curBlock][5] = OLSFK.Items[OLSFK.Options.curBlock][Pnt];
            
            OLSFK.draw();
        } 
    }
}

var R = block[5]; 就是获取旋转点;

就开始对方块四个小块以旋转点为中心,逆时针旋转(并不全是 90 度);当当前块不为旋转点时,旋转公式;

var nson = new Object();

nson.x = R.x + R.y - y;
nson.y = R.y - R.x + x;

这个公式要这样看; 

ResultX = RotateX + (RotateY - CurrentY);
ResultY = RotateY - (RotateX - CurrentX);

//Y的偏移量,就是X的增加值;
//反之同

当旋转四周都无占用物时;清除当前图形,重绘旋转后的图形位置;

重置 OLSFK.Options.GampMap[itm.x+'_'+itm.y] 各个方块的占用值;

OLSFK.Random = function() {

    if (OLSFK.Options.nextBlock != 0)
    {
        OLSFK.Options.curBlock = OLSFK.Options.nextBlock;

        var block = OLSFK.Next[OLSFK.Options.nextBlock];
        if (block)
        {
            for (var i=1; i<=4; i++)
            {
                var itm = block[i];
                var box = Lib.Getid('cur_'+itm.x+'_'+itm.y);

                box.style.background = '#ddd';
                //OLSFK.Options.GampMap[itm.x+'_'+itm.y] = 0;
            }
        }
    } else {
        OLSFK.Options.curBlock = Math.floor(Math.random() * 7 + 1);
    }
    OLSFK.Options.nextBlock = Math.floor(Math.random() * 7 + 1);

    OLSFK.drawNext();
}

随机生成下次预下落的方块;并显示到右上角的预览表格里;

OLSFK.play = function(speed) {
    var block = OLSFK.Items[OLSFK.Options.curBlock];

    if (block && OLSFK.Options.Move)
    {

        var flag = true;
        for (var i=1; i<=4; i++)
        {
            var x = block[i].x;
            var y = block[i].y;

            if (y+1 >= OLSFK.Options.height)
            {
                flag = false;
                break;
            }
            
            if (OLSFK.Options.GampMap[x+'_'+(y+1)] == 1 && !OLSFK.isMe(x,y+1))
            {
                flag = false;
                break;
            }
        }
        
        if (flag)
        {
            for (var i=1; i<=4; i++) //清除图形
            {
                var itm = block[i];
                var box = Lib.Getid('box_'+itm.x+'_'+itm.y);

                box.style.background = '#ddd';
                OLSFK.Options.GampMap[itm.x+'_'+itm.y] = 0;
            }

            for (var i=1; i<=5; i++)
            {
                var x = block[i].x;
                var y = block[i].y;

                OLSFK.Items[OLSFK.Options.curBlock][i] = {x:x,y:(y+1)};
            }
            OLSFK.draw();
            
            var S = OLSFK.Options.Levels[OLSFK.Options.curLevel];
            if (speed)
            {
                S = 50;
            }
            OLSFK.Options.Timer = setTimeout(function() {
                OLSFK.play();
            }, S);
        } else {
            OLSFK.ReItems(OLSFK.Options.curBlock);
            OLSFK.newRun();
        }
    }
}

OLSFK.play 正常下落的方法,也得判断下落一格是否有被占用的格,如果没有,清除当前方块,绘制方块新位置;

当方块不能再下落时(flag = false),重置当前方块坐标配置; OLSFK.ReItems(OLSFK.Options.curBlock);

进入 OLSFK.newRun(); 新下落方块下落过程准备;

OLSFK.newRun = function() {

    clearTimeout(OLSFK.Options.Timer);
    OLSFK.DelFunc();
    if (OLSFK.Options.deline >= 10)
    {
        OLSFK.Options.deline = 0;
        OLSFK.Options.curLevel ++;
        OLSFK.Element.CurLevel.setHTML("级:"+OLSFK.Options.curLevel);
    }
    OLSFK.Element.Score.setHTML("分:"+OLSFK.Options.Score);

    if (OLSFK.Options.curLevel <= OLSFK.Options.lineNum)
    {
        OLSFK.Random();
        //判断是否结束
        OLSFK.ChkEnd();
    } else {
        OLSFK.Options.Move = false;
        OLSFK.Options.Start = false;
        OLSFK.Options.Eventing = false;
        OLSFK.Options.Deling = false;
        Lib.Getid('spn').innerHTML = 'Game Is Over! You Win the Game!';

        Lib.Getid('dobtn').innerHTML = ' 开始 ';
    }
    
}

当下落结束时,清除定时器,暂停新方块下落,检测是否有可消除的行;减了多少行;

每减去一行 加分 OLSFK.Options.Score += OLSFK.Options.ScoreNum;

这个方法在 : 

OLSFK.DelFunc = function() {
    OLSFK.Options.Deling = true;
    OLSFK.Options.Move = false;
    OLSFK.Options.Eventing = false;
    var Fn = 0;
    for (var i=OLSFK.Options.height-1; i>=0; i--)
    {
        Fn = 0;
        for (var j=0; j<OLSFK.Options.width; j++)
        {
            if (OLSFK.Options.GampMap[j+'_'+i] == 1)
            {
                Fn++;
            }
        }

        if (Fn == OLSFK.Options.width)
        {
            OLSFK.Options.deline ++;
            OLSFK.Options.Score += OLSFK.Options.ScoreNum;
            OLSFK.DelLine(i);
            i++;
        }
    }
    OLSFK.Options.Deling = false;
    OLSFK.Options.Move = true;
    OLSFK.Options.Eventing = true;
}

中执行;

减完一行,就重置该行以上所有行往下降一行;并重置 :

OLSFK.Options.GampMap[x+'_'+y] = OLSFK.Options.GampMap[x+'_'+(y-1)];

该减行为上行的数据;

if (Fn == OLSFK.Options.width)
        {
            OLSFK.Options.deline ++;
            OLSFK.Options.Score += OLSFK.Options.ScoreNum;
            OLSFK.DelLine(i);
            i++;
        }

该判断表示该行上所有格都被占用到;

回到 newRun 上,当判断消行超过几行时,即加级;

if (OLSFK.Options.curLevel <= OLSFK.Options.lineNum)
    {
        OLSFK.Random();
        //判断是否结束
        OLSFK.ChkEnd();
    }

如果级数小于配置的总级数,则进入 OLSFK.random();

设置当前下落方块,并随机生成下次下落方块并预览右上角表格上;

OLSFK.ChkEnd = function() {
    var block = OLSFK.Items[OLSFK.Options.curBlock];

    if (block && OLSFK.Options.Move)
    {

        var flag = true;
        for (var i=1; i<=4; i++)
        {
            var x = block[i].x;
            var y = block[i].y;
            
            if (OLSFK.Options.GampMap[x+'_'+y] == 1)
            {
                flag = false;
                break;
            }
        }
    }

    if (flag )
    {
        OLSFK.draw();

        //定时往下掉
        OLSFK.Options.Timer = setTimeout(function() {
            OLSFK.play();
        }, OLSFK.Options.Levels[OLSFK.Options.curLevel]);
    } else {
        OLSFK.Options.Move = false;
        OLSFK.Options.Start = false;
        OLSFK.Options.Eventing = false;
        OLSFK.Options.Deling = false;
        Lib.Getid('spn').innerHTML = 'Game Is Over';

        Lib.Getid('dobtn').innerHTML = ' 开始 ';
    }
}

当当前下落的方块进入表格上有被占用的格子,即被卡住,游戏结束;

反之 则 setTimeout 开始新方块的下落动作;

其他方法说明

OLSFK.Event = function() {
    if (window.isIE)
        return window.event;
    func = OLSFK.Event.caller; 

    while(func!=null) 
    {
        var arg0=func.arguments[0]; 
        if(arg0) 
        {
            return arg0; 
        }
        func=func.caller; 
    }
    return null; 
}

OLSFK.KeyCode = function() {
    return OLSFK.Event().keyCode || OLSFK.Event().which;
}

OLSFK.Event = function();

这是一种获取当前事件的方法,可以比较兼容获取当前的事件;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/596362.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【C++】指针 - 定义和使用,所占内存空间,空指针,野指针,const 修饰指针,指针和数组,指针和函数

文章目录 1. 定义和使用2. 所占内存空间3. 空指针4. 野指针5. const 修饰指针6. 指针和数组7. 指针和函数 1. 定义和使用 数据类型 * 变量名; 指针的作用是&#xff0c;可以通过指针间接访问内存。 内存编号是从 0 开始记录的&#xff0c;一般用十六进制数字表示。可以利用指…

关于如何用好线程池的一些建议

文章目录 1. 线程的使用场景2. 线程池创建3. 参数的配置建议常见的拒绝策略其他的拒绝策略 4. 线程池的任务处理流程5. 线程的状态6. 线程池的监控 1. 线程的使用场景 异步任务 简单来说就是某些不需要同步返回业务处理结果的场景&#xff0c;比如&#xff1a;短信、邮件等通…

一款IP渗透小工具

MoreFind 一款用于快速导出URL、Domain和IP的小工具 快速安装 方式一: 通过Go包管理安装 go install github.com/mstxq17/MoreFindlatest 方式二: 直接安装二进制文件 wget --no-check-certificate https://ghproxy.com/https://github.com/mstxq17/MoreFind/releases/…

软件测评师2012年下半年考试真题

基础知识&#xff1a; 解析&#xff1a;死锁就是运行不下去了&#xff0c;但是这里它说资源是同类型的&#xff0c;也就是说&#xff0c;我多出来的一个资源&#xff08;11个资源5个进程&#xff0c;每个进程分配2个&#xff09;给其中一个进程之后&#xff0c;运行完了资源释放…

【源码篇】基于SpringBoot+thymeleaf实现的图书管理系统

系统介绍 基于SpringBootthymeleaf实现的图书管理系统分为管理员、读者两个登录角色&#xff0c;一共是8个功能模块 管理员权限 图书管理&#xff1a; 添加图书&#xff1a;书名、作者、出版社、ISBM、简介、价格、出版日期、分类、数量查询图书&#xff1a;根据书名或分类…

Tomcat发布成服务

一、配置服务 1.配置bin目录下的service.bat&#xff08;用于生成新服务&#xff09; 配置内容&#xff1a; rem Tomcat解压根目录 set CATALINA_HOMED:\apache-tomcat-7.0.55-8080 rem 服务显示名称&#xff08;服务中对外显示的名称&#xff09; set PR_DISPLAYNAMEapache…

ChunJun FTP Connector 功能扩展解读

本文将从 FTP Connector 的功能详解&#xff0c;自定义文件切割及自定义 FileFormat 三个方面为大家带来 ChunJun FTP Connector 的功能扩展分享。 FTP Connector 详解 FTP 是用于在网络上进行文件传输的一套标准协议&#xff0c;它工作在 OSI 模型的第七层&#xff0c; TCP …

从0到1搭建自己的脚手架(java后端) | 京东云技术团队

一、脚手架是什么 脚手架是一种基础设施工具&#xff0c;用于快速生成项目的框架代码和文件结构。它是一种标准化的开发工具&#xff0c;使开发人员能够在项目的早期阶段快速搭建出一个具备基本功能和结构的系统。 二、脚手架的意义 主流的微服务架构体系下很多公司会将原有…

HarmonyOS/OpenHarmony应用开发-ArkTS语言基本语法说明

以一个具体的示例来说明ArkTS的基本组成。如下图所示&#xff0c;当开发者点击按钮时&#xff0c;文本内容从“Hello World”变为“Hello ArkUI”。 图1 示例效果图 本示例中&#xff0c;ArkTS的基本组成如下所示。 图2 ArkTS的基本组成 装饰器&#xff1a; 用于装饰类、结构…

impala中group_concat()函数无法对内容进行order by

描述&#xff1a; 使用的是impala数据库&#xff0c;假设有四笔数据&#xff0c;是无序的&#xff0c;业务上要求将其行转列成一行数据&#xff0c;并且里面的数据要按从小到大排序。 过程&#xff1a; 猜测&#xff1a; 数据库Oracle、Mysql、MSsql等支持group_concat中使…

依赖倒置原则:高层代码和底层代码,到底谁该依赖谁?

前言 上一篇&#xff0c;我们讲了 ISP 原则&#xff0c;知道了在设计接口的时候&#xff0c;我们应该设计小接口&#xff0c;不应该让使用者依赖于用不到的方法。 依赖这个词&#xff0c;程序员们都好理解&#xff0c;意思就是&#xff0c;我这段代码用到了谁&#xff0c;我就…

签名支持全球管控AI 三巨头侧漏“求生欲”

又一封“群星云集”警示AI风险的公开信来了&#xff0c;这封信的内容简短但措辞炸裂&#xff1a;减轻 AI 带来的灭绝风险&#xff0c;应该与管控流行病和核战争等其他社会级规模的风险一样&#xff0c;成为一项全球优先事项。 5月30日&#xff0c;这纸原文只有22个单词的声明&…

核心交换机的四种关键技术:链路聚合、冗余、堆叠和热备份,真简单!

你好&#xff0c;这里是网络技术联盟站。 当涉及到核心交换机的关键技术&#xff0c;如链路聚合、冗余、堆叠和热备份时&#xff0c;下面更详细地介绍每个技术的工作原理和优势。 1. 链路聚合 链路聚合是一种技术&#xff0c;用于将多个物理链路组合成一个逻辑链路&#xff0…

详解Java枚举

一、知识点 二、概念 enum 的全称为 enumeration&#xff0c; 是 JDK 1.5 中引入的新特性。 在Java中&#xff0c;被 enum 关键字修饰的类型就是枚举类型。形式如下&#xff1a; enum Color { RED, GREEN, BLUE }如果枚举不添加任何方法&#xff0c;枚举值默认为从0开始的有…

CSS 选择器的常见用法

前言 CSS在编写代码的时候有很多种样式&#xff0c;和和HTML&#xff0c;JS相似&#xff0c;他们都是运行在浏览器中的&#xff0c;下面就介绍一下CSS选择器的常见用法。 标签选择器使用标签名把页面中所有同名标签都选中类选择器使用.类名的方式对应一组CSS属性id选择器使用 …

小米再度登上《焦点访谈》!中关村论坛展科技风采

5月30日下午&#xff0c;以“开放合作共享未来”为主题的2023中关村论坛展览&#xff08;科博会&#xff09;在京圆满落幕。小米作为科技领军企业参展&#xff0c;设立“科技创新、绿色低碳”主题展区。 小米携智能手机、可穿戴设备、智能家居以及全尺寸人形仿生机器人CyberOne…

【ROS】ROS2编程示例:话题订阅-发布-C++版

1、准备 1&#xff09;安装ROS2 【ROS】Ubuntu22.04安装ROS2&#xff08;Humble Hawksbill&#xff09; 2&#xff09;ROS2命令 【ROS】ROS2命令行工具详解 3&#xff09;配置工作空间 【ROS】ROS2中的概念和名词解释中第一节&#xff1a;工作空间 workspace 4&#xff09;…

MySQL-12-SQL优化

一、MySQL体系结构 1.1、体系结构 # 1.2、查询执行流程 参考&#xff1a;https://www.cnblogs.com/xfeiyun/p/15899990.html 1.3、组件说明 管理工具&#xff1a;MySQL服务软件安装后提供的命令连接池&#xff1a;检查本机是否有空闲资源&#xff08;线程&#xff0c;内存&…

Geoffrey Hinton、姚期智、张钹、Sam Altman等专家共话AI安全与对齐丨2023智源大会议程公开...

6月9日&#xff0c;2023北京智源大会&#xff0c;将邀请AI领域的探索者、实践者、以及关心智能科学的每个人&#xff0c;共同拉开未来舞台的帷幕&#xff0c;你准备好了吗&#xff1f;与会知名嘉宾包括&#xff0c;图灵奖得主Yann LeCun、OpenAI创始人Sam Altman、图灵奖得主Ge…

Linux教程——Linux和UNIX的关系及区别(详解版)

UNIX 与 Linux 之间的关系是一个很有意思的话题。在目前主流的服务器端操作系统中&#xff0c;UNIX 诞生于 20 世纪 60 年代末&#xff0c;Windows 诞生于 20 世纪 80 年代中期&#xff0c;Linux 诞生于 20 世纪 90 年代初&#xff0c;可以说 UNIX 是操作系统中的"老大哥&…