软件实习2020-12-22

又经过四天的摸索和学习,对第四个实验“实现迷宫游戏”有了新的认识。
我一开始自己写了一个版本,但是效果并不好,只实现了地图的随机生成和棋子的控制移动。
自己的版本:
程序代码分为两部分,第一部分为地图的生成,以Text类实现;第二部分为棋子类,以Piece类实现。
Text类:
1.初始化数组,以数组为基础建立地图
public int[][] createMazeData() {
mazeData = new int[height][width];// 初始化迷宫,给迷宫添加一圈外墙
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
if (x == 0 || x == width - 1 || y == 0 || y == height - 1) {
mazeData[y][x] = WALL;
} else if (x == 0 && y == 1) {
mazeData[y][x] = ENTRANCE;
} else if (x == width - 1 && y == height - 2) {
mazeData[y][x] = EXIT;
} else
mazeData[y][x] = ROUND;
}
}
division(1, 1, width - 2, height - 2);
// 设置起点和终点
mazeData[1][0] = ENTRANCE;
mazeData[height - 2][width - 1] = EXIT;
return mazeData;
}
2.使用递归分割算法生成随机地图
private void division(int startX, int startY, int endX, int endY) {

	Random random = new Random();

	// 如果迷宫的宽度或者高度小于2了就不能再分割了
	if (endX - startX < 2 || endY - startY < 2)
		return;

	// x,y只能是偶数
	int posX = startX + 1 + random.nextInt((endX - startX) / 2) * 2;// 纵向分割分割线的横坐标
	int posY = startY + 1 + random.nextInt((endY - startY) / 2) * 2;// 横向分割线的纵坐标
	for (int i = startX; i <= endX; i++) // 横向分割
		mazeData[posY][i] = WALL;
	for (int i = startY; i <= endY; i++) // 纵向分割
		mazeData[i][posX] = WALL;
	// 递归
	division(startX, startY, posX - 1, posY - 1);// 左下区域
	division(startX, posY + 1, posX - 1, endY);// 左上区域
	division(posX + 1, posY + 1, endX, endY);// 右上区域
	division(posX + 1, startY, endX, posY - 1);// 右下区域

	// 随机打开三扇门
	switch (random.nextInt(4)) {// 生成随机数0~3
	case 0:
		openDoor(startX, posY, posX - 1, posY); // 开左边的墙
		openDoor(posX, posY + 1, posX, endY); // 开上方的墙
		openDoor(posX + 1, posY, endX, posY); // 开右边的墙
		break;
	case 1:
		openDoor(posX, posY + 1, posX, endY); // 开上方的墙
		openDoor(posX + 1, posY, endX, posY); // 开右边的墙
		openDoor(posX, startY, posX, posY - 1);// 开下面的墙
		break;
	case 2:
		openDoor(posX + 1, posY, endX, posY); // 开右边的墙
		openDoor(posX, startY, posX, posY - 1);// 开下面的墙
		openDoor(startX, posY, posX - 1, posY); // 开左边的墙
		break;
	case 3:
		openDoor(posX, startY, posX, posY - 1);// 开下面的墙
		openDoor(startX, posY, posX - 1, posY); // 开左边的墙
		openDoor(posX, posY + 1, posX, endY); // 开上方的墙
		break;
	}
}


 // 在指定的一面墙上开一个随机的门

private void openDoor(int startX, int startY, int endX, int endY) {
	Random random = new Random();
	int x;// 开门的横坐标
	int y;// 开门的纵坐标

	// 墙是横着的
	if (startY == endY) {
		x = startX + random.nextInt((endX - startX) / 2 + 1) * 2;
		mazeData[startY][x] = ROUND;
	}
	// 墙是竖着的
	if (startX == endX) {
		y = startY + random.nextInt((endY - startY) / 2 + 1) * 2;// 在奇数墙上开门
		mazeData[y][startX] = ROUND;
	}
}

