目录

一.项目介绍

主要功能:

主要构成:

项目实现步骤:

二.具体实现

1.棋盘的绘制:

2.五子棋的绘制:

3.黑白棋子交替功能:

 4.实现重绘功能   

5.判断输赢

6.悔棋

三.完整代码

GoBangUI:

GoBangListener:

GoPanel:

GoData:

GoWin:

四.思考:

关于GoData接口


一.项目介绍

本次五子棋项目用 Java Swing 做图形界面,实现了双人对战、判胜负和悔棋功能。我按功能拆分了类,用数组保存棋盘状态并实现界面刷新,熟练使用事件监听的用法。

主要功能:

黑白两子双人对战,判断胜负,悔棋

主要构成:

-GoBangUI:主界面。负责创建窗口、添加面板、绑定监听器、把监听器 画笔 AI 绑在一起。是程

序执行的入口。

-GoBangListener: 监听器类。负责接收鼠标点击、下棋、切换黑白、调用 AI。

-GoPanel 面板类。负责绘制网格、棋子,实现刷新重绘功能。

-GoData 接口。负责统一共享数据。

-GoWin 只负责判断是否连成五子。

public class GoBangUI {
    GoBangListener gbl=new GoBangListener();//创建监听器对象
    public void GoUI(){
        `````
        `````
        //传递画笔,绑定监听器
        Graphics g = gp.getGraphics();  // 获取棋盘面板的画笔对象
        gbl.g = g;                      // 把画笔交给监听器(监听器用它画棋子)
        gp.addMouseListener(gbl);       // 给棋盘面板绑定鼠标监听器
}
    public static void main(String[] args) {
        GoBangUI ui=new GoBangUI();
        ui.GoUI();
    }
}
public class GoBangListener implements MouseListener, ActionListener,GoData {
    `````````
}

public class GoPanel extends JPanel implements GoData{
    public void paint (Graphics g){
        ``````
}
}
public interface GoData {
    `````
`````
}

项目实现步骤:

1.绘制网格,添加面板

2.绘制五子棋

3.实现黑白棋子交替

4.实现重绘功能,添加弹窗

5.添加按钮-->按钮“开始游戏 到 结束游戏”的转换。然后点击“开始游戏”按钮,棋盘清空

6.实现判断输赢的功能

7.实现悔棋功能


二.具体实现

1.棋盘的绘制:

(1).在GoBangUI类的GoUI方法中 设置窗口,添加面板和按钮。

 public void GoUI(){
        JFrame jf=new JFrame();
        jf.setTitle("五子棋");
        jf.setSize(1050, 800);
        jf.setLocationRelativeTo(null);
        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        //添加面板
        GoPanel gp=new GoPanel();
        jf.add(gp);

        //添加右边面板
        JPanel right=new JPanel();
        right.setPreferredSize(new Dimension(260,0));
        right.setBackground(Color.GRAY);
        jf.add(right,BorderLayout.EAST);

        String[] text={"开始游戏","悔棋","AI对战"};
        for(int i=0;i<text.length;i++){
            JButton btn=new JButton(text[i]);
            right.add(btn);
            //给按钮加点击监听(点按钮会触发动作)。按钮被点击时,交给 gol 处理
            btn.addActionListener(gbl);
            if(i==0) {
                btn.setBackground(Color.GREEN);
            }else{
                btn.setBackground(Color.WHITE);
            }

        }
        jf.setVisible(true);

(2)在GoPanel类中绘制 棋盘背景和网格

        //棋盘背景
        g.setColor(new Color(218, 131, 60));
        g.fillRect(X - SIZE / 2, Y - SIZE / 2, COL * SIZE + SIZE, ROW * SIZE + SIZE);
        g.setColor(Color.BLACK);

        //画棋盘线
        for(int i=0;i<=ROW;i++){
        g.drawLine(X,Y+i*SIZE,X+ROW*SIZE,Y+i*SIZE);
        g.drawLine(X+i*SIZE,Y,X+i*SIZE,Y+COL*SIZE);
    }

(3).GoPanel为什么要继承JPanel?

1.继承 JPanel 后,拥有画布绘图能力,可以重写 paint 方法,绘制棋盘背景、线条和黑白棋子。

2.成为标准界面组件,能添加到 JFrame 窗口中显示

3.属于可视组件,可以绑定鼠标监听器,获取鼠标点击位置实现落子。

4.自带 Swing 重绘机制,支持 repaint (),窗口缩放、遮挡、悔棋后能自动刷新重绘画面


2.五子棋的绘制:

(1)关键一步: 传递画笔,绑定监听器

在GoBangUI类的GoUI方法中:

//传递画笔,绑定监听器
        Graphics g = gp.getGraphics();  // 获取棋盘面板的画笔对象
        gbl.g = g;                      // 把画笔交给监听器(监听器用它画棋子)
        gp.addMouseListener(gbl);       // 给棋盘面板绑定鼠标监听器

(2)确定棋子绘制位置

在GoBangListening的mousePressed方法中:

//获取鼠标点击处的坐标
int x=e.getX();
int y=e.getY();
//进行坐标换算,来确定最终落子的位置
int r=(y - Y + SIZE / 2) / SIZE;
int c=(x - X + SIZE / 2) / SIZE;

(3)绘制棋子

在GoBangListening的mousePressed方法中:

int cx=c * SIZE + X - SIZE / 2;
int cy=r * SIZE + Y - SIZE / 2;
g.fillOval(cx,cy,SIZE,SIZE);

fillRect(x, y, 宽, 高) 画实心矩形。 

fillOval(x, y, 宽, 高)画实心椭圆 (当宽 = 高时就是圆)

两个方法的 xy 都是外接矩形的左上角坐标,不是圆心


3.黑白棋子交替功能:

定义  int CHESS_FLAG=0;(初始化CHESS_FLAG=0的原因:当点击“开始游戏”按钮后,CHESS_FLAG被置为1,可以下棋。未点击按钮,为0,不可以下棋)

在GoBangListening的mousePressed方法中:

//1表示黑棋,2表示白棋
        if(CHESS_FLAG==1){
            g.setColor(Color.BLACK);
            CHESS_FLAG=2;//黑棋下完后,CHESS_FLAG被置为2
        }else if(CHESS_FLAG==2){
            g.setColor(Color.WHITE);
            CHESS_FLAG=1;
        }

这段代码要在下面这段代码之前,先判断给画笔颜色,再绘制第一颗棋子

 g.fillOval(cx,cy,SIZE,SIZE);

 4.实现重绘功能   

重绘的分两种情况:

1.系统自动重绘:拉动窗口、放大缩小窗口、窗口被遮挡再显示、窗口最小化还原时,Swing 系统会自动调用 paint () 方法,读取最新 chessArr 数组数据,重新绘制棋盘与所有棋子。

2.手动重绘:悔棋、AI 自动下棋、重新开始游戏时,界面不会自动刷新,需要手动调用repaint()方法,主动触发 paint () 执行,根据更新后的数组刷新画面。

原理:

窗口发生界面变化时,Swing 自动调用 paint 方法;

悔棋、AI 下棋等逻辑变化时,手动调用 repaint 触发 paint 方法;

最终都是读取最新 chessArr 数组,重新绘制全部棋盘和棋子。

步骤:

(1).用二维数组保存所有棋子状态(例如chessArr[r][c]记录 0 空 / 1 黑子 / 2 白子)。

 -定义chessArr,此时数组中所有元素都为零。(在GoBangListener中:

-在每次绘制棋子之前将 CHESS_FLAG 存入数组中,此时1表示黑棋,2表示白棋,0表示没有棋。(在GoBangListener的mousePressed方法中:

        if(chessArr[r][c]==0){
            chessArr[r][c]=CHESS_FLAG;//将棋子颜色存入数组中
        }
        else {
            showMessage("请不要重复下棋");
            return;
        }

(2).GoPanel的 paint 方法中,根据数组绘制全部棋子。

        //遍历数组,将数组中的棋子重绘
        for(int i=0;i<chessArr.length;i++){
            for(int j=0;j<chessArr.length;j++){
                if(chessArr[i][j]!=0) {
                    if (chessArr[i][j] == 1) {
                        g.setColor(Color.BLACK);
                    } else if (chessArr[i][j] == 2) {
                        g.setColor(Color.WHITE);
                    }
                    int cx, cy;
                    cx = j * SIZE + X - SIZE / 2;
                    cy = i * SIZE + Y - SIZE / 2;
                    g.fillOval(cx, cy, SIZE, SIZE);
                }
            }
        }

注意:

1.鼠标点击时,只更新数组,然后调用 repaint() 触发重绘

2.鼠标mousePressed里画的棋子是临时的,只有将棋子信息保存到数组里才能永久重绘(每次刷新窗口,系统自动调用GoPanel里的paint()方法 )​​​​​​​

3.重绘分两种:系统自动重绘、代码手动重绘


5.判断输赢

1.在GoWin类中 判断“是否赢了”:

每次落下一颗棋子,就检查这颗棋子的 "上下、左右、两个斜方向" 有没有连成 5 个同色棋子。只

要任意一个方向>=5 个,就判定赢棋。

以上下方向为例:

向最后一颗棋子的 上下两端延伸统计 连续同色棋子数量。若遇到不同色的,就停止循环。最终返回count的值。

        //纵向
        public int col(int[][] chessArr,int r,int c){
            int count=1;
            //向上查找
            for(int i=r-1;i>=0;i--){
                if(chessArr[i][c]==chessArr[r][c]) count++;
                else {break;}
            }
            //向下查找
            for(int i=r+1;i<chessArr.length;i++){
                if(chessArr[i][c]==chessArr[r][c]) count++;
                else {break;}
            }
            return count;
        }

2.在GoBanglistener类中 再判断是“什么颜色的棋子赢了”:

mousePressed方法中:

若最后下的一颗棋是黑棋,则黑棋赢。反之,白棋赢。

        //判断输赢
        GoWin win=new GoWin();
        if(win.checkWin(chessArr,r,c)){
            if(chessArr[r][c]==1){
                showMessage("黑棋赢");
            }else if(chessArr[r][c]==2){
                showMessage("白棋赢");
            }
            CHESS_FLAG = 0; // 禁止继续下棋
        }

6.悔棋

关键步骤:

-记住上一步下在哪

-点悔棋时把数组里那颗棋子删掉

-通知面板刷新画面

(1).在GoBanglistener中加上:

//初始化为-1,表示没有棋子可以悔棋
int lastR = -1;
int lastC = -1;
// 拿到棋盘面板,用来刷新画面
GoPanel panel;

GoPanel panel; 的目的和作用 :

把棋盘面板拿进监听器里,悔棋、AI 下棋时方便调用 repaint () 刷新画面

(2).在 chessArr[r][c]=CHESS_FLAG; 下面加两行:

// 把当前落子位置记下来,给悔棋用
lastR = r;
lastC = c;

你每点一次鼠标下棋,就自动把行 r、列 c存起来,后面点悔棋,就知道删哪个位置的棋子

注意:

      不可以加在

int r=(y - Y + SIZE / 2) / SIZE;
int c=(x - X + SIZE / 2) / SIZE;

      这个后面

      原因:可能会记录 超出棋盘、重复下棋 的坐标,导致无法功能混乱

(3).写悔棋方法:

// 悔棋功能方法
    public void huiQi(){
        // 如果没点开始游戏,不让悔棋
        if(CHESS_FLAG == 0){
            showMessage("请先开始游戏");
            return;
        }
        // 还没下过棋,不能悔棋
        if(lastR == -1 || lastC == -1){
            showMessage("还没有棋子可以悔棋");
            return;
        }

        // 1. 把上一步棋子从数组清空(相当于拿走棋子)
        chessArr[lastR][lastC] = 0;

        // 2. 回合倒回去:刚才黑下的,变回黑下;白下的变回白下
        if(CHESS_FLAG == 1){
            CHESS_FLAG = 2;
        }else{
            CHESS_FLAG = 1;
        }

        // 3. 清空记录,防止重复悔棋
        lastR = -1;
        lastC = -1;

        // 4. 刷新棋盘画面,自动重新画所有棋子
        panel.repaint();

        showMessage("悔棋成功");
    }

repaint与paint的关系:

repaint是Swing自带的刷新方法,paint是自己写的方法

你不能直接调用 paint (),你只能 喊 repaint (),系统自动帮你调用 paint ()

repaint 是发通知,paint 是真正画图;你调用 repaint,系统自动帮你跑 paint。

自动执行paint的时候(此时不用写repaint):窗口的 放大、缩小、被别的窗口挡住再移开、最小化再还原

不会自动执行(要写repaint):悔棋、AI 自动下棋、清空棋盘

(4).绑定按钮

else if (a.equals("悔棋")) {
    huiQi();
}

(5).把棋盘面板传给监听器

在GoBangUI中加上:

gbl.panel = gp;

解释:GoBangUI里创建了棋盘GoPanel  gp ,把棋盘 gp 交给监听器里的 panel 变量,这样悔棋时才能调用 panel.repaint() 刷新画面。


三.完整代码

GoBangUI:

package 五子棋3;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionListener;

public class GoBangUI {
    GoBangListener gbl=new GoBangListener();
    public void GoUI(){
        JFrame jf=new JFrame();
        jf.setTitle("五子棋");
        jf.setSize(1050, 800);
        jf.setLocationRelativeTo(null);
        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        //添加面板
        GoPanel gp=new GoPanel();
        jf.add(gp);

        //添加右边面板
        JPanel right=new JPanel();
        right.setPreferredSize(new Dimension(260,0));
        right.setBackground(Color.GRAY);
        jf.add(right,BorderLayout.EAST);

        String[] text={"开始游戏","悔棋","AI对战"};
        for(int i=0;i<text.length;i++){
            JButton btn=new JButton(text[i]);
            right.add(btn);
            //给按钮加点击监听(点按钮会触发动作)。按钮被点击时,交给 gol 处理
            btn.addActionListener(gbl);
            if(i==0) {
                btn.setBackground(Color.GREEN);
            }else{
                btn.setBackground(Color.WHITE);
            }

        }
        jf.setVisible(true);

        //传递画笔,绑定监听器
        Graphics g = gp.getGraphics();  // 获取棋盘面板的画笔对象
        gbl.g = g;                      // 把画笔交给监听器(监听器用它画棋子)
        gp.addMouseListener(gbl);       // 给棋盘面板绑定鼠标监听器

        gp.setChessArr(gbl.chessArr);  // 把监听器里的真实棋盘 交给面板画画

        gbl.panel = gp;

    }

    public static void main(String[] args) {
        GoBangUI ui=new GoBangUI();
        ui.GoUI();
    }
}

GoBangListener:

package 五子棋3;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

public class GoBangListener implements MouseListener, ActionListener,GoData {
    Graphics g;
    int CHESS_FLAG=0;
    int[][] chessArr=new int[ROW+1][COL+1];

    // 记住上一步棋子的行、列
    int lastR = -1;
    int lastC = -1;
    // 拿到棋盘面板,用来刷新画面
    GoPanel panel;

    @Override
    public void mouseClicked(MouseEvent e) {

    }

    @Override
    public void mousePressed(MouseEvent e) {

        if(CHESS_FLAG==0){
            showMessage("请点开始游戏");
            return;
        }

        int x=e.getX();
        int y=e.getY();
        System.out.println("按下~");
        System.out.println("x=" + x + " y=" + y);
        //坐标换算,目的是确定落子位置
        int r=(y - Y + SIZE / 2) / SIZE;
        int c=(x - X + SIZE / 2) / SIZE;
        System.out.println(r+" "+c);
        if (r > ROW || c > COL) {
            showMessage("请在棋盘范围内下棋");
            return;
        }
        //绘制棋子
        if(chessArr[r][c]==0){
            chessArr[r][c]=CHESS_FLAG;//将棋子颜色存入数组中
            // 把当前落子位置记下来,给悔棋用
            lastR = r;
            lastC = c;
        }
        else {
            showMessage("请不要重复下棋");
            return;
        }
        if(CHESS_FLAG==1){
            g.setColor(Color.BLACK);
            CHESS_FLAG=2;
        }else if(CHESS_FLAG==2){
            g.setColor(Color.WHITE);
            CHESS_FLAG=1;
        }
        int cx=c * SIZE + X - SIZE / 2;
        int cy=r * SIZE + Y - SIZE / 2;
        g.fillOval(cx,cy,SIZE,SIZE);

        //判断输赢
        GoWin win=new GoWin();
        if(win.checkWin(chessArr,r,c)){
            if(chessArr[r][c]==1){
                showMessage("黑棋赢");
            }else if(chessArr[r][c]==2){
                showMessage("白棋赢");
            }
            CHESS_FLAG = 0; // 禁止继续下棋
        }
    }

    @Override
    public void mouseReleased(MouseEvent e) {
        
    }

    @Override
    public void mouseEntered(MouseEvent e) {

    }

    @Override
    public void mouseExited(MouseEvent e) {

    }

    @Override
    public void actionPerformed(ActionEvent e) {
        String a = e.getActionCommand();
        JButton btn=(JButton) e.getSource();


        if(a.equals("开始游戏")){
            for(int i=0;i<=ROW;i++){
                for(int j=0;j<=COL;j++){
                    if(chessArr[i][j]!=0){
                        chessArr[i][j]=0;
                    }
                }
            }
            //重新绘制棋盘,覆盖之前的棋盘,实现重绘
            g.setColor(new Color(218, 131, 60));
            g.fillRect(X - SIZE / 2, Y - SIZE / 2, COL * SIZE + SIZE, ROW * SIZE + SIZE);
            g.setColor(Color.BLACK);
            for (int i = 0; i <= ROW; i++) {
                g.drawLine(X, Y + i * SIZE, X + COL * SIZE, Y + i * SIZE);
                g.drawLine(X + i * SIZE, Y, X + i * SIZE, Y + ROW * SIZE);
            }

            btn.setText("结束对局");
            CHESS_FLAG=1;
            btn.setBackground(Color.RED);
        }
        else if (a.equals("结束对局")) {
            btn.setText("开始游戏");
            btn.setBackground(Color.GREEN);
            CHESS_FLAG=0;
            showMessage("游戏已结束");
        } else if (a.equals("悔棋")) {
            huiQi();
        } else if (a.equals("AI对战")) {

        }

    }

    // 悔棋功能方法
    public void huiQi(){
        // 如果没点开始游戏,不让悔棋
        if(CHESS_FLAG == 0){
            showMessage("请先开始游戏");
            return;
        }
        // 还没下过棋,不能悔棋
        if(lastR == -1 || lastC == -1){
            showMessage("还没有棋子可以悔棋");
            return;
        }

        // 1. 把上一步棋子从数组清空(相当于拿走棋子)
        chessArr[lastR][lastC] = 0;

        // 2. 回合倒回去:刚才黑下的,变回黑下;白下的变回白下
        if(CHESS_FLAG == 1){
            CHESS_FLAG = 2;
        }else{
            CHESS_FLAG = 1;
        }

        // 3. 清空记录,防止重复悔棋
        lastR = -1;
        lastC = -1;

        // 4. 刷新棋盘画面,自动重新画所有棋子
        panel.repaint();//强制调用GoPanel里的paint()方法,让GoPanel重新画一遍

        showMessage("悔棋成功");
    }
}

GoPanel:

package 五子棋3;

import javax.swing.*;
import java.awt.*;

public class GoPanel extends JPanel implements GoData{
    // 在这里加一个数组,用来接收
    int[][] chessArr;

    // 加一个方法,让外面把数组传进来
    public void setChessArr(int[][] chessArr) {
        this.chessArr = chessArr;
    }

    public void paint (Graphics g){
        super.paint(g);
        //棋盘背景
        g.setColor(new Color(218, 131, 60));
        g.fillRect(X - SIZE / 2, Y - SIZE / 2, COL * SIZE + SIZE, ROW * SIZE + SIZE);
        g.setColor(Color.BLACK);

        //画棋盘线
        for(int i=0;i<=ROW;i++){
        g.drawLine(X,Y+i*SIZE,X+ROW*SIZE,Y+i*SIZE);
        g.drawLine(X+i*SIZE,Y,X+i*SIZE,Y+COL*SIZE);
    }

        //遍历数组,将数组中的棋子重绘
        for(int i=0;i<chessArr.length;i++){
            for(int j=0;j<chessArr.length;j++){
                if(chessArr[i][j]!=0) {
                    if (chessArr[i][j] == 1) {
                        g.setColor(Color.BLACK);
                    } else if (chessArr[i][j] == 2) {
                        g.setColor(Color.WHITE);
                    }
                    int cx, cy;
                    cx = j * SIZE + X - SIZE / 2;
                    cy = i * SIZE + Y - SIZE / 2;
                    g.fillOval(cx, cy, SIZE, SIZE);
                }
            }
        }
}
}

GoData:

package 五子棋3;

import javax.swing.*;

public interface GoData {
    //棋盘左上角坐标
    int X=50;
    int Y=50;
    //每个格子的大小
    int SIZE=40;
    //棋盘15*15格
    int ROW=15;//15行
    int COL=15;//15列

    public default void showMessage(String msg){
        JOptionPane.showMessageDialog(null,msg);
    }


}

GoWin:

package 五子棋3;

public class GoWin {
    public boolean checkWin(int[][] chessArr,int r,int c) {//注意:这里的chessArr是形参,和前面的chessArr不是一个
        if (col(chessArr, r, c) >= 5 || row(chessArr, r, c) >= 5 ||
                leftUp(chessArr, r, c) >= 5 || rightUp(chessArr, r, c) >= 5) {
            return true;
        } else {
            return false;
        }
    }


        //纵向
        public int col(int[][] chessArr,int r,int c){
            int count=1;
            //向上查找
            for(int i=r-1;i>=0;i--){
                if(chessArr[i][c]==chessArr[r][c]) count++;
                else {break;}
            }
            //向下查找
            for(int i=r+1;i<chessArr.length;i++){
                if(chessArr[i][c]==chessArr[r][c]) count++;
                else {break;}
            }
            return count;
        }

        //横向
        public int row(int[][] chessArr,int r,int c){
            int count=1;
            //向左查找
            for(int i=c-1;i>=0;i--){
                if(chessArr[r][i]==chessArr[r][c]) count++;
                else {break;}
            }
            //向右查找
            for(int i=c+1;i<chessArr[0].length;i++){
                if(chessArr[r][i]==chessArr[r][c]) count++;
                else {break;}
            }
            return count;
        }

        //左上-右下
        public int leftUp(int[][] chessArr,int r,int c) {
        int count=1;
        //左上找
        for(int i=r-1,j=c-1;i>=0&&j>=0;i--,j--){
                if(chessArr[i][j]==chessArr[r][c]) {
                count++;
                }
                else{break;}
        }
        //右下找
            for(int i=r+1,j=c+1;i<chessArr.length&&j<chessArr[0].length;i++,j++){
                    if(chessArr[i][j]==chessArr[r][c]) {
                        count++;
                    }else{break;}
            }
        return count;
        }

    //右上-左下
    public int rightUp(int[][] chessArr,int r,int c) {
        int count=1;
        //右上找
        for(int i=r-1,j=c+1;i>=0&&j<chessArr[0].length;i--,j++){
                if(chessArr[i][j]==chessArr[r][c]) {
                    count++;
                }
                else{break;}

        }
        //左下找
        for(int i=r+1,j=c-1;i<chessArr.length&&j>=0;i++,j--){
                if(chessArr[i][j]==chessArr[r][c]) {
                    count++;
                }else{break;}
        }
        return count;
    }

}

四.思考:

关于GoData接口

错误做法:

在GoData接口中定义数组chessArr:

    int[][] chessArr=new int[ROW+1][COL+1];

注意:不能在GoData接口里定义数组,这是错误的

因为:接口里的变量 → 默认是 public static final(常量)常量数组 = 一旦创建,永远不能真正清空、永远不能重新初始化所以你点 “开始游戏” 清空棋盘是无效的

接口只放常量和抽象规则,不放数组。接口更像是一份“契约”或“菜单”,它应该用来定义 “能做什么” (比如 showMessage 弹窗),而不是去实现 “怎么做” 的具体业务逻辑(比如怎么用 for 循环去遍历清空)。

正确的做法:

1. 在 GoBangListener 里创建数组chessArr

2. 给 GoPanel 加一个 “接收数组” 的方法

3.关键一步:GoBangUI 里把数组传给面板

总结:

接口只放常量,不放数组

数组在监听器里定义,面板需要时,从监听器拿过去

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