深入理解C语言:开发属于你的三子棋小游戏

三子棋

    • 1. 前言
    • 2. 准备工作
    • 3. 使用二维数组存储下棋的数据
    • 4. 初始化棋盘为全空格
    • 5. 打印棋盘
    • 6. 玩家下棋
    • 7. 电脑下棋
    • 8. 判断输赢
    • 9. 效果展示
    • 10. 完整代码

在这里插入图片描述

1. 前言

大家好,我是努力学习游泳的鱼,今天我们会用C语言实现三子棋。所谓三子棋,就是三行三列的棋盘,玩家可以和电脑下棋,率先连成三个的获胜。话不多说,我们开始吧。
在这里插入图片描述

2. 准备工作

我们可以在一个项目中创建三个文件,分别是:

  1. test.c,测试游戏的逻辑。
  2. game.c,游戏的实现。
  3. game.h,函数声明,符号的定义。

测试这个游戏时,我们玩一把肯定不过瘾,所以需要使用do while循环,每次可以选择继续玩或者退出游戏。先把大致的框架搭出来。

#include <stdio.h>

void menu()
{
	printf("****************************\n");
	printf("********  1. play     ******\n");
	printf("********  0. exit     ******\n");
	printf("****************************\n");
}

int main()
{
	int input = 0;
	do
	{
		menu();
		printf("请选择(1/0):>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("三子棋\n");
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误\n");
			break;
		}
	} while (input);

	return 0;
}

在这里插入图片描述

当然,我们的游戏不可能只是打印“三子棋”这三个字这么简单,具体的实现我们会封装成一个函数,暂且取名为game

3. 使用二维数组存储下棋的数据

当我们下三子棋的时候,需要把下棋的数据存起来。由于三子棋的棋盘是3×3的,我们就需要一个三行三列的数组来存储下棋的数据。char board[3][3] = { 0 };

4. 初始化棋盘为全空格

当我们还没开始下棋时,棋盘上应该啥都没有,但是真的是啥都没有吗?事实上,如果我们打印棋盘时,能打印出“没有棋子”的效果,数组里应该是全空格。所以,我们需要写一个函数,初始化棋盘为全空格。InitBoard(board, 3, 3);这个函数的声明,我们会放在game.h里。具体的实现,我们会放在game.c里。以下的函数同理。该函数的声明:void InitBoard(char board[3][3], int row, int col);(以下的函数均省略声明)。该函数的实现,只需遍历这个二维数组,全部赋值为空格。

void InitBoard(char board[3][3], int row, int col)
{
	int i = 0;
	for (; i < row; ++i)
	{
		int j = 0;
		for (; j < col; ++j)
		{
			board[i][j] = ' ';
		}
	}
}

有没有发现,这个程序中,到处都要用到数组“三行三列”这个特点。如果我们想要改变这一点,比如改成五行五列,就需要改很多地方,非常麻烦。怎么解决这个问题呢?我们可以在game.h里定义两个常量ROWCOL,这样每次只需要改变#define后的值就行了。

#define ROW 3
#define COL 3

这样以上出现的所有3都可以用ROWCOL替代了(此处省略)。注意:如果想使用game.h里的符号,我们需要在game.ctest.c里引用这个头文件。引用自己写的头文件要用双引号。#include "game.h"而对于其他头文件如stdio.h,我们不需要在game.ctest.c里包含两次,只需在game.h里包含就行了。

5. 打印棋盘

我们初始化棋盘后,会想要看一看棋盘长啥样,所以接下来写一个打印棋盘的函数DisplayBoard(board, ROW, COL);
如果你认为打印出数组的数据就行了,从而这样写:

void DisplayBoard(char board[ROW][COL], int row, int col)
{
	int i = 0;
	for (; i < row; ++i)
	{
		int j = 0;
		for (; j < col; ++j)
		{
			printf("%c", board[i][j]);
		}
		printf("\n");
	}
}

实际运行时,你会发现,打印了,但没完全打印。
在这里插入图片描述
事实上,此时打印的是一堆空格,非常难看。如果我们想打印得好看点,可以考虑加上一些横向和竖向的分割。比如我设想了这样一种打印的效果:

   |   |   
---|---|---
   |   |   
---|---|---
   |   |   

假设把

   |   |   
---|---|---

当成一组,总共需要打印3组,为什么呢?因为有3行。
每一组里面,又分为数据行和分割行。我们需要先打印数据行,再打印分割行。

   |   |    // 数据行
---|---|--- // 分割行

所以一个简单的想法是这么写:

void DisplayBoard(char board[ROW][COL], int row, int col)
{
	int i = 0;
	for (; i < row; ++i)
	{
		// 打印数据
		printf(" %c | %c | %c \n", board[i][0], board[i][1], board[i][2]);
		// 打印分割的行
		printf("---|---|---\n");
	}
}

效果如下:
在这里插入图片描述
我们发现多打印了一行分割行,所以打印分割行时要加一条判断,不是最后一行才打印分割行。

if (i < row - 1)
	printf("---|---|---\n");

这样子打印就好看多了。

在这里插入图片描述

但是这么写的话,就相当于,一行一定要打印三列,后面就没有改变的可能了。比如我把ROWCOL都改成5,效果如下:
在这里插入图片描述
所以这种写法还是不够好。最好的写法是,再用一层循环控制列的打印。比如对于数据行:

   |   |   

我们可以把

   |  

当成一组数据,打印三组,最后一组就不用加上|了(同上一种写法用一个if语句来控制)。
对于分割行

---|---|---

我们可以把

---|

当成一组数据,打印三组,最后一组就不用加上|了(还是用if语句来控制)。
实现如下:

void DisplayBoard(char board[ROW][COL], int row, int col)
{
	int i = 0;
	for (; i < row; ++i)
	{
		// 打印数据
		int j = 0;
		for (; j < col; ++j)
		{
			printf(" %c ", board[i][j]);
			if (j < col - 1)
				printf("|");
		}
		printf("\n");
		// 打印分割的行
		if (i < row - 1)
		{
			for (j = 0; j < col; ++j)
			{
				printf("---");
				if (j < col - 1)
					printf("|");
			}
			printf("\n");
		}
	}
}

效果也没问题:
在这里插入图片描述
若把ROWCOL改成10,打印出来的效果如下:
在这里插入图片描述
这样写出来的代码就比较通用,具有比较强的可维护性。

6. 玩家下棋

接下来写玩家下棋的函数。player_move(board, ROW, COL);
先让玩家输入坐标,若xy都在13之间,则输入的坐标合法,在数组对应的位置是board[x-1][y-1],若该位置仍然是空格,则这个位置没有被下过,就把数组的这个元素改成*。由于若玩家输入的坐标非法或者被占用时,需要重新输入,故需要一个循环。

void player_move(char board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;
	printf("玩家下棋\n");
	while (1)
	{
		printf("请输入坐标:>");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			// 下棋
			if (board[x - 1][y - 1] == ' ')
			{
				board[x - 1][y - 1] = '*';
				break;
			}
			else
			{
				printf("该坐标被占用,请重新输入\n");
			}
		}
		else
		{
			printf("坐标非法,请重新输入\n");
		}
	}
}

玩家下棋后再把棋盘打印一下DisplayBoard(board, ROW, COL);效果如下:
在这里插入图片描述

7. 电脑下棋

玩家下完棋后就轮到电脑下棋computer_move(board, ROW, COL);
我们让电脑随机下棋,只需生成两个随机的坐标xy,若该位置是空格,就下在这个位置,如果是空格,那就再次产生随机坐标,可见,这也需要一个循环来实现。

void computer_move(char board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;

	printf("电脑下棋\n");
	while (1)
	{
		x = rand() % row; // 0~2
		y = rand() % col; // 0~2
		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			break;
		}
	}
}

使用rand函数前需要使用srand函数来设置随机数生成器的起点。我们需要给srand函数传一个时间戳,这就要用到time函数。srand((unsigned int)time(NULL));使用randsrand都需要引用头文件stdlib.h,使用time函数需要引用头文件time.h
我们用一个循环,就能实现玩家和电脑轮流下棋的效果。

while (1)
{
	// 玩家下棋
	player_move(board, ROW, COL);
	DisplayBoard(board, ROW, COL);

	// 电脑下棋
	computer_move(board, ROW, COL); // 随机下棋
	DisplayBoard(board, ROW, COL);
}

8. 判断输赢

什么时候游戏就结束了呢?如果玩家赢了,或者电脑赢了,或者平局,游戏就结束了,否则游戏继续。
我们来设计一个is_win函数来判断棋局是上面四种状态的哪一种。我们这么设计is_win函数的返回值:

  1. 玩家赢 - '*'
  2. 电脑赢 - '#'
  3. 平局 - 'Q'
  4. 继续 - 'C'

当棋局状态不是C时说明游戏结束了,就跳出循环,接着根据不同情况打印不同的结果。

char ret = 0;
while (1)
{
	// 玩家下棋
	player_move(board, ROW, COL);
	DisplayBoard(board, ROW, COL);
	// 判断输赢
	ret = is_win(board, ROW, COL);
	if (ret != 'C')
	{
		break;
	}
	// 电脑下棋
	computer_move(board, ROW, COL); // 随机下棋
	DisplayBoard(board, ROW, COL);
	ret = is_win(board, ROW, COL);
	if (ret != 'C')
	{
		break;
	}
}
if (ret == '*')
{
	printf("玩家赢\n");
}
else if (ret == '#')
{
	printf("电脑赢\n");
}
else
{
	printf("平局\n");
}

如何实现is_win函数呢?只需判断每行,每列,每条对角线是否有三个同样的棋子就行了。
先判断行(由于玩家赢和电脑赢都是返回对应的棋子——*#,所以直接把对应的数组元素返回就行了。):

int i = 0;
for (; i < row; ++i)
{
	if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][1] != ' ')
	{
		return board[i][1];
	}
}

判断列同理:

for (i = 0; i < col; ++i)
{
	if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[1][i] != ' ')
	{
		return board[1][i];
	}
}

最后判断对角线:

if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')
{
	return board[1][1];
}
if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] != ' ')
{
	return board[1][1];
}

如果有人赢了,在前面的代码中就会返回。如果没人赢,再来判断是否平局。如果棋盘没有空格了,那就是平局。我们可以写一个is_full函数来判断棋盘是否满了。如果满了就返回1,没满就返回0

if (is_full(board, row, col) == 1)
{
	return 'Q';
}

由于is_full函数只是写给is_win函数的,只需要在game.c这个文件内使用,所以加上static。具体的实现,只需要遍历数组,若发现空格,说明棋盘没满,就返回0,否则返回1

static int is_full(char board[ROW][COL], int row, int col)
{
	int i = 0;
	for (; i < row; ++i)
	{
		int j = 0;
		for (; j < col; ++j)
		{
			if (board[i][j] == ' ')
			{
				return 0; // 没有满
			}
		}
	}

	return 1; // 满了
}

而如果没人赢,也不是平局,则游戏继续,return 'C';即可。

9. 效果展示

写到这,我们就把三子棋程序写完啦。接下来看看效果:
玩家赢:
在这里插入图片描述
电脑赢:
在这里插入图片描述
平局:
在这里插入图片描述

10. 完整代码

下面是完整的代码:
game.h

#pragma once

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define ROW 3
#define COL 3

// 初始化棋盘
void InitBoard(char board[ROW][COL], int row, int col);

// 打印棋盘
void DisplayBoard(char board[ROW][COL], int row, int col);

// 玩家下棋
void player_move(char board[ROW][COL], int row, int col);

// 电脑下棋
void computer_move(char board[ROW][COL], int row, int col);

// 判断输赢
char is_win(char board[ROW][COL], int row, int col);

game.c

#define _CRT_SECURE_NO_WARNINGS 1

#include "game.h"

void InitBoard(char board[ROW][COL], int row, int col)
{
	int i = 0;
	for (; i < row; ++i)
	{
		int j = 0;
		for (; j < col; ++j)
		{
			board[i][j] = ' ';
		}
	}
}

//void DisplayBoard(char board[ROW][COL], int row, int col)
//{
//	int i = 0;
//	for (; i < row; ++i)
//	{
//		int j = 0;
//		for (; j < col; ++j)
//		{
//			printf("%c", board[i][j]);
//		}
//		printf("\n");
//	}
//}

//void DisplayBoard(char board[ROW][COL], int row, int col)
//{
//	int i = 0;
//	for (; i < row; ++i)
//	{
//		// 打印数据
//		printf(" %c | %c | %c \n", board[i][0], board[i][1], board[i][2]);
//		// 打印分割的行
//		if (i < row - 1)
//			printf("---|---|---\n");
//	}
//}

void DisplayBoard(char board[ROW][COL], int row, int col)
{
	int i = 0;
	for (; i < row; ++i)
	{
		// 打印数据
		int j = 0;
		for (; j < col; ++j)
		{
			printf(" %c ", board[i][j]);
			if (j < col - 1)
				printf("|");
		}
		printf("\n");
		// 打印分割的行
		if (i < row - 1)
		{
			for (j = 0; j < col; ++j)
			{
				printf("---");
				if (j < col - 1)
					printf("|");
			}
			printf("\n");
		}
	}
}

void player_move(char board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;
	printf("玩家下棋\n");
	while (1)
	{
		printf("请输入坐标:>");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			// 下棋
			if (board[x - 1][y - 1] == ' ')
			{
				board[x - 1][y - 1] = '*';
				break;
			}
			else
			{
				printf("该坐标被占用,请重新输入\n");
			}
		}
		else
		{
			printf("坐标非法,请重新输入\n");
		}
	}
}

void computer_move(char board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;

	printf("电脑下棋\n");
	while (1)
	{
		x = rand() % row; // 0~2
		y = rand() % col; // 0~2
		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			break;
		}
	}
}

static int is_full(char board[ROW][COL], int row, int col)
{
	int i = 0;
	for (; i < row; ++i)
	{
		int j = 0;
		for (; j < col; ++j)
		{
			if (board[i][j] == ' ')
			{
				return 0; // 没有满
			}
		}
	}
	return 1; // 满了
}

char is_win(char board[ROW][COL], int row, int col)
{
	int i = 0;
	// 判断行
	for (; i < row; ++i)
	{
		if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][1] != ' ')
		{
			return board[i][1];
		}
	}
	// 判断列
	for (i = 0; i < col; ++i)
	{
		if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[1][i] != ' ')
		{
			return board[1][i];
		}
	}
	// 对角线
	if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')
	{
		return board[1][1];
	}
	if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] != ' ')
	{
		return board[1][1];
	}

	// 判断平局
	if (is_full(board, row, col) == 1)
	{
		return 'Q';
	}

	// 继续
	return 'C';
}

test.c

#define _CRT_SECURE_NO_WARNINGS 1

#include "game.h"

void menu()
{
	printf("****************************\n");
	printf("********  1. play     ******\n");
	printf("********  0. exit     ******\n");
	printf("****************************\n");
}

void game()
{
	// 三子棋小游戏的具体实现
	char ret = 0;
	// 存放下棋的数据
	char board[ROW][COL] = { 0 };
	// 初始化棋盘为全空格
	InitBoard(board, ROW, COL);
	// 打印棋盘
	DisplayBoard(board, ROW, COL);

	while (1)
	{
		// 玩家下棋
		player_move(board, ROW, COL);
		DisplayBoard(board, ROW, COL);
		// 判断输赢
		ret = is_win(board, ROW, COL);
		if (ret != 'C')
		{
			break;
		}
		// 电脑下棋
		computer_move(board, ROW, COL); // 随机下棋
		DisplayBoard(board, ROW, COL);
		ret = is_win(board, ROW, COL);
		if (ret != 'C')
		{
			break;
		}
	}
	if (ret == '*')
	{
		printf("玩家赢\n");
	}
	else if (ret == '#')
	{
		printf("电脑赢\n");
	}
	else
	{
		printf("平局\n");
	}
	//DisplayBoard(board, ROW, COL);
}

//
// 什么时候,游戏就结束了
// 玩家赢 - '*'
// 电脑赢 - '#'
// 平局   - 'Q'
// 继续   - 'C'
//
int main()
{
	int input = 0;
	srand((unsigned int)time(NULL));
	do
	{
		menu();
		printf("请选择(1/0):>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误\n");
			break;
		}
	} while (input);

	return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

分享经典、现代和前沿软件工程课程

随着信息技术的发展&#xff0c;软件已经深入到人类社会生产和生活的各个方面。软件工程是将工程化的方法运用到软件的开发、运行和维护之中&#xff0c;以达到提高软件质量&#xff0c;降低开发成本的目的。软件工程已经成为当今最活跃、最热门的学科之一。 本次软件工程MOOC课…

跟着这份指南,让你的下拉列表设计更加顺畅!

下拉列表广泛应用于UI设计中&#xff0c;可以简化界面&#xff0c;帮助用户缩小选择范围&#xff0c;减轻用户认知负担&#xff0c;防止数据输入错误。但与此同时&#xff0c;它也是一个受到用户批评的灾区。在某些情况下&#xff0c;下拉列表不仅意义不大&#xff0c;而且对用…

全新攻击面管理平台

首页大屏 内测阶段&#xff0c;免费试用一个月 有兴趣体验的师傅&#xff0c;来长亭云图极速版群里找我 py

面试经典150题【51-60】

文章目录 面试经典150题【51-60】71.简化路径155.最小栈150.逆波兰表达式求值224.基本计算器141.环形链表2.两数相加21.合并两个有序链表138.随机链表的复制19.删除链表的倒数第N个节点82.删除链表中的重复元素II 面试经典150题【51-60】 71.简化路径 先用split(“/”)分开。然…

Flutter混合栈管理方案对比

1.Google官方&#xff08;多引擎方案&#xff09; Google官方建议的方式是多引擎方案&#xff0c;即每次使用一个新的FlutterEngine来渲染Widget树&#xff0c;存在的主要问题是每个引擎都要有比较大的内存等资源消耗&#xff0c;虽然Flutter 2.0之后的FlutterEngineGroup通过在…

如何选择O2OA(翱途)开发平台的部署架构?

概述 O2OA(翱途)开发平台[下称O2OA开发平台或者O2OA]支持公有云&#xff0c;私有云和混合云部署&#xff0c;也支持复杂的网络结构下的分布式部署。本篇主要介绍O2OA(翱途)开发平台支持的部署环境以及常用的集群部署架构。 软硬件环境说明 支持的云化平台&#xff1a; 华为云…

【算法】二叉搜索树的插入、删除、转换操作

1 二叉搜索树的插入操作 给定二叉搜索树&#xff08;BST&#xff09;的根节点 root 和要插入树中的值 value &#xff0c;将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。 输入数据 保证 &#xff0c;新值和原始二叉搜索树中的任意节点值都不同。 注意&#xff0c;可能…

卷积神经网络(CNN)原理与实现

卷积神经网络(CNN) 卷积神经网络原理卷积神经网络的数学推导卷积层反向传播算法数学推导卷积层实现代码 卷积神经网络(CNN) 卷积神经网络原理 卷积神经网络是一种用于图像、语音、自然语言等数据的深度学习模型&#xff0c;其核心思想是使用卷积操作提取输入数据的特征&…

【开源项目】经典开源项目数字孪生智慧医院

飞渡科技数字孪生医院管理平台&#xff0c;融合数字孪生、物联网IOT、无线定位等技术&#xff0c;提供病房管理、医疗管理、照明管理、停车场管理等应用&#xff0c;同时结合完善的安防系统&#xff0c;立体化、全覆盖的视频监控体系&#xff0c;实现医院数字化卓越运营以及精细…

汇编语言程序设计实验二

实验目的和要求 继续学习使用DEBUG程序的各种命令。利用DEBUG学习了解计算机取指令、执行指令的工作过程。 掌握8086/8088基本指令的使用方法和功能。 实验环境 DOSBox 0.74 实验内容与过程 1&#xff0e; 按照下列给定步骤完成求累加和程序: 程序: MOV BX,1000MOV C…

MBR10200FCT-ASEMI适配开关电源MBR10200FCT

编辑&#xff1a;ll MBR10200FCT-ASEMI适配开关电源MBR10200FCT 型号&#xff1a;MBR10200FCT 品牌&#xff1a;ASEMI 封装&#xff1a;ITO-220AB 最大平均正向电流&#xff08;IF&#xff09;&#xff1a;10A 最大循环峰值反向电压&#xff08;VRRM&#xff09;&#xf…

BUUCTF---[极客大挑战 2019]Upload1

1.题目描述 2.点开链接&#xff0c;需要上传文件&#xff0c;要求是image&#xff0c;上传文件后缀为jpg的一句话木马&#xff0c;发现被检测到了 3.换另一个木马试试 GIF89a? <script language"php">eval($_REQUEST[1])</script> 发现可以上传成功 4…

(C语言)sizeof和strlen的对比(详解)

sizeof和strlen的对⽐&#xff08;详解&#xff09; 1. sizeof sizeof是用来计算变量所占内存空间大小的&#xff0c; 单位是字节&#xff0c;如果操作数是类型的话&#xff0c;计算的是用类型创建的变量所占空间的大小。 sizeof 只关注占用内存空间的大小 &#xff0c;不在乎内…

GitLab EE 企业版破解

在当今数字化时代&#xff0c;软件开发与团队协作已经成为现代企业不可或缺的一部分。而在这个过程中&#xff0c;版本控制、协作和持续集成等工具的运用变得至关重要。GitLab作为一个领先的、完整的DevOps平台&#xff0c;为团队提供了一个集成的解决方案&#xff0c;使得软件…

【Leetcode每日一题】DP35 二维前缀和(难度⭐⭐)(26)

1. 题目解析 题目链接&#xff1a;DP35 【模板】二维前缀和 这个问题的理解其实相当简单&#xff0c;只需看一下示例&#xff0c;基本就能明白其含义了。 核心在于计算题目所给二维区间数组元素和返回即可。 2. 算法原理 和上题了类似的方法&#xff0c;使用dp数组来保存[1…

科普【1】:web3.0初探,不懂技术也能看懂。

Hi&#xff0c;我是贝格前端工场&#xff0c;本期来科普一下web3这个概念&#xff0c;力争讲的浅显易懂。 一、什么是web3及其特征 Web3是指第三代互联网&#xff0c;也被称为分布式互联网或区块链互联网。它是对传统互联网的一种进化和扩展&#xff0c;旨在提供更加去中心化、…

为什么中小APP开发者要选择聚合SDK广告变现服务?

广告变现听起来容易&#xff0c;但要在不影响用户体验的情况下&#xff0c;把变现收益做到最大化&#xff0c;其实非常复杂。 对于处于行业腰部和尾部的中小APP来说&#xff0c;团队资源有限&#xff0c;要将所有的资源集中在投入到核心业务竞争力上——扩大用户规模和活跃度上…

如何测试代理IP是否可用?

目录 一、了解代理IP基础知识 二、为什么需要测试代理IP的可用性&#xff1f; 三、测试代理IP的可用性方法 使用Ping命令测试代理IP的连通性 使用curl或wget测试代理IP的可用性 编写代码测试代理IP的可用性 四、案例分析 五、总结与建议 在数字时代的今天&#xff0c;代…

.net 日志

一、Log4net 1、log4net写入文本 1、nuget引入log4net、Microsoft.Extensions.Logging.Log4Net.AspNetCore这2个 2、引入配置文件,可以直接去官网(log4net官网配置文件)复制下来,放到项目目录下面,设置成始终复制,因为这个文件最终要到我们项目运行目录下面去 3、要在pr…

3月4日工作记录

周末总结 周末花6.5k的4060ti主机到家了&#xff0c;配好了和女朋友一起玩了两天帕鲁&#xff0c;真好玩&#xff01; 玩完开始上班&#xff01; 今天&#xff0c;上午先看三篇paper&#xff0c;然后下午继续1日计划的工作 文章阅读 文章一&#xff1a;SciGLM: Training Sc…