3.打印迷宫
public static void printMaze(int[][] data) {
for (int i = 0; i < data.length; i++) {
for (int j = 0; j < data[0].length; j++) {
if (data[i][j] == 0) {
System.out.print("[]");
} else if (data[i][j] == 1) {
System.out.print(" “);
} else if (data[i][j] == 2) {
System.out.print(”@");
} else
System.out.print("*");
}
System.out.println();// 换行
}
}

Piece类:
1.控制棋子移动
switch (accept) {
case “w”: // 向上走
if (mazeData[piece1.getX() - 1][piece1.getY()] != 0) {
mazeData[piece1.getX()][piece1.getY()] = 1;
mazeData[piece1.getX() - 1][piece1.getY()] = 2;
piece1.x1 = piece1.getX() - 1;
}
break;
case “s”: //向下走
if (mazeData[piece1.getX() + 1][piece1.getY()] != 0) {
mazeData[piece1.getX()][piece1.getY()] = 1;
mazeData[piece1.getX() + 1][piece1.getY()] = 2;
piece1.x1 = piece1.getX() + 1;
}
break;
case “a”: // 向左走
if (mazeData[piece1.getX()][piece1.getY() - 1] != 0) {
mazeData[piece1.getX()][piece1.getY()] = 1;
mazeData[piece1.getX()][piece1.getY() - 1] = 2;
piece1.y1 = piece1.getY() - 1;
}
break;
case “d”: //向右走
if (mazeData[piece1.getX()][piece1.getY() + 1] != 0) {
mazeData[piece1.getX()][piece1.getY()] = 1;
mazeData[piece1.getX()][piece1.getY() + 1] = 2;
piece1.y1 = piece1.getY() + 1;
}
break;

		default:// 违规操作或没路可走不进行任何操作
			System.out.println();
			break;

		}

2.每走一步,画一次地图
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array[0].length; j++) {
if (array[i][j] == 0) {
System.out.print("[]");
} else if (array[i][j] == 1) {
System.out.print(" “);
} else if (array[i][j] == 2) {
System.out.print(”@");
} else if (array[i][j] == 3) {
System.out.print("*");
}

			}

			System.out.println();
		}

完整代码:
Text类:
package cn.edu.Maze;

import java.util.Random;

/**

  • 递归分割生成迷宫
  • @author SongErrors

*/
public class Text {

protected static int[][] mazeData;// 迷宫数据

private final int WALL = 0;// 墙

private final int ROUND = 1;// 路

private final int ENTRANCE = 2;// 入口

private final int EXIT = 3;// 出口

protected int width;// 迷宫的宽度

protected int height;// 迷宫的高度

/**
 * 生成自定义大小的迷宫
 * 
 * @param width
 * @param height
 */
// 构造方法
public Text() {

}

// 构造方法
public Text(int width, int height) {
	this.width = width % 2 + 1 == 0 ? width : width + 1;
	this.height = height % 2 + 1 == 0 ? height : height + 1;
}

// 方法
public int getWIDTH() {
	return width;
}

public int getHEIGHT() {
	return height;
}

/**
 * 自动生成迷宫
 * 
 * @return
 */
public int[][] createMazeData() {
	mazeData = new int[height][width];// 初始化迷宫,给迷宫添加一圈外墙
	for (int y = 0; y < height; y++) {
		for (int x = 0; x < width; x++) {
			if (x == 0 || x == width - 1 || y == 0 || y == height - 1) {
				mazeData[y][x] = WALL;
			} else if (x == 0 && y == 1) {
				mazeData[y][x] = ENTRANCE;
			} else if (x == width - 1 && y == height - 2) {
				mazeData[y][x] = EXIT;
			} else
				mazeData[y][x] = ROUND;
		}
	}
	division(1, 1, width - 2, height - 2);
	// 设置起点和终点
	mazeData[1][0] = ENTRANCE;
	mazeData[height - 2][width - 1] = EXIT;
	return mazeData;
}

/**
 * 递归分割画迷宫
 *
 * @param startX:迷宫的起点横坐标
 * @param startY:迷宫的起点纵坐标
 * @param endX:迷宫的终点横坐标
 * @param endY:迷宫的终点纵坐标
 */
private void division(int startX, int startY, int endX, int endY) {

	Random random = new Random();

	// 如果迷宫的宽度或者高度小于2了就不能再分割了
	if (endX - startX < 2 || endY - startY < 2)
		return;

	// x,y只能是偶数
	int posX = startX + 1 + random.nextInt((endX - startX) / 2) * 2;// 纵向分割分割线的横坐标
	int posY = startY + 1 + random.nextInt((endY - startY) / 2) * 2;// 横向分割线的纵坐标
	for (int i = startX; i <= endX; i++) // 横向分割
		mazeData[posY][i] = WALL;
	for (int i = startY; i <= endY; i++) // 纵向分割
		mazeData[i][posX] = WALL;
	// 递归
	division(startX, startY, posX - 1, posY - 1);// 左下区域
	division(startX, posY + 1, posX - 1, endY);// 左上区域
	division(posX + 1, posY + 1, endX, endY);// 右上区域
	division(posX + 1, startY, endX, posY - 1);// 右下区域

	// 随机打开三扇门
	switch (random.nextInt(4)) {// 生成随机数0~3
	case 0:
		openDoor(startX, posY, posX - 1, posY); // 开左边的墙
		openDoor(posX, posY + 1, posX, endY); // 开上方的墙
		openDoor(posX + 1, posY, endX, posY); // 开右边的墙
		break;
	case 1:
		openDoor(posX, posY + 1, posX, endY); // 开上方的墙
		openDoor(posX + 1, posY, endX, posY); // 开右边的墙
		openDoor(posX, startY, posX, posY - 1);// 开下面的墙
		break;
	case 2:
		openDoor(posX + 1, posY, endX, posY); // 开右边的墙
		openDoor(posX, startY, posX, posY - 1);// 开下面的墙
		openDoor(startX, posY, posX - 1, posY); // 开左边的墙
		break;
	case 3:
		openDoor(posX, startY, posX, posY - 1);// 开下面的墙
		openDoor(startX, posY, posX - 1, posY); // 开左边的墙
		openDoor(posX, posY + 1, posX, endY); // 开上方的墙
		break;
	}
}

/**
 * 在指定的一面墙上开一个随机的门
 *
 * @param startX:迷宫开始的横坐标
 * @param startY:迷宫开始的纵坐标
 * @param endX:迷宫结束的横坐标
 * @param endY:迷宫结束的纵坐标
 */
private void openDoor(int startX, int startY, int endX, int endY) {
	Random random = new Random();
	int x;// 开门的横坐标
	int y;// 开门的纵坐标

	// 墙是横着的
	if (startY == endY) {
		x = startX + random.nextInt((endX - startX) / 2 + 1) * 2;
		mazeData[startY][x] = ROUND;
	}
	// 墙是竖着的
	if (startX == endX) {
		y = startY + random.nextInt((endY - startY) / 2 + 1) * 2;// 在奇数墙上开门
		mazeData[y][startX] = ROUND;
	}
}

public void setWidth(int width) {
	this.width = width % 2 + 1 == 0 ? width : width + 1;
}

public void setHeight(int height) {
	this.height = height % 2 + 1 == 0 ? height : height + 1;
}

/**
 * 打印迷宫
 * 
 * @param data
 */
public static void printMaze(int[][] data) {
	for (int i = 0; i < data.length; i++) {
		for (int j = 0; j < data[0].length; j++) {
			if (data[i][j] == 0) {
				System.out.print("[]");
			} else if (data[i][j] == 1) {
				System.out.print("  ");
			} else if (data[i][j] == 2) {
				System.out.print("@");
			} else
				System.out.print("*");
		}
		System.out.println();// 换行
	}
}

}

Piece类:
package cn.edu.Maze;

import java.util.Scanner;

public class Piece extends Text {
private int x1;
private int y1;
public static int[][] array;

// 构造方法
Piece() {
	x1 = 1;
	y1 = 0;// array[1][0]代表人物的初始位置

}

// 构造方法
Piece(int x1, int y1) {
	this.x1 = x1;
	this.y1 = y1;
}

// 方法
public int getX() {
	return x1;
}

// 方法
public int getY() {
	return y1;
}

// 程序开始
public static void main(String[] args) {
	Scanner in = new Scanner(System.in);
	System.out.println("Enter the width and height :");
	int width = in.nextInt(), height = in.nextInt();
	Text maze = new Text(width, height);
	Piece piece1 = new Piece();
	piece1.printMaze(maze.createMazeData());
	array = maze.createMazeData();

	while (true) {

		Scanner input = new Scanner(System.in);// 创建一个键盘接收器
		String accept = input.nextLine();// 接受键盘输入的内容
		// 判断人物角色的走向
		switch (accept) {
		case "w":// 向上走
			if (mazeData[piece1.getX() - 1][piece1.getY()] != 0) {
				mazeData[piece1.getX()][piece1.getY()] = 1;
				mazeData[piece1.getX() - 1][piece1.getY()] = 2;
				piece1.x1 = piece1.getX() - 1;
			}
			break;
		case "s":// 向下走
			if (mazeData[piece1.getX() + 1][piece1.getY()] != 0) {
				mazeData[piece1.getX()][piece1.getY()] = 1;
				mazeData[piece1.getX() + 1][piece1.getY()] = 2;
				piece1.x1 = piece1.getX() + 1;
			}
			break;
		case "a":// 向左走
			if (mazeData[piece1.getX()][piece1.getY() - 1] != 0) {
				mazeData[piece1.getX()][piece1.getY()] = 1;
				mazeData[piece1.getX()][piece1.getY() - 1] = 2;
				piece1.y1 = piece1.getY() - 1;
			}
			break;
		case "d":// 向右走
			if (mazeData[piece1.getX()][piece1.getY() + 1] != 0) {
				mazeData[piece1.getX()][piece1.getY()] = 1;
				mazeData[piece1.getX()][piece1.getY() + 1] = 2;
				piece1.y1 = piece1.getY() + 1;
			}
			break;

		default:// 违规操作或没路可走不进行任何操作
			System.out.println();
			break;

		}
		for (int i = 0; i < array.length; i++) {
			for (int j = 0; j < array[0].length; j++) {
				if (array[i][j] == 0) {
					System.out.print("[]");
				} else if (array[i][j] == 1) {
					System.out.print("  ");
				} else if (array[i][j] == 2) {
					System.out.print("@");
				} else if (array[i][j] == 3) {
					System.out.print("*");
				}

			}

			System.out.println();
		}
	}
}

}

之后我又找了一位大佬的资源,基本上能够把代码给搞懂,他的代码一开始没注释,我有些看不懂,我就试着联系了作者,说明了原因,作者第二天就把部分注释给加上了,我又把注释完善了下,以下是完整代码:
package maze;

import java.awt.Color;//绘制颜色的类
import java.awt.Graphics;//绘制图像
import java.awt.event.KeyAdapter;//适配器
import java.awt.event.KeyEvent;//键盘按键
import java.util.Random;//随机生成
import java.util.Stack;//栈操作
import javax.swing.JFrame;//创建图形化框架
import javax.swing.JOptionPane;//编写图形用户界面
import javax.swing.JPanel;//面板

class Lattice {
static final int INTREE = 1;
static final int NOTINTREE = 0;
private int x = -1;
private int y = -1;
private int flag = NOTINTREE;
private Lattice father = null;

// 构造方法
public Lattice(int xx, int yy) {
	x = xx;
	y = yy;
}

// 方法
public int getX() {
	return x;
}

public int getY() {
	return y;
}

public int getFlag() {
	return flag;
}

public void setFlag(int f) {
	flag = f;
}
public Lattice getFather() {
	return father;
}

public void setFather(Lattice f) {
	father = f;
}

public String toString() {
	return new String("(" + x + "," + y + ")\n");
}

}

public class Maze extends JPanel {
private int NUM, width, padding;// NUM 迷宫的大小,width 每个格子的宽度和高度,padding 边框
private Lattice[][] maze;// 创建数组
private int ballX, ballY;//球的位置
private boolean drawPath = false;//标识是否画出路径

// 构造方法
Maze(int m, int w, int p) {
	NUM = m;
	width = w;
	padding = p;
	maze = new Lattice[NUM][NUM];//在栈内开辟内存空间
	for (int i = 0; i <= NUM - 1; i++)
		for (int j = 0; j <= NUM - 1; j++)
			maze[i][j] = new Lattice(i, j);
	createMaze();//在一个类中,直接使用方法名调用方法
	setKeyListener();
	this.setFocusable(true);
}

// 方法
//初始化游戏,重开一局时使用
private void init() {
	for (int i = 0; i <= NUM - 1; i++)
		for (int j = 0; j <= NUM - 1; j++) {
			maze[i][j].setFather(null);
			maze[i][j].setFlag(Lattice.NOTINTREE);
		}
	ballX = 0;
	ballY = 0;//入口位置
	drawPath = false;
	createMaze();
	// setKeyListener();
	this.setFocusable(true);
	repaint();
}
// 由格子的行数,得到格子中心点的像素X座标
public int getCenterX(int x) {
	return padding + x * width + width / 2;
}
// 由格子的列数,得到格子中心点的像素Y座标
public int getCenterY(int y) {
	return padding + y * width + width / 2;
}
//重载
public int getCenterX(Lattice p) {
	return padding + p.getY() * width + width / 2;
}

public int getCenterY(Lattice p) {
	return padding + p.getX() * width + width / 2;
}

//检查是否赢得了游戏
private void checkIsWin() {
	if (ballX == NUM - 1 && ballY == NUM - 1) {
		JOptionPane.showMessageDialog(null, "YOU WIN !", "你走出了迷宫。", JOptionPane.PLAIN_MESSAGE);
		init();//重新开局
	}
}

synchronized private void move(int c) {// synchronized实现同步控制
	int tx = ballX, ty = ballY;
	// System.out.println(c);
	switch (c) {
	case KeyEvent.VK_LEFT://左
		ty--;
		break;
	case KeyEvent.VK_RIGHT://右
		ty++;
		break;
	case KeyEvent.VK_UP://上
		tx--;
		break;
	case KeyEvent.VK_DOWN://下
		tx++;
		break;
	case KeyEvent.VK_SPACE://空格建提示正确路径
		if (drawPath == true) {
			drawPath = false;
		} else {
			drawPath = true;
		}
		break;
	default:
	}
	// 若移动后未出界且格子之间有路径,则进行移动,更新小球位置,否则移动非法
	if (!isOutOfBorder(tx, ty)
			&& (maze[tx][ty].getFather() == maze[ballX][ballY] || maze[ballX][ballY].getFather() == maze[tx][ty])) {
		ballX = tx;
		ballY = ty;
	}
}

private void setKeyListener() {
	this.addKeyListener(new KeyAdapter() {
		public void keyPressed(KeyEvent e) {
			int c = e.getKeyCode();
			move(c);
			repaint();
			checkIsWin();

		}
	});
}
//方法重载,判断是否出界
private boolean isOutOfBorder(Lattice p) {
	return isOutOfBorder(p.getX(), p.getY());
}
//上面的方法调用后会调用此方法
private boolean isOutOfBorder(int x, int y) {
	return (x > NUM - 1 || y > NUM - 1 || x < 0 || y < 0) ? true : false;
}
//方法,获取格子的邻居格子
private Lattice[] getNeis(Lattice p) {
	final int[] adds = { -1, 0, 1, 0, -1 };// 顺序为上右下左
	if (isOutOfBorder(p)) {
		return null;
	}//如果小球已出界,则返回空值
	Lattice[] ps = new Lattice[4];//  四个邻居格子,顺序为上右下左,出界的邻居为null
	int xt;
	int yt;
	for (int i = 0; i <= 3; i++) {
		xt = p.getX() + adds[i];
		yt = p.getY() + adds[i + 1];
		if (isOutOfBorder(xt, yt))
			continue;
		ps[i] = maze[xt][yt];
	}
	return ps;
}
//方法,构建随机树,创建迷宫
private void createMaze() {
	// 随机选一个格子作为树的根
	Random random = new Random();
	int rx = Math.abs(random.nextInt()) % NUM;//Math.abs取绝对值
	int ry = Math.abs(random.nextInt()) % NUM;
	//深度优先遍历
	Stack<Lattice> s = new Stack<Lattice>();//创建一个对象s
	Lattice p = maze[rx][ry];
	Lattice neis[] = null;
	s.push(p);//压栈
	while (!s.isEmpty()) {
		p = s.pop();//出栈
		p.setFlag(Lattice.INTREE);
		neis = getNeis(p);
		int ran = Math.abs(random.nextInt()) % 4;
		for (int a = 0; a <= 3; a++) {
			ran++;
			ran %= 4;
			if (neis[ran] == null || neis[ran].getFlag() == Lattice.INTREE) {
				continue;
			}
			s.push(neis[ran]);
			neis[ran].setFather(p);
		}
	}
	// changeFather(maze[0][0],null);
}
// 抹掉两个格子之间的边
private void clearFence(int i, int j, int fx, int fy, Graphics g) {
	int sx = padding + ((j > fy ? j : fy) * width), sy = padding + ((i > fx ? i : fx) * width),
			dx = (i == fx ? sx : sx + width), dy = (i == fx ? sy + width : sy);
	if (sx != dx) {
		sx++;
		dx--;
	} else {
		sy++;
		dy--;
	}
	g.drawLine(sx, sy, dx, dy);
}
// 画迷宫
protected void paintComponent(Graphics g) {
	super.paintComponent(g);
	// 画NUM*NUM条黑线
	for (int i = 0; i <= NUM; i++) {
		g.drawLine(padding + i * width, padding, padding + i * width, padding + NUM * width);
	}//画竖线
	for (int j = 0; j <= NUM; j++) {
		g.drawLine(padding, padding + j * width, padding + NUM * width, padding + j * width);
	}//画横线
	// 使用背景色,在有路径的格子之间画边,把墙抹掉
	g.setColor(this.getBackground());
	for (int i = NUM - 1; i >= 0; i--) {
		for (int j = NUM - 1; j >= 0; j--) {
			Lattice f = maze[i][j].getFather();
			if (f != null) {
				int fx = f.getX(), fy = f.getY();
				clearFence(i, j, fx, fy, g);
			}
		}
	}
	// 画左上角的入口
	g.drawLine(padding, padding + 1, padding, padding + width - 1);
	int last = padding + NUM * width;
	// 画右下角出口
	g.drawLine(last, last - 1, last, last - width + 1);
	// 画小球
	g.setColor(Color.RED);
	g.fillOval(getCenterX(ballY) - width / 3, getCenterY(ballX) - width / 3, width / 2, width / 2);
	if (drawPath == true)
		drawPath(g);
}

private void drawPath(Graphics g) {
	Color PATH_COLOR = Color.ORANGE, BOTH_PATH_COLOR = Color.PINK;
	if (drawPath == true)
		g.setColor(PATH_COLOR);//正确路径
	else
		g.setColor(this.getBackground());//错误路径
	Lattice p = maze[NUM - 1][NUM - 1];
	while (p.getFather() != null) {
		p.setFlag(2);
		p = p.getFather();
	}
	g.fillOval(getCenterX(p) - width / 3, getCenterY(p) - width / 3, width / 2, width / 2);
	p = maze[0][0];
	while (p.getFather() != null) {
		if (p.getFlag() == 2) {
			p.setFlag(3);
			g.setColor(BOTH_PATH_COLOR);
		}
		g.drawLine(getCenterX(p), getCenterY(p), getCenterX(p.getFather()), getCenterY(p.getFather()));
		p = p.getFather();
	}
	g.setColor(PATH_COLOR);
	p = maze[NUM - 1][NUM - 1];
	while (p.getFather() != null) {
		if (p.getFlag() == 3)
			break;
		g.drawLine(getCenterX(p), getCenterY(p), getCenterX(p.getFather()), getCenterY(p.getFather()));
		p = p.getFather();
	}
}

public static void main(String[] args) {
	final int n = 30, width = 600, padding = 20, LX = 200, LY = 100;
	JPanel p = new Maze(n, (width - padding - padding) / n, padding);//生成一个对象p
	JFrame frame = new JFrame("MAZE(按空格键显示或隐藏路径)");//生成一个对象frame
	frame.getContentPane().add(p);//将面板添加到窗口上
	frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//关闭窗口
	frame.setSize(width + padding, width + padding + padding);//窗口大小
	frame.setLocation(LX, LY);//窗口生成位置
	frame.setVisible(true);//窗口可视
}

}
总结:
这次软件实习共历时约三周半,经过三个实验的洗礼,受到的打击也蛮大的,一想就明白,一写就完蛋,自身的知识深度和广度远远达不到要求,看来努力的道路还很漫长。
对了,补充一下,第二个完整代码的来源是一位大佬的文章,链接如下:
https://www.cnblogs.com/maxuewei2/p/5470157.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值