2022秋人工智能实验报告

2022秋人工智能实验报告

实验名称一:百事好合

实验要求:将 1, 2, 3, 4, 5, 6, 7, 8, 9 这九个数字,不改变顺序,用加减号连起来(允许减
号出现在 1 的前面),使其结果等于 100。请编程输出所有可能的情况,数字和符号之间不
要有空格,如 1+23-4+56+7+8+9=100。总共有 12 种不同的组合。

一、主要思路:

1、模型:

​ 通过对题目进行分析,我们可以在1~9这九个数字之间画上一个空格,填入相应的符号,让这些数字之间的关系变成相加、相减或者连接成为新的高位数,通过判断所填的空的内容是否符合要求来解决问题。这一类的问题都可以通过“填空”进行解决,通过穷举法筛选需要的结果 。

2、算法:穷举法、双重循环、十进制转换三进制

​ 因为考虑到数字1的前面也涉及到了符号的问题,所以首先考虑的是在每个数字前面插上一个空,这样一来九个数字前面就有九个对应的空格需要填充,这样就会涉及到3的9次方种结果,但是在这里面也会包含1前面的连接符,但是1前面是不涉及到连接问题的,所以将1做一个单独的处理,先默认为+1,这样一来,我们所要考虑的就只有8个空格,也就是3的8次方种结果。然后把+1替换成为-1的情况,输出符合的结果。这样一来就大大减少了计算量。

首先设定一个外层循环,这个外层循环用来遍历3的8次方种结果,然后设定一个内层循环用来遍历8个空格,从9前面的空格往后推。这里涉及到怎样把外层循环的i和内层联系起来,保证每个空格的符号一一对应上。每个空格有三种填法:0、1、2代表+、-、连接,也就是三进制的数,将外层的i值转换成三进制的数就可以保证每种结果都被考虑到,然后在这三种情况下对数字进行操作,进行存储。最后判断结果是否符合100,如果是就输出表达式。

对于特殊情况1 ,先用+1进行遍历,然后再进行一次遍历,找到102的结果把+1替换成-1的结果输出。

二、程序实现及优化:

1、程序实现所用头文件解释:

#include :数据的输入输出
#define NUM 6561 :定义常量,表示填8个空的总共种类

#include :清零函数的使用

2、算法伪代码:

for i=0 to NUM do //总共有6561种可能
{
memset(Number, 0, sizeof(int)*9); //初始化函数清零
flag:= i;
for second=9 to 1 do //有8处符号需要判断1_2_3_4… 从后往前推
{
symbol: = flag%3; //该空的符号,规定0:+,1:-;2:连接,三种模式
flag:= flag/3; //往上一层(即上一个空)的位置
switch(symbol)//判断空里的符号是哪种
{
case 0://为+号时
if temp == 0 ,Then
Number[n]: = second;
else{
Number[n]: = temp;
temp: = 0;
m:= 2;
}
n++;
break;
case 1://符号为减
if temp == 0,Then
Number[n]: = 0 - second;
else
{
Number[n] := 0 - temp;
temp: = 0;
m: = 2;
}
n++;
break;
case 2://符号连接起来
if temp == 0,Then
temp := second + (second-1)*10;
else
{
temp := temp + (second-1)*PowerFunction(10,m);//调用函数 ,将数连接
m++;
}
break;
default:
break;
}
}

3、主要功能函数之间的调用关系

本实验仅使用一个函数:int PowerFunction(int m, int i);用来实现幂次

调用关系:主函数调用求幂次函数

三、实验结果及分析:

实验结果如下,共12种结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LEiUH2yL-1663916391018)(C:\Users\三分\Pictures\NET实验背景图\1.png)]

程序优化:一开始考虑的是将1前面的空也给考虑进去,这样就会导致在连接问题上出问题,后来就直接改成了把1拆出来,先当成+1处理,然后把+1换成-1找出结果符合的效率就大大提高了。

实验名称二:奥赛罗

实验要求:

写一个程序,读入若干奥赛罗游戏。输入的第一行是要处理的游戏个数,每个游戏有棋
盘局面及其后所跟的一系列命令组成。棋盘局面由 9 行组成,前 8 行给出了棋盘的当前状
态。这 8 行每行包括 8 个字符,所有这些字符是如下之一:
‘-’ 表示空格子
‘B’ 表示放有黑色棋子的格子
‘W’ 表示放有白色棋子的格子
第 9 行是‘B’或‘W’之一,以指出当前游戏者。你可以假定数据是合法格式的。
命令可以是为当前玩家列处所有可能的走步,走一步或退出当前游戏。每条命令占一行
且无空格。1. 为当前游戏者列出所有可能的走步:
命令是‘L’,在命令行的第一列。程序应检查棋盘并用格式(x, y)打印当前玩家的所有
合法走步,这里 x 代表合法走步的行号,y 代表列号。这些走步应当被按行优先顺序打印,
也就是说:
(1) 如果 j 大于 i,则所有行号是 i 的合法走步将在所有行号是 j 的合法走步之前打
印;
(2) 如果有多于一个的合法走步的行号是 i,则这些走步将按列号升序打印。
(3) 所有的合法走步应当输出在一行上。如果因为当前游戏者不可能夹住任何棋子而不
存在合法走步,程序应当打印“No legal move.“信息。

  1. 走一步:
    命令是‘M’,在命令行的第 1 列,其后是在第 2、3 列的两个数字。这两个数字是放置当
    前玩家颜色的棋子的格子的行、列号,除非当前游戏者没有合法走步。如果当前玩家没有合
    法走步,当前游戏者将首先换成另一个游戏者,此走步将是新游戏者的走步。你可以假定此
    时走步一定是合法的。你应该将变更记录至棋盘,包括加入新棋子和改变所有被夹住棋子的
    颜色(将棋子翻过来)。走完了此步之后,按 “Black-xx White-yy”的格式打印棋盘上每
    种颜色的棋子数目,这里 xx 是棋盘上黑色棋子的数目,yy 是棋盘上白色棋子的数目。走完
    一步后,当前游戏者将换成没有走步的游戏者。

  2. 退出当前游戏:
    命令是‘Q’,在命令行的第 1 列。在这里,用同输入相同的格式打印最后的棋盘局面。
    这终止了当前游戏的命令输入。
    你可以假定命令在语法上是正确的。用一个空行隔开不同游戏的输出,输出中的其他地
    方不得出现空行。

一、主要思路

由于大部分的代码已经在给出的文件中实现了,所以我们需要完成的是在borad.cpp文件中完成需要的函数,包括findCaptives、findMoves、makeMove以及print函数,主要算法基本由伪代码实现。

 //棋盘上每个位置周围的八个方位, 每个分量代表沿这个方位行进时行和列的偏移量
 const int Board::DIRECTIONS[8][2] = {
	{0, 1},
	{0, -1},
	{1, 0},
	{-1, 0},
	{1, 1},
	{1, -1},
	{-1, 1},
	{-1, -1},
};
void Board::setRow(int row, string rowData) {
	for int i = 0 to 8 do {
		_board[row][i] = rowData[i];
	}
}
void Board::setStone(int row, int col, char color) {
	_board[row][col] = color;
}

findCaptives函数伪代码如下,首先遍历该位置的八个方向,然后再寻找端点进行操作,伪代码如下:

findCaptives(int row, int col, char color, vector& captives) {
	//遍历该位置的八个方向
	auto solveRow = [row, col, color, &captives, this](int dir_x, int dir_y){
		vector temp;
		int x <- col + dir_x;
		int y <- row + dir_y;
		while (x >= 0 && x < 8 && y >= 0 && y < 8)
		{
			//无端点,无事发生
			if ('-' == _board[y][x]) {
				break;
			}
			//有端点,可俘获
			else if (color == _board[y][x]) {
				captives.insert(captives.end(), temp.begin(), temp.end());
				break;
			}
			else {
				temp.push_back(Point(x + 1, y + 1));
			}
			x += dir_x;
			y += dir_y;
		}
	};
	for (auto dir : DIRECTIONS) {
		solveRow(dir[0], dir[1]);
	}
	return captives.size();
}

获取行棋方的可能走步(L)return 可能走步的个数,本函数使用算法首先遍历棋盘,对于每一个同色棋子遍历8个方向的格子,如果是空格且非相邻格,中间必定含有异色棋子,放到可行的步,相邻的空格无法夹棋,所以不可行(规则似乎棋子必须紧贴)

findMoves(char color, vector& moves) {
//遍历棋盘,对于每一个同色棋子遍历8个方向的格子
std::settemp;
for  i = 0 to 8 do{
	for  j = 0 to 8 do {
		if (color == _board[i][j])  then {
			auto check = [&moves, &temp, color, i, j, this](int dir_x, int dir_y) {
				int x <-j + dir_x;
				int y <- i + dir_y;
				while (x >= 0 && x < 8 && y >= 0 && y < 8)
				{
					//空格且非相邻格,中间必定有包裹异色棋子,加入可行步
					//相邻的空格无法夹棋,不可行(规则似乎棋子必须紧贴)
					if ('-' == _board[y][x]) then {
						if (x != j + dir_x || y != i + dir_y) {
							temp.insert(Point(x + 1, y + 1));
						}
					跳出循环
					}
					//在该方向上已有第二颗同色棋无法包含
					else if (color == _board[y][x])  then {
						break;
					}
					x += dir_x;
					y += dir_y;
				}
			};
			for (auto dir : DIRECTIONS) {
				check(dir[0], dir[1]);
			}
		}
	}
}
moves.insert(moves.end(), temp.begin(), temp.end());
std::sort(moves.begin(), moves.end());
return moves.size();
}

行棋方在位置(row, col)走一步棋,并翻转每个俘虏为己方颜色(M)

@param row:行

@param col: 列

@param color:行棋方的棋子颜色

算法伪代码:

makeMove(int row, int col, char color) 
{
	vector temp;
	findCaptives(row, col, color, temp);
	setStone(row, col, color);
	for (auto p : temp) {
		setStone(p.getY() - 1, p.getX() - 1, color);
	}
	//重新统计一下
	 white = 0;
	 black = 0;
	for i = 0 to 8 {
		for j = 0 to 8 {
			if ('B' = _board[i][j])  then ++black;
			else if ('W' = _board[i][j])  then ++white;
		}
	}
	输出 "Black - " <

打印函数,将结果输出,使用双重for循环输出

print() {
	for i = 0 to 8 do
    {
		for  j = 0 to 8  do
		{
			cout  board[i][j];
		}
		cout  endl;
	}
}

二、代码实现及优化

功能函数的调用关系:

makemoves函数调用findCaptives、setStone函数

三、实验结果及分析

运行结果与实验样例一致,认为结果正确

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VKO8Z4qA-1663916391019)(C:\Users\三分\Pictures\NET实验背景图\奥赛罗.jpg)]

do
{
for j = 0 to 8 do
{
cout board[i][j];
}
cout endl;
}
}




## 二、代码实现及优化

### 功能函数的调用关系:

makemoves函数调用findCaptives、setStone函数

## 三、实验结果及分析

运行结果与实验样例一致,认为结果正确

[外链图片转存中...(img-VKO8Z4qA-1663916391019)]















你可能感兴趣的:(2022秋人工智能实验报告)