中国象棋--基于EasyX实现图形化

最近学习EasyX,在发现[PE]经典八炮的发布的一个中国象棋算法部分的代码后,于是尝试使用EasyX实现图形化。

另外一点小提示:如果想要实现本代码,需要安装EasyX,安装方法自行百度。

算法部分代码来自:C++实现双人中国象棋(一)——算法篇(附完整代码)_[PE]经典八炮的博客-CSDN博客_c++象棋代码s​​​​​​​z最近突发奇想,要使用C++做一个双人象棋的程序,昨天肝了一天,终于把算法部分完成了,下面把开发过程中的经验分享一下。知识要求:熟练掌握C++语言面向对象编程的知识(继承,多态)掌握STL的基本操作了解中国象棋基本规则(不会还有人不知道中国象棋规则吧!)..................https://blog.csdn.net/qq_54121864/article/details/125652386?spm=1001.2014.3001.5502直接贴源码吧:

#include<string>
#include<list>
#include<string>
#include<algorithm>

using namespace std;

const bool BLACK_CHESS = 0, RED_CHESS = 1;
const uint8_t DRAW = 2, NONE = 3;

class Point {
public:
	int8_t x, y;

	Point(int8_t nx, int8_t ny) {
		this->x = nx;
		this->y = ny;
	}
	bool ColorOfTheArea() const {
		if (y <= 4) {
			return RED_CHESS;
		}
		return BLACK_CHESS;
	}
	bool IsInNinePalaces()const {
		return x >= 3 && x <= 5 && (y <= 2 || y >= 7);
	}
};

bool operator==(const Point& a, const Point& b) {
	return a.x == b.x && a.y == b.y;
}

class ChessBoard {
private:
	friend class ChessPiece;
	ChessPiece* board[10][9];
	list<ChessPiece*> red, black;
public:
	ChessBoard();
	const list<ChessPiece*>& GetRedPieces() const {
		return red;
	}
	const list<ChessPiece*>& GetBlackPieces() const {
		return black;
	}
	ChessPiece*& GetChess(const Point& point) {
		return board[point.y][point.x];
	}
	ChessPiece* const& GetChess(const Point& point) const {
		return board[point.y][point.x];
	}
	void RemoveChess(const Point& point);
	bool KingsFaceToFace() const;
	~ChessBoard();
};

class ChessPiece {
protected:
	Point pt;
	ChessBoard& board;
public:
	const bool cl;
	ChessPiece(const Point& point, bool color, ChessBoard& chessboard) :pt(point), cl(color), board(chessboard) {
		if (cl == BLACK_CHESS) {
			board.black.push_back(this);
		}
		else {
			board.red.push_back(this);
		}
		board.GetChess(pt) = this;
	}
	const Point& GetPoint() const {
		return pt;
	}

	virtual bool CanMoveTo(const Point& point)const = 0;
	virtual const char* GetName() = 0;
	virtual const bool CanCrossTheRiver() const = 0;

	bool MoveTo(const Point& point) {
		if (CanMoveTo(point)) {
			board.GetChess(pt) = nullptr;
			pt.x = point.x;
			pt.y = point.y;
			board.RemoveChess(point);
			board.GetChess(point) = this;
			return true;
		}
		return false;
	}
};

class Rook :public ChessPiece {
public:
	Rook(const Point& point, bool color, ChessBoard& chessboard) :ChessPiece(point, color, chessboard) {}
	virtual bool CanMoveTo(const Point& point)const override {
		if (board.GetChess(point) == nullptr || board.GetChess(point)->cl != this->cl) {
			if (point.x == pt.x) {
				if (point.y < pt.y) {
					for (uint8_t i = point.y + 1; i < pt.y; i++) {
						if (board.GetChess(Point(point.x, i))) {
							return false;
						}
					}
				}
				else {
					for (uint8_t i = pt.y + 1; i < point.y; i++) {
						if (board.GetChess(Point(point.x, i))) {
							return false;
						}
					}
				}
				return true;
			}
			else if (point.y == pt.y) {
				if (point.x < pt.x) {
					for (uint8_t i = point.x + 1; i < pt.x; i++) {
						if (board.GetChess(Point(i, point.y))) {
							return false;
						}
					}
				}
				else {
					for (uint8_t i = pt.x + 1; i < point.x; i++) {
						if (board.GetChess(Point(i, point.y))) {
							return false;
						}
					}
				}
				return true;
			}
		}
		return false;
	}
	virtual const char* GetName() {
		return "車";
	}
	virtual const bool CanCrossTheRiver() const {
		return true;
	}
};


class Horse :public ChessPiece {
public:
	Horse(const Point& point, bool color, ChessBoard& chessboard) :ChessPiece(point, color, chessboard) {}
	virtual bool CanMoveTo(const Point& point)const override {
		static const Point s[8] = {
			{2, 1},
			{2, -1},
			{-2, -1},
			{-2, -1},
			{1, 2},
			{-1, 2},
			{1, -2},
			{-1, -2}
		},
			u[8] = {
				{1, 0},
				{1, 0},
				{-1, 0},
				{-1, 0},
				{0, 1},
				{0, 1},
				{0, -1},
				{0, -1}
		};
		if (board.GetChess(point) == nullptr || board.GetChess(point)->cl != this->cl) {
			for (size_t i = 0; i < 8; i++) {
				if (point == Point(pt.x + s[i].x, pt.y + s[i].y) && board.GetChess(Point(pt.x + u[i].x, pt.y + u[i].y)) == nullptr) {
					return true;
				}
			}
		}
		return false;
	}
	virtual const char* GetName() {
		return "馬";
	}
	virtual const bool CanCrossTheRiver() const {
		return true;
	}
};

class Cannon : public ChessPiece {
public:
	Cannon(const Point& point, bool color, ChessBoard& chessboard) : ChessPiece(point, color, chessboard) {}
	virtual bool CanMoveTo(const Point& point) const override {
		if (board.GetChess(point) == nullptr) {
			if (point.x == pt.x) {
				if (point.y < pt.y) {
					for (uint8_t i = point.y + 1; i < pt.y; i++) {
						if (board.GetChess(Point(point.x, i))) {
							return false;
						}
					}
				}
				else {
					for (uint8_t i = pt.y + 1; i < point.y; i++) {
						if (board.GetChess(Point(point.x, i))) {
							return false;
						}
					}
				}
				return true;
			}
			else if (point.y == pt.y) {
				if (point.x < pt.x) {
					for (uint8_t i = point.x + 1; i < pt.x; i++) {
						if (board.GetChess(Point(i, point.y))) {
							return false;
						}
					}
				}
				else if (pt.x < point.x) {
					for (uint8_t i = pt.x + 1; i < point.x; i++) {
						if (board.GetChess(Point(i, point.y))) {
							return false;
						}
					}
				}
				return true;
			}
			return false;
		}
		else if (board.GetChess(point)->cl != this->cl) {
			uint8_t count = 0;
			if (point.x == pt.x) {
				if (point.y < pt.y) {
					for (uint8_t i = point.y + 1; i < pt.y; i++) {
						if (board.GetChess(Point(point.x, i))) {
							count++;
						}
					}
				}
				else {
					for (uint8_t i = pt.y + 1; i < point.y; i++) {
						if (board.GetChess(Point(point.x, i))) {
							count++;
						}
					}
				}

			}
			else if (point.y == pt.y) {
				if (point.x < pt.x) {
					for (uint8_t i = point.x + 1; i < pt.x; i++) {
						if (board.GetChess(Point(i, point.y))) {
							count++;
						}
					}

				}
				else if (pt.x < point.x) {
					for (uint8_t i = pt.x + 1; i < point.x; i++) {
						if (board.GetChess(Point(i, point.y))) {
							count++;
						}
					}
				}

			}
			if (count == 1) {
				return true;
			}
		}
		return false;
	}
	virtual const char* GetName() {
		return "炮";
	}
	virtual const bool CanCrossTheRiver() const {
		return true;
	}
};

class Elephant :public ChessPiece {
public:
	Elephant(const Point& point, bool color, ChessBoard& chessboard) :ChessPiece(point, color, chessboard) {}
	virtual bool CanMoveTo(const Point& point) const override {
		static const Point s[4] = {
			{2,2},
			{2, -2},
			{-2, 2},
			{-2, -2}
		}, u[4] = {
			{1,1},
			{1, -1},
			{-1,1},
			{-1, -1}
		};
		if (board.GetChess(point) == nullptr || board.GetChess(point)->cl != this->cl) {
			if (cl == point.ColorOfTheArea()) {
				for (size_t i = 0; i < 4; i++) {
					if (point == Point(pt.x + s[i].x, pt.y + s[i].y) && board.GetChess(Point(pt.x + u[i].x, pt.y + u[i].y)) == nullptr) {
						return true;
					}
				}
			}
		}
		return false;
	}

	virtual const char* GetName() {
		return cl == BLACK_CHESS ? "象" : "相";
	}
	virtual const bool CanCrossTheRiver()const {
		return false;
	}
};

class Adviser :public ChessPiece {
public:
	Adviser(const Point& point, bool color, ChessBoard& chessboard) :ChessPiece(point, color, chessboard) {

	}
	virtual bool CanMoveTo(const Point& point)const override {
		static const Point s[4] = {
			{1,1},
			{1,-1},
			{-1, 1},
			{-1,-1}
		};
		if (board.GetChess(point) == nullptr || board.GetChess(point)->cl != this->cl) {
			if (cl == point.ColorOfTheArea() && point.IsInNinePalaces()) {
				for (size_t i = 0; i < 4; i++) {
					if (point == Point(pt.x + s[i].x, pt.y + s[i].y)) {
						return true;
					}
				}
			}
			return false;
		}
		return false;
	}
	virtual const char* GetName() {
		return "士";
	}
	virtual const bool CanCrossTheRiver()const {
		return false;
	}
};

class Pawn :public ChessPiece {
public:
	Pawn(const Point& point, bool color, ChessBoard& chessboard) :ChessPiece(point, color, chessboard) {

	}
	virtual bool CanMoveTo(const Point& point)const override {
		if (board.GetChess(point) == nullptr || board.GetChess(point)->cl != this->cl) {
			int8_t front = (cl == RED_CHESS ? 1 : -1);
			if (cl == pt.ColorOfTheArea()) {
				return point == Point(pt.x, pt.y + front);
			}
			static const Point s[3] = {
				{0, front},
				{1, 0},
				{-1, 0}
			};
			for (size_t i = 0; i < 4; i++) {
				if (point == Point(pt.x + s[i].x, pt.y + s[i].y)) {
					return true;
				}
			}
		}
		return false;
	}
	virtual const char* GetName() {
		return cl == BLACK_CHESS ? "卒" : "兵";
	}
	virtual const bool CanCrossTheRiver()const {
		return true;
	}
};

class King :public ChessPiece {
public:
	King(const Point& point, bool color, ChessBoard& chessboard) :ChessPiece(point, color, chessboard) {

	}
	virtual bool CanMoveTo(const Point& point)const override {
		static const Point s[4] = {
			{0,1},
			{0,-1},
			{1, 0},
			{-1, 0}
		};
		if (board.GetChess(point) == nullptr || board.GetChess(point)->cl != this->cl) {
			if (point.IsInNinePalaces() && point.ColorOfTheArea() == cl) {
				for (size_t i = 0; i < 4; i++) {
					if (point == Point(pt.x + s[i].x, pt.y + s[i].y)) {
						return true;
					}
				}
			}
		}
		return false;
	}
	virtual const char* GetName() {
		return cl == BLACK_CHESS ? "将" : "帥";
	}
	virtual const bool CanCrossTheRiver()const {
		return false;
	}
};


ChessBoard::ChessBoard() {
	memset(board, 0, sizeof(board));
	new King(Point(4, 0), RED_CHESS, *this);
	new King(Point(4, 9), BLACK_CHESS, *this);

	new Adviser(Point(3, 0), RED_CHESS, *this);
	new Adviser(Point(5, 0), RED_CHESS, *this);
	new Adviser(Point(3, 9), BLACK_CHESS, *this);
	new Adviser(Point(5, 9), BLACK_CHESS, *this);

	new Elephant(Point(2, 0), RED_CHESS, *this);
	new Elephant(Point(6, 0), RED_CHESS, *this);
	new Elephant(Point(2, 9), BLACK_CHESS, *this);
	new Elephant(Point(6, 9), BLACK_CHESS, *this);

	new Horse(Point(1, 0), RED_CHESS, *this);
	new Horse(Point(7, 0), RED_CHESS, *this);
	new Horse(Point(1, 9), BLACK_CHESS, *this);
	new Horse(Point(7, 9), BLACK_CHESS, *this);

	new Rook(Point(0, 0), RED_CHESS, *this);
	new Rook(Point(8, 0), RED_CHESS, *this);
	new Rook(Point(0, 9), BLACK_CHESS, *this);
	new Rook(Point(8, 9), BLACK_CHESS, *this);

	new Cannon(Point(1, 2), RED_CHESS, *this);
	new Cannon(Point(7, 2), RED_CHESS, *this);
	new Cannon(Point(1, 7), BLACK_CHESS, *this);
	new Cannon(Point(7, 7), BLACK_CHESS, *this);

	new Pawn(Point(0, 3), RED_CHESS, *this);
	new Pawn(Point(2, 3), RED_CHESS, *this);
	new Pawn(Point(4, 3), RED_CHESS, *this);
	new Pawn(Point(6, 3), RED_CHESS, *this);
	new Pawn(Point(8, 3), RED_CHESS, *this);
	new Pawn(Point(0, 6), BLACK_CHESS, *this);
	new Pawn(Point(2, 6), BLACK_CHESS, *this);
	new Pawn(Point(4, 6), BLACK_CHESS, *this);
	new Pawn(Point(6, 6), BLACK_CHESS, *this);
	new Pawn(Point(8, 6), BLACK_CHESS, *this);
}
// 将RemoveChess 放在ChessPiece之后能防止报未定义未定义ChessPiece的错误

void ChessBoard::RemoveChess(const Point& point) {
	if (GetChess(point)) {
		if (GetChess(point)->cl == RED_CHESS) {
			red.erase(find(red.begin(), red.end(), GetChess(point)));
		}
		else {
			black.erase(find(black.begin(), black.end(), GetChess(point)));
		}
	}
	delete GetChess(point);
	GetChess(point) = nullptr;
}

bool ChessBoard::KingsFaceToFace() const {
	auto r = find_if(red.begin(), red.end(), [](ChessPiece* p) {return !strcmp(p->GetName(), "帥"); }),
		b = find_if(black.begin(), black.begin(), [](ChessPiece* p) {return !strcmp(p->GetName(), "将"); });
	if (r != red.end() && b != black.end()) {
		if ((*r)->GetPoint().x == (*b)->GetPoint().x) {
			for (uint8_t i = (*r)->GetPoint().y + 1; i < (*b)->GetPoint().y; i++) {
				if (GetChess(Point((*r)->GetPoint().x, i))) {
					return false;
				}
			}
			return true;
		}
	}
	return false;
}

ChessBoard::~ChessBoard() {
	for (ChessPiece* p : red) {
		delete p;
	}
	for (ChessPiece* p : black) {
		delete p;
	}
}

算法代码部分只做了一些小修改,如GetName() 函数中函数体后面加const ,_stprintf_s会读取失败。

#pragma once

#include<graphics.h>
#include"Chess.hpp"

const int BLOCK_SIZE = 55;
const int RowNum = 11;
const int COLNum = 10;

class Game
{
private:
	bool nextPlayer;
	ChessBoard board;
	Point* firstPoint, *secondPoint;	// 鼠标点击两次的位置
public:
	Game() {
		nextPlayer = RED_CHESS;
		firstPoint = new Point(-1, -1);
		secondPoint = new Point(-1, -1);
	}

	bool Move(const Point& a, const Point& b) {
		if (board.GetChess(a) && board.GetChess(a)->cl == nextPlayer) {
			if (board.GetChess(a)->MoveTo(b)) {
				nextPlayer = !nextPlayer;
				return true;
			}
			return false;
		}
		return false;
	}
	const ChessBoard& GetBoard() const {
		return board;
	}

	uint8_t GetWinner() const {
		if (board.KingsFaceToFace()) {
			return nextPlayer;
		}
		if (find_if(board.GetRedPieces().begin(), board.GetRedPieces().end(), [](ChessPiece* p) {return !strcmp(p->GetName(),"帥"); }) == board.GetRedPieces().end()) {
			return BLACK_CHESS;

		}
		if (find_if(board.GetBlackPieces().begin(), board.GetBlackPieces().end(), [](ChessPiece* p) {return !strcmp(p->GetName(), "将"); }) == board.GetBlackPieces().end()) {
			return RED_CHESS;
		}
		if (count_if(board.GetRedPieces().begin(), board.GetRedPieces().end(), [](ChessPiece* p) {return p->CanCrossTheRiver(); }) +
			count_if(board.GetBlackPieces().begin(), board.GetBlackPieces().end(), [](ChessPiece* p) {return p->CanCrossTheRiver(); }) == 0) {
			return DRAW;   // 双方都不能过河,平局
		}
		return NONE;
	}

	bool GetNexPlayer() const {
		return nextPlayer;
	}

	void startup();
	void show();
	void updateWithInput();

};

void Game::startup() {	
	initgraph(BLOCK_SIZE * COLNum, BLOCK_SIZE * RowNum);
	setbkcolor(RGB(221, 200, 157));
	setlinestyle(PS_SOLID, 2);
	cleardevice();
	BeginBatchDraw();
}

void Game::show() {
	cleardevice();

	// 下面为绘制棋盘
	setlinecolor(BLACK);
	setfillcolor(RGB(221, 200, 157));
	for (int i = 0; i < RowNum - 2; i++) {
		for (int j = 0; j < COLNum - 2; j++) {
			if (i != 4) {
				fillrectangle((j + 1) * BLOCK_SIZE, (i + 1) * BLOCK_SIZE, (j + 2) * BLOCK_SIZE, (i + 2) * BLOCK_SIZE);
			}
			
		}
	}
	//画楚河汉界外的两笔		坐标(i = 5,j = 1      i = 5 , j = 9
	line(1 * BLOCK_SIZE, 5 * BLOCK_SIZE, BLOCK_SIZE, 6 * BLOCK_SIZE);
	line(9 * BLOCK_SIZE, 5 * BLOCK_SIZE, 9 * BLOCK_SIZE, 6 * BLOCK_SIZE);

	//画两个九宫格  i = 1 + 2, j = 4 + 2 ; i = 1 + 2, j = 6 -2 ;		
	//  下面的:   i  = 10 - 2, j = 4 + 2; i = 8 + 2, j = 4 + 2
	line(4 * BLOCK_SIZE, 1 * BLOCK_SIZE, 6 * BLOCK_SIZE, 3 * BLOCK_SIZE);
	line(6 * BLOCK_SIZE, 1 * BLOCK_SIZE, 4 * BLOCK_SIZE, 3 * BLOCK_SIZE);
	line(4 * BLOCK_SIZE, 10 * BLOCK_SIZE, 6 * BLOCK_SIZE, 8 * BLOCK_SIZE);
	line(4 * BLOCK_SIZE, 8 * BLOCK_SIZE, 6 * BLOCK_SIZE, 10 * BLOCK_SIZE);
	
	//写楚河汉界	  楚河 i = 5 , j = 2    汉界   i = 5, j = 6
	TCHAR s[20];
	setbkmode(TRANSPARENT);
	settextcolor(BLACK);
	settextstyle(55, 0, _T("华文行楷"));
	_stprintf_s(s, _T("楚河"));
	outtextxy(2 * BLOCK_SIZE, 5 * BLOCK_SIZE, s);

	settextcolor(RED);
	_stprintf_s(s, _T("汉界"));
	outtextxy(6 * BLOCK_SIZE, 5 * BLOCK_SIZE, s);

	//绘制棋子,首先,需要有一个画圆的函数,画出一个圆,然后中间是棋子的名字,棋子在
	setlinecolor(RGB(100, 100, 100));
	setfillcolor(RGB(221 - 20, 200 - 20, 157 -20));
	for (int y = 0; y < 10; y++) {	//这里对应的线
		for (int x = 0; x < 9; x++) {
			ChessPiece* p = this->board.GetChess(Point(x, y));
			if (p) {
				if (p->cl == BLACK_CHESS) {	// 绘制黑棋   画圆,设置字体
					fillcircle((p->GetPoint().x + 1) * BLOCK_SIZE, (p->GetPoint().y + 1)  * BLOCK_SIZE, BLOCK_SIZE / 2 - 2);

					TCHAR s[20];
					setbkmode(TRANSPARENT);
					settextcolor(BLACK);
					settextstyle(35, 0, _T("宋体"));
					
					_stprintf_s(s, _T("%hs"), p->GetName());
					outtextxy((x + 1) * BLOCK_SIZE - BLOCK_SIZE / 2 + 10, (y + 1) * BLOCK_SIZE - BLOCK_SIZE / 2 + 10, s);
				}
				else {
					fillcircle((p->GetPoint().x + 1) * BLOCK_SIZE, (p->GetPoint().y + 1) * BLOCK_SIZE, BLOCK_SIZE / 2 - 2);
					TCHAR s[20];
					setbkmode(TRANSPARENT);
					settextcolor(RED);
					settextstyle(35, 0, _T("宋体"));
					
					_stprintf_s(s, _T("%hs"),p->GetName());
					outtextxy((x + 1) * BLOCK_SIZE - BLOCK_SIZE / 2 + 10, (y + 1) * BLOCK_SIZE - BLOCK_SIZE / 2 + 10, s);
				}
				
			}
		}

	}

	//绘制第一个棋子的位置
	if (firstPoint->x != -1) {
		setlinecolor(RED);
		circle((firstPoint->y + 1) * BLOCK_SIZE, (firstPoint->x + 1) * BLOCK_SIZE, BLOCK_SIZE / 2 + 4);
		
	}
	TCHAR s1[20];
	setbkmode(TRANSPARENT);
	settextcolor(RED);
	settextstyle(35, 0, _T("宋体"));

	_stprintf_s(s1, _T("%d, %d"),firstPoint->x, firstPoint->y);
	outtextxy(0, 0, s1);

	// 判断胜利者
	auto winner = GetWinner();
	if (winner == BLACK_CHESS) {
		_stprintf_s(s1, _T("黑方胜利!"));
		outtextxy(4*BLOCK_SIZE, 5*BLOCK_SIZE, s1);
	}
	else if (winner == RED_CHESS) {
		_stprintf_s(s1, _T("红方胜利!"));
		outtextxy(4 * BLOCK_SIZE, 5 * BLOCK_SIZE, s1);
	}
	else if (winner == DRAW) {
		_stprintf_s(s1, _T("平局"));
		outtextxy(4 * BLOCK_SIZE, 5 * BLOCK_SIZE, s1);
	}
	FlushBatchDraw();
}

void Game::updateWithInput() {
	MOUSEMSG m;
	if (MouseHit()) {
		m = GetMouseMsg();
		if (m.uMsg == WM_LBUTTONDOWN) {
			int clicked_j = int(m.y - BLOCK_SIZE / 2) / BLOCK_SIZE;
			int clicked_i = int(m.x - BLOCK_SIZE / 2) / BLOCK_SIZE;
			
			//首先应该判断点击次数,两种方法,静态变量,和成员变量
			static int i = 1;
			//第一次点击判断是否有棋子 
			if (i == 1) {
				i++;
				firstPoint->y = clicked_i;
				firstPoint->x = clicked_j;
				show();

			}
			else {	//第二次点击
				i = 1;
				secondPoint->y = clicked_i;
				secondPoint->x = clicked_j;
				Move(Point(firstPoint->y, firstPoint->x), Point(secondPoint->y, secondPoint->x));
				firstPoint->y = -1;
				firstPoint->x = -1;
				show();
			}

		}
	}
}

主要图形化实现部分:游戏总体的框架为 startup()初始化,  然后无限循环show()和updateWithInput()直到得到游戏结果。

startup主要新建一个窗口,并且,设置一些基本参数。

show()主要绘制棋盘,楚河汉界的文字,棋子,如果出现胜利者需要宣布胜利

updateWithInput()主要读取鼠标状态,进行棋子的移动。

#include<iostream>
#include<conio.h>
#include"Game.hpp"
using namespace std;

// 在此声明,Height为y为RowNum, Width为x为ColNum

int main() {
	Game g;
	g.startup();
	while (1) {
		g.show();
		g.updateWithInput();
		if (g.GetWinner() != NONE) {
			break;
		}
	}
	_getch();
	closegraph();
	return 0;
}

主程序主要执行程序框架。

执行效果如图:

 

可以看出,选择棋子的界面有点简陋,本来想做一个类似于直角引号的选择提示,但是发现代码量比直接画圆复杂很多,遂放弃。。。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值