mirror of https://github.com/dnomd343/HRD_Game
Dnomd343
5 years ago
8 changed files with 1019 additions and 3 deletions
@ -0,0 +1,644 @@ |
|||
#include <iostream> |
|||
#include <vector> |
|||
#include <string> |
|||
#include <list> |
|||
#include <fstream> |
|||
using namespace std; |
|||
|
|||
ifstream File_Input; |
|||
ofstream File_Output; |
|||
const unsigned char Up = 1, Down = 2, Left = 3, Right = 4; |
|||
|
|||
//用于寻找下一布局
|
|||
struct block_struct { |
|||
unsigned char address; //0~19
|
|||
unsigned char style; //0:2*2 1:2*1 2:1*2 3:1*1
|
|||
}; |
|||
unsigned char table[20]; //0~9:block[?] 0xA:space 0xFF:empty
|
|||
unsigned char space[2]; //space[0~1]的位置
|
|||
struct block_struct block[10]; //0:2*2 1~5:1*2,2*1 6~9:1*1
|
|||
|
|||
bool quick; //仅计算最短步骤
|
|||
int Solution_num_quick; |
|||
vector <unsigned int> Source_quick; //父布局编号
|
|||
vector <unsigned int> Solution_quick; //最短路径
|
|||
|
|||
//用于绘制整个队列树
|
|||
unsigned int Now_Move; //目前正在进行计算的布局编号
|
|||
list <unsigned int> int_list; //空列表
|
|||
vector <unsigned int> int_vector; //空vector
|
|||
list <unsigned int> Hash[0x10000]; //哈希索引表
|
|||
vector <unsigned int> List; //所有情况的队列
|
|||
vector <unsigned int> Layer_Num; //所在层的编号
|
|||
vector <unsigned int> Layer_Index; //层中所属的编号
|
|||
vector <list <unsigned int> > Source; //父布局编号
|
|||
vector <vector <unsigned int> > Layer; //分层
|
|||
vector <vector <vector <unsigned int> > > Layer_Next; //分层链接
|
|||
|
|||
//布局的基本参数
|
|||
int group_size; //整个队列组的大小
|
|||
int min_steps; //解的最少步骤
|
|||
int farthest_steps; //最远布局的步数
|
|||
vector <unsigned int> Solutions; //所有解
|
|||
vector <unsigned int> Solutions_steps; //所有解的最少步
|
|||
vector <unsigned int> min_Solutions; //所有最少步解
|
|||
vector <unsigned int> farthest_cases; //所有最远的布局
|
|||
//vector <unsigned int> solution_path; //最少步解法
|
|||
|
|||
void debug(); |
|||
void Find_All_Case(); |
|||
void Data_Output(string File_name); |
|||
void Split_Layer(); |
|||
unsigned int Change_int (char str[8]); |
|||
string Change_str (unsigned int dat); |
|||
void Output_Graph (unsigned int Code); |
|||
void Analyse_Code (unsigned int Code); |
|||
void Add_Case (unsigned int Code); |
|||
void Calculate (unsigned int Start_Code); |
|||
void Date_Back_quick (int num); |
|||
void Calculate_quick (unsigned int Start_Code); |
|||
bool Check (unsigned int Code); |
|||
unsigned int Get_Code(); |
|||
void Find_Next(); |
|||
bool Check_Empty (unsigned char address,unsigned char dir,unsigned char num); |
|||
void Move_Block (unsigned char num,unsigned char dir_1,unsigned char dir_2); |
|||
void Fill_Block (unsigned char addr, unsigned char style, unsigned char filler); |
|||
vector <unsigned int> Search_Path (unsigned int target_num); |
|||
void Analyse_Case (unsigned int Start_Code); |
|||
|
|||
int main(int argc, char* argv[]) { |
|||
unsigned int Code, i; |
|||
string File_name, Parameter; |
|||
cout << "HRD-Engine by Dnomd343" << endl; |
|||
if (argc == 3) { |
|||
Parameter = argv[1]; |
|||
Code = Change_int(argv[2]); |
|||
if (Check(Code) == false) {cout << "Code Error" << endl; return 0;} |
|||
File_name = Change_str(Code) + ".txt"; |
|||
if (Parameter == "-q") { |
|||
cout << "Quick Calculate Mode." << endl; |
|||
cout << "Code: " << Change_str(Code) << endl; |
|||
cout << "Data Save at " << File_name << endl; |
|||
quick = true; |
|||
Calculate_quick(Code); |
|||
Date_Back_quick(Solution_num_quick); |
|||
File_Output.open(File_name.c_str()); |
|||
if (Solution_quick.size() == 0) { |
|||
File_Output << "No Solution" << endl; |
|||
} else { |
|||
File_Output << Solution_quick.size() - 1 << endl; |
|||
} |
|||
for (i = 0; i < Solution_quick.size(); i++) { |
|||
File_Output << Change_str(Solution_quick[i]) << endl; |
|||
} |
|||
File_Output.close(); |
|||
} else if (Parameter == "-a") { |
|||
cout << "All Calculate Mode." << endl; |
|||
cout << "Code: " << Change_str(Code) << endl; |
|||
cout << "Data Save at " << File_name << endl; |
|||
quick = false; |
|||
Analyse_Case(Code); |
|||
Data_Output(File_name); |
|||
} else { |
|||
cout << "Parameter Error" << endl; |
|||
} |
|||
} else { |
|||
cout << "Usage: [HRD-Engine.exe] [-q]/[-a] Code" << endl; |
|||
cout << "-q: Quick Calculate Mode" << endl << "-a: All Calculate Mode" << endl; |
|||
cout << "Such as 'HRD-Engine.exe -q 4FEA134'" << endl << " or 'HRD-Engine.exe -a 1A9BF0C'" << endl; |
|||
} |
|||
return 0; |
|||
} |
|||
|
|||
void Analyse_Case (unsigned int Start_Code) { //对一个布局进行分析
|
|||
unsigned int i, first_solution; |
|||
if (Check(Start_Code) == false) {return;} //若输入编码无效则退出
|
|||
Calculate(Start_Code); //通过计算建立队列表
|
|||
group_size = List.size(); //整个队列树大小
|
|||
min_steps = -1; |
|||
farthest_steps = -1; |
|||
Solutions.clear(); |
|||
Solutions_steps.clear(); |
|||
min_Solutions.clear(); |
|||
farthest_cases.clear(); |
|||
//solution_path.clear();
|
|||
bool get_it = false; |
|||
for (i = 0; i < List.size(); i++) { //遍历队列中所有元素
|
|||
Analyse_Code(List[i]); |
|||
if (block[0].address == 13) { //若当前布局为有效解
|
|||
if (get_it == false){ |
|||
min_steps = Layer_Num[i]; //第一个找到的解为最少步解
|
|||
first_solution = i; |
|||
get_it = true; |
|||
} |
|||
Solutions.push_back(List[i]); //将找到的有效解加入解集中
|
|||
Solutions_steps.push_back(Layer_Num[i]); |
|||
if (Layer_Num[i] == min_steps) { |
|||
min_Solutions.push_back(List[i]); //将找到的最小步有效解加入最小步解集中
|
|||
} |
|||
} |
|||
} |
|||
farthest_steps = Layer_Num[Layer_Num.size() - 1]; //计算最远布局的步数
|
|||
for (i = Layer_Num.size() - 1; i > 0; i--) { //找到所有最远的布局
|
|||
if(Layer_Num[i] != farthest_steps) {break;} |
|||
farthest_cases.push_back(List[i]); |
|||
} |
|||
//if (min_steps != -1) {solution_path = Search_Path(first_solution);}
|
|||
} |
|||
|
|||
void Split_Layer() { |
|||
vector <vector <unsigned int> > temp; |
|||
int i, num, index; |
|||
num = -1; |
|||
index = 0; |
|||
for (i = 0; i < Layer_Num.size(); i++) { |
|||
if (Layer_Num[i] != num) { |
|||
Layer.push_back(int_vector); |
|||
Layer_Next.push_back(temp); |
|||
num = Layer_Num[i]; |
|||
index = 0; |
|||
} |
|||
Layer_Index.push_back(index); |
|||
index++; |
|||
Layer[num].push_back(List[i]); |
|||
Layer_Next[num].push_back(int_vector); |
|||
} |
|||
|
|||
list <unsigned int>::iterator poi; |
|||
for (i = 1; i < List.size(); i++) { |
|||
poi = Source[i].begin(); |
|||
while (poi != Source[i].end()) { |
|||
Layer_Next[Layer_Num[*poi]][Layer_Index[*poi]].push_back(Layer_Index[i]); |
|||
++poi; |
|||
} |
|||
} |
|||
} |
|||
|
|||
vector <unsigned int> Search_Path (unsigned int target_num) { //搜索到达目标布局的一条最短路径 返回vector类
|
|||
vector <unsigned int> path; |
|||
int temp = -1; |
|||
path.push_back(target_num); //路径中加入目标布局
|
|||
while (temp != 0) { |
|||
temp = path[path.size() - 1]; |
|||
path.push_back(*Source[temp].begin()); |
|||
} |
|||
path.pop_back(); //去掉重复的根布局
|
|||
temp = path.size() / 2; //反置整个路径
|
|||
for (int i = 0; i < temp; i++) { |
|||
swap(path[i], path[path.size() - i - 1]); |
|||
} |
|||
for (int i = 0; i < path.size(); i++){ //将序号改成布局的编码
|
|||
path[i] = List[path[i]]; |
|||
} |
|||
return path; |
|||
} |
|||
|
|||
void Date_Back_quick (int num) { |
|||
if (num == -1) {return;} |
|||
Solution_quick.push_back(List[num]); |
|||
if (num == 0) {return;} |
|||
while (Source_quick[num] != 0) { |
|||
num = Source_quick[num]; |
|||
Solution_quick.push_back(List[num]); |
|||
} |
|||
Solution_quick.push_back(List[0]); |
|||
unsigned int i; |
|||
for (i = 0; i < Solution_quick.size() / 2 ; i++) { |
|||
swap(Solution_quick[i],Solution_quick[Solution_quick.size() - i - 1]); |
|||
} |
|||
} |
|||
|
|||
void Calculate_quick (unsigned int Start_Code) { //启动计算引擎
|
|||
unsigned int i; |
|||
for (i = 0; i <= 0xFFFF; i++) {Hash[i].clear();} //初始化
|
|||
List.clear(); |
|||
Source_quick.clear(); |
|||
Solution_num_quick = -1; |
|||
Hash[(Start_Code>>4) & 0xFFFF].push_back(0); //加入初始布局
|
|||
List.push_back(Start_Code); |
|||
Source_quick.push_back(0); |
|||
Now_Move = 0; //搜索目标指向根节点
|
|||
while (Now_Move != List.size()) { //进行广度优先搜索
|
|||
Analyse_Code(List[Now_Move]); //解析目标布局
|
|||
if (block[0].address == 13) { |
|||
Solution_num_quick = Now_Move; |
|||
return; |
|||
} |
|||
Find_Next(); //根据解析结果搜索所有子布局
|
|||
Now_Move++; |
|||
} |
|||
} |
|||
|
|||
void Calculate (unsigned int Start_Code) { //启动计算引擎
|
|||
unsigned int i; |
|||
for (i = 0; i <= 0xFFFF; i++) {Hash[i].clear();} //初始化
|
|||
List.clear(); |
|||
Layer_Num.clear(); |
|||
Source.clear(); |
|||
Hash[(Start_Code>>4) & 0xFFFF].push_back(0); //加入初始布局
|
|||
List.push_back(Start_Code); |
|||
Layer_Num.push_back(0); |
|||
Source.push_back(int_list); |
|||
Now_Move = 0; //搜索目标指向根节点
|
|||
while (Now_Move != List.size()) { //进行广度优先搜索
|
|||
Analyse_Code(List[Now_Move]); //解析目标布局
|
|||
Find_Next(); //根据解析结果搜索所有子布局
|
|||
Now_Move++; |
|||
} |
|||
Split_Layer(); |
|||
} |
|||
|
|||
void Add_Case (unsigned int Code) { //将计算结果加入队列
|
|||
list <unsigned int>::iterator poi; //定义迭代器
|
|||
poi = Hash[(Code>>4) & 0xFFFF].begin(); //设置poi为索引表的起始点
|
|||
while (poi != Hash[(Code>>4) & 0xFFFF].end()) { //遍历索引表
|
|||
if (Code == List[*poi]) { //若发现重复
|
|||
if (quick == true) {return;} |
|||
if ((Layer_Num[*poi] - Layer_Num[Now_Move]) == 1) { //若高一层
|
|||
Source[*poi].push_back(Now_Move); //加入父节点列表
|
|||
} |
|||
return; //重复 退出
|
|||
} |
|||
++poi; |
|||
} |
|||
Hash[(Code>>4) & 0xFFFF].push_back(List.size()); //将计算结果添加至索引表
|
|||
List.push_back(Code); //将计算结果加入队列
|
|||
if (quick == false) { |
|||
Layer_Num.push_back(Layer_Num[Now_Move] + 1); //添加对应的层数
|
|||
Source.push_back(int_list); //初始化其父节点列表
|
|||
Source[Source.size()-1].push_back(Now_Move); //将正在进行搜索的布局添加为父节点
|
|||
} else { |
|||
Source_quick.push_back(Now_Move); //将正在进行搜索的布局添加为父布局
|
|||
} |
|||
} |
|||
|
|||
void Fill_Block (unsigned char addr, unsigned char style, unsigned char filler) { //用指定内容填充table中指定的位置
|
|||
if (style == 0) {table[addr] = table[addr + 1] = table[addr + 4] = table[addr + 5] = filler;} //2*2
|
|||
else if (style == 1) {table[addr] = table[addr + 1] = filler;} //2*1
|
|||
else if (style == 2) {table[addr] = table[addr + 4] = filler;} //1*2
|
|||
else if (style == 3) {table[addr] = filler;} //1*1
|
|||
} |
|||
|
|||
void Move_Block (unsigned char num, unsigned char dir_1, unsigned char dir_2) { //按要求移动块并将移动后的编码传给Add_Case
|
|||
unsigned char i, addr, addr_bak; |
|||
addr = block[num].address; |
|||
addr_bak = addr; |
|||
|
|||
if (dir_1 == Up) {addr -= 4;} //第一次移动
|
|||
else if (dir_1==Down) {addr += 4;} |
|||
else if (dir_1==Left) {addr--;} |
|||
else if (dir_1==Right) {addr++;} |
|||
if (dir_2 == Up) {addr -= 4;} //第二次移动
|
|||
else if (dir_2 == Down) {addr += 4;} |
|||
else if (dir_2 == Left) {addr--;} |
|||
else if (dir_2 == Right) {addr++;} |
|||
|
|||
Fill_Block(addr_bak, block[num].style, 0xA); //修改 table为移动后的状态
|
|||
Fill_Block(addr, block[num].style, num); |
|||
block[num].address = addr; |
|||
|
|||
Add_Case(Get_Code()); //生成编码并赋予Add_Case
|
|||
|
|||
block[num].address = addr_bak; |
|||
Fill_Block(addr, block[num].style, 0xA); //还原 table原来的状态
|
|||
Fill_Block(addr_bak, block[num].style, num); |
|||
} |
|||
|
|||
void Find_Next() { //寻找所有移动的方式并提交给Move_Block
|
|||
bool Can_Move[10]; |
|||
unsigned char i, addr; |
|||
for (i = 0; i < 10; i++) {Can_Move[i] = false;} |
|||
for (i = 0; i <= 1; i++) { //寻找位于空格周围的所有块
|
|||
addr = space[i]; |
|||
if (addr > 3) |
|||
if (table[addr - 4] != 0xA) {Can_Move[table[addr - 4]] = true;} |
|||
if (addr < 16) |
|||
if (table[addr + 4] != 0xA) {Can_Move[table[addr + 4]] = true;} |
|||
if (addr % 4 != 0) |
|||
if (table[addr - 1] != 0xA) {Can_Move[table[addr - 1]] = true;} |
|||
if (addr % 4 != 3) { |
|||
if (table[addr + 1] != 0xA) {Can_Move[table[addr + 1]] = true;}} |
|||
} |
|||
|
|||
for (i = 0; i <= 9; i++) { |
|||
if (Can_Move[i] == true) { //若该块可能可以移动
|
|||
addr = block[i].address; |
|||
if (block[i].style == 0) { //2*2
|
|||
if ((Check_Empty(addr, Up, 1) == true) && |
|||
(Check_Empty(addr + 1,Up, 1) == true)) {Move_Block(i, Up, 0);} |
|||
|
|||
if ((Check_Empty(addr, Down, 2) == true) && |
|||
(Check_Empty(addr + 1,Down, 2) == true)) {Move_Block(i, Down, 0);} |
|||
|
|||
if ((Check_Empty(addr, Left, 1) == true) && |
|||
(Check_Empty(addr + 4, Left, 1) == true)) {Move_Block(i, Left, 0);} |
|||
|
|||
if ((Check_Empty(addr, Right, 2) == true) && |
|||
(Check_Empty(addr + 4, Right, 2) == true)) {Move_Block(i, Right, 0);} |
|||
} |
|||
else if (block[i].style == 1) { //2*1
|
|||
if ((Check_Empty(addr, Up, 1) == true) && |
|||
(Check_Empty(addr + 1, Up, 1) == true)) {Move_Block(i, Up, 0);} |
|||
|
|||
if ((Check_Empty(addr, Down, 1) == true) && |
|||
(Check_Empty(addr + 1, Down, 1) == true)) {Move_Block(i, Down, 0);} |
|||
|
|||
if (Check_Empty(addr, Left, 1) == true) { |
|||
Move_Block(i, Left, 0); |
|||
if (Check_Empty(addr, Left, 2) == true) {Move_Block(i, Left, Left);} |
|||
} |
|||
|
|||
if (Check_Empty(addr, Right, 2) == true) { |
|||
Move_Block(i, Right, 0); |
|||
if (Check_Empty(addr, Right, 3) == true) {Move_Block(i, Right, Right);} |
|||
} |
|||
} |
|||
else if (block[i].style == 2) { //1*2
|
|||
if (Check_Empty(addr, Up, 1) == true) { |
|||
Move_Block(i, Up, 0); |
|||
if (Check_Empty(addr, Up, 2) == true) {Move_Block(i, Up, Up);} |
|||
} |
|||
|
|||
if (Check_Empty(addr, Down, 2) == true) { |
|||
Move_Block(i, Down, 0); |
|||
if (Check_Empty(addr, Down, 3) == true) {Move_Block(i, Down, Down);} |
|||
} |
|||
|
|||
if ((Check_Empty(addr, Left, 1) == true) && |
|||
(Check_Empty(addr + 4, Left, 1) == true)) {Move_Block(i, Left, 0);} |
|||
|
|||
if ((Check_Empty(addr, Right, 1) == true) && |
|||
(Check_Empty(addr + 4, Right, 1) == true)) {Move_Block(i, Right, 0);} |
|||
} |
|||
else if (block[i].style == 3) { //1*1
|
|||
if (Check_Empty(addr, Up, 1) == true) { |
|||
Move_Block(i, Up, 0); |
|||
if (Check_Empty(addr - 4, Up, 1) == true) {Move_Block(i, Up, Up);} |
|||
if (Check_Empty(addr - 4, Left, 1) == true) {Move_Block(i, Up, Left);} |
|||
if (Check_Empty(addr - 4, Right, 1) == true) {Move_Block(i, Up, Right);} |
|||
} |
|||
|
|||
if (Check_Empty(addr, Down, 1) == true) { |
|||
Move_Block(i, Down, 0); |
|||
if (Check_Empty(addr + 4, Down, 1) == true) {Move_Block(i, Down, Down);} |
|||
if (Check_Empty(addr + 4, Left, 1) == true) {Move_Block(i, Down, Left);} |
|||
if (Check_Empty(addr + 4, Right, 1) == true) {Move_Block(i, Down, Right);} |
|||
} |
|||
|
|||
if (Check_Empty(addr, Left, 1) == true) { |
|||
Move_Block(i, Left, 0); |
|||
if (Check_Empty(addr - 1, Up, 1) == true) {Move_Block(i, Left, Up);} |
|||
if (Check_Empty(addr - 1, Down, 1) == true) {Move_Block(i, Left, Down);} |
|||
if (Check_Empty(addr - 1, Left, 1) == true) {Move_Block(i, Left, Left);} |
|||
} |
|||
|
|||
if (Check_Empty(addr, Right, 1) == true) { |
|||
Move_Block(i, Right, 0); |
|||
if (Check_Empty(addr + 1, Up, 1) == true) {Move_Block(i, Right, Up);} |
|||
if (Check_Empty(addr + 1, Down, 1) == true) {Move_Block(i, Right, Down);} |
|||
if (Check_Empty(addr + 1, Right, 1) == true) {Move_Block(i, Right, Right);} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
bool Check_Empty (unsigned char address, unsigned char dir, unsigned char num) { //判断指定位置是否为空格 若不是空格或者无效返回false
|
|||
unsigned char x, y, addr; |
|||
if (address > 19) {return false;} //输入位置不存在
|
|||
|
|||
x = address % 4; |
|||
y = (address - x) / 4; |
|||
if (dir == Up) { //上方
|
|||
if (y < num) {return false;} |
|||
addr = address - num * 4; |
|||
} |
|||
if (dir == Down) { //下方
|
|||
if (y + num > 4) {return false;} |
|||
addr = address + num * 4; |
|||
} |
|||
if (dir == Left) { //左侧
|
|||
if (x < num) {return false;} |
|||
addr = address - num; |
|||
} |
|||
if (dir == Right) { //右侧
|
|||
if(x + num > 3){return false;} |
|||
addr = address + num; |
|||
} |
|||
|
|||
if (table[addr] == 0xA) { |
|||
return true; |
|||
} else { |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
unsigned int Get_Code() { //生成编码
|
|||
bool temp[20]; |
|||
unsigned int Code = 0; |
|||
unsigned char i, addr, style; |
|||
|
|||
for (i = 0; i < 20; i++) {temp[i] = false;} //初始化
|
|||
temp[block[0].address] = temp[block[0].address + 1] = |
|||
temp[block[0].address + 4] = temp[block[0].address + 5] = true; |
|||
|
|||
Code |= block[0].address<<24; //2*2块的位置
|
|||
addr = 0; |
|||
for (i = 1; i <= 11; i++) { |
|||
while(temp[addr] == true){ //找到下一个未填充的空格
|
|||
if (addr < 19) { |
|||
addr++; |
|||
} else { |
|||
return 0; |
|||
} |
|||
} |
|||
if (table[addr] == 0xA) { //空格
|
|||
temp[addr] = true; |
|||
} else { |
|||
style = block[table[addr]].style; |
|||
if (style == 1) { //2*1
|
|||
temp[addr] = temp[addr + 1] = true; |
|||
Code |= 1<<(24 - i * 2); |
|||
} |
|||
else if (style == 2) { //1*2
|
|||
temp[addr] = temp[addr + 4] = true; |
|||
Code |= 2<<(24 - i * 2); |
|||
} |
|||
else if (style == 3) { //1*1
|
|||
temp[addr] = true; |
|||
Code |= 3<<(24 - i * 2); |
|||
} |
|||
} |
|||
} |
|||
return Code; |
|||
} |
|||
|
|||
void Analyse_Code (unsigned int Code) { //解译编码到 table[20] block[10] space[2]中
|
|||
unsigned char i, addr, style; |
|||
unsigned char num_space = 0, num_type_1 = 0, num_type_2 = 5; |
|||
space[0] = space[1] = 0xFF; //初始化
|
|||
for (i = 0; i < 20; i++) {table[i] = 0xFF;} |
|||
for (i = 0; i <= 9; i++) { |
|||
block[i].address = 0xFF; |
|||
block[i].style = 0xFF; |
|||
} |
|||
|
|||
block[0].address = 0xF & (Code>>24); //开始解译
|
|||
if (block[0].address > 14) {goto err;} //2*2块越界 退出
|
|||
block[0].style = 0; //设置2*2块的参数
|
|||
Fill_Block(block[0].address, 0, 0x0); |
|||
addr = 0; |
|||
for (i = 0; i < 11; i++) { //遍历 10个块
|
|||
while (table[addr] != 0xFF){ //向下搜索空块
|
|||
if (addr < 19) { |
|||
addr++; |
|||
} else { |
|||
break; |
|||
} |
|||
} |
|||
style = 0x3 & (Code>>(22 - i * 2)); //0:space 1:2*1 2:1*2 3:1*1
|
|||
if (style == 0) { //space
|
|||
table[addr] = 0xA; |
|||
space[num_space] = addr; |
|||
if (num_space == 0) {num_space++;} |
|||
} |
|||
if (style == 1) { //2*1
|
|||
if (num_type_1 < 5) {num_type_1++;} |
|||
if (addr > 18) {goto err;} //2*1块越界
|
|||
block[num_type_1].style = 1; |
|||
block[num_type_1].address = addr; |
|||
table[addr] = table[addr + 1] = num_type_1; |
|||
} |
|||
if (style == 2) { //1*2
|
|||
if (num_type_1 < 5) {num_type_1++;} |
|||
if (addr > 15) {goto err;} //1*2块越界
|
|||
block[num_type_1].style = 2; |
|||
block[num_type_1].address = addr; |
|||
table[addr] = table[addr + 4] = num_type_1; |
|||
} |
|||
if (style == 3) { //1*1
|
|||
if (num_type_2 < 9) {num_type_2++;} |
|||
block[num_type_2].style = 3; |
|||
block[num_type_2].address = addr; |
|||
table[addr] = num_type_2; |
|||
} |
|||
} |
|||
err:; |
|||
} |
|||
|
|||
bool Check (unsigned int Code) { //检查编码是否合法 正确返回true 错误返回false
|
|||
bool temp[20]; |
|||
unsigned char addr, i; |
|||
Analyse_Code(Code); |
|||
|
|||
for (i = 0; i < 20; i++){temp[i] = false;} //初始化
|
|||
for (i = 0; i < 20; i++) { //检查table内容是否合法
|
|||
if (table[i] > 10) {return false;} |
|||
} |
|||
|
|||
if (block[0].style != 0) {return false;} //检查2*2块
|
|||
for (i = 1; i <= 5; i++) { //检查2*1与1*2块
|
|||
if ((block[i].style != 1) && (block[i].style != 2)) {return false;} |
|||
} |
|||
for (i = 6; i <= 9; i++) { //检查1*1块
|
|||
if (block[i].style != 3) {return false;} |
|||
} |
|||
for (i = 0; i <= 1; i++) { //检查空格
|
|||
if (space[i] > 19) { |
|||
return false; |
|||
} else { |
|||
temp[space[i]] = true; |
|||
} |
|||
} |
|||
|
|||
addr = block[0].address; //检查2*2块
|
|||
if ((addr > 14) || (addr%4 == 3)) {return false;} |
|||
if ((temp[addr] == true) || (temp[addr + 1] == true) || |
|||
(temp[addr + 4] == true) || (temp[addr + 5] == true)) {return false;} |
|||
temp[addr] = temp[addr + 1] = temp[addr + 4] = temp[addr + 5] = true; |
|||
for (i = 1; i <= 5; i++) { //检查2*1与1*2块
|
|||
addr = block[i].address; |
|||
if (block[i].style == 1) { |
|||
if ((addr > 18) || (addr % 4 == 3)) {return false;} |
|||
if ((temp[addr] == true) || (temp[addr + 1] == true)) {return false;} |
|||
temp[addr] = temp[addr + 1] = true; |
|||
} |
|||
if (block[i].style == 2) { |
|||
if (addr > 15) {return false;} |
|||
if ((temp[addr] == true) || (temp[addr + 4] == true)) {return false;} |
|||
temp[addr] = temp[addr + 4] = true; |
|||
} |
|||
} |
|||
for (i = 6; i <= 9; i++) { //检查1*1块
|
|||
addr = block[i].address; |
|||
if (addr > 19) {return false;} |
|||
if (temp[addr] == true) {return false;} |
|||
temp[addr] = true; |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
string Change_str (unsigned int dat) { //将编码数据转化为字符
|
|||
unsigned char i, bit; |
|||
string str = ""; |
|||
for (i = 0; i < 7; i++) { |
|||
bit = 0xF & dat>>(6 - i)*4; //分离单个十六进制位
|
|||
if ((bit >= 0) && (bit <= 9)) {str += bit + 48;} //0~9
|
|||
if ((bit >= 0xA) && (bit <= 0xF)) {str += bit + 55;} //A~F
|
|||
} |
|||
return str; |
|||
} |
|||
|
|||
unsigned int Change_int (char str[8]) { //将编码字符转化为int
|
|||
unsigned int i, dat = 0; |
|||
for (i = 0; i < 7; i++) { |
|||
if ((str[i] >= 48) && (str[i] <= 57)) {dat = dat | (str[i] - 48)<<(24 - i * 4);} //0~9
|
|||
if ((str[i] >= 65) && (str[i] <= 70)) {dat = dat | (str[i] - 55)<<(24 - i * 4);} //A~F
|
|||
if ((str[i] >= 97) && (str[i] <= 102)) {dat = dat | (str[i] - 87)<<(24 - i * 4);} //a~f
|
|||
} |
|||
return dat; |
|||
} |
|||
|
|||
void Find_All_Case(){ //Right_File's MD5: 4A0179C6AEF266699A73E41ECB4D87C1
|
|||
File_Output.open("All_Case.txt"); |
|||
unsigned int i; |
|||
for (i = 0; i < 0xFFFFFFF; i += 4) { |
|||
if (Check(i) == true) {File_Output << Change_str(i) << endl;} |
|||
} |
|||
File_Output.close(); |
|||
} |
|||
|
|||
void Data_Output(string File_name){ |
|||
unsigned int i, j, layer; |
|||
File_Output.open(File_name.c_str()); |
|||
File_Output << "[Group_size]" << endl << group_size << endl; |
|||
File_Output << "[Min_steps]" << endl << min_steps << endl; |
|||
File_Output << "[Farthest_steps]" << endl << farthest_steps << endl; |
|||
File_Output << "[Min_Solutions]" << endl; |
|||
for (i = 0; i < min_Solutions.size(); i++) { |
|||
File_Output << Change_str(min_Solutions[i]) << endl; |
|||
} |
|||
File_Output << "[Farthest_cases]" << endl; |
|||
for (i = 0; i < farthest_cases.size(); i++) { |
|||
File_Output << Change_str(farthest_cases[i]) << endl; |
|||
} |
|||
File_Output << "[Solutions]" << endl; |
|||
for (i = 0; i < Solutions.size(); i++) { |
|||
File_Output << Change_str(Solutions[i]) << " (" << Solutions_steps[i] << ")" << endl; |
|||
} |
|||
File_Output << "[List]" << endl; |
|||
for (i = 0; i < List.size(); i++) { |
|||
File_Output << i << " -> " << Change_str(List[i]) << " (" << Layer_Num[i] << "," << Layer_Index[i] << ")" << endl; |
|||
} |
|||
File_Output << "[Layer]" << endl; |
|||
for (layer = 0; layer < Layer.size(); layer++) { |
|||
for (i = 0; i < Layer_Next[layer].size(); i++) { |
|||
File_Output << "(" << layer << "," << i << ")" << " -> "; |
|||
for (j = 0; j < Layer_Next[layer][i].size(); j++) { |
|||
File_Output << "(" << (layer+1) << "," << Layer_Next[layer][i][j] << ") "; |
|||
} |
|||
File_Output << endl; |
|||
} |
|||
} |
|||
File_Output.close(); |
|||
} |
Binary file not shown.
@ -0,0 +1,358 @@ |
|||
VERSION 5.00 |
|||
Begin VB.Form Form_Detail |
|||
AutoRedraw = -1 'True |
|||
BorderStyle = 1 'Fixed Single |
|||
Caption = "详细信息" |
|||
ClientHeight = 4605 |
|||
ClientLeft = 45 |
|||
ClientTop = 390 |
|||
ClientWidth = 7965 |
|||
LinkTopic = "Form1" |
|||
MaxButton = 0 'False |
|||
MinButton = 0 'False |
|||
ScaleHeight = 4605 |
|||
ScaleWidth = 7965 |
|||
StartUpPosition = 2 '屏幕中心 |
|||
Begin VB.CommandButton Command_Analyse |
|||
Caption = "全局溯源分析" |
|||
Height = 300 |
|||
Left = 2520 |
|||
TabIndex = 4 |
|||
Top = 120 |
|||
Width = 1695 |
|||
End |
|||
Begin VB.Timer Timer_Debug |
|||
Interval = 100 |
|||
Left = 0 |
|||
Top = 0 |
|||
End |
|||
Begin VB.TextBox Text_Debug |
|||
Height = 4380 |
|||
Left = 7960 |
|||
MultiLine = -1 'True |
|||
TabIndex = 3 |
|||
Top = 120 |
|||
Width = 2415 |
|||
End |
|||
Begin VB.Timer Timer_Get_Data |
|||
Interval = 50 |
|||
Left = 0 |
|||
Top = 0 |
|||
End |
|||
Begin VB.ListBox List_Data |
|||
Height = 4020 |
|||
ItemData = "Form_Detail.frx":0000 |
|||
Left = 2520 |
|||
List = "Form_Detail.frx":0002 |
|||
TabIndex = 2 |
|||
Top = 480 |
|||
Width = 1695 |
|||
End |
|||
Begin VB.ComboBox Combo_Detail |
|||
Height = 300 |
|||
Left = 120 |
|||
Style = 2 'Dropdown List |
|||
TabIndex = 1 |
|||
Top = 120 |
|||
Width = 2295 |
|||
End |
|||
Begin VB.ListBox List_Detail |
|||
Height = 4020 |
|||
ItemData = "Form_Detail.frx":0004 |
|||
Left = 120 |
|||
List = "Form_Detail.frx":0006 |
|||
TabIndex = 0 |
|||
Top = 480 |
|||
Width = 2295 |
|||
End |
|||
End |
|||
Attribute VB_Name = "Form_Detail" |
|||
Attribute VB_GlobalNameSpace = False |
|||
Attribute VB_Creatable = False |
|||
Attribute VB_PredeclaredId = True |
|||
Attribute VB_Exposed = False |
|||
Option Explicit |
|||
Private Type Case_Block |
|||
address As Integer |
|||
style As Integer |
|||
End Type |
|||
Private Type Layer_struct |
|||
size As Integer |
|||
layer_dat() As String |
|||
End Type |
|||
Dim wait_data As Boolean, loading As Boolean |
|||
Dim Block(0 To 9) As Case_Block |
|||
Dim start_x As Integer, start_y As Integer, square_width As Integer, gap As Integer |
|||
Dim group_size As Long, min_steps As Integer, farthest_steps As Integer |
|||
Dim min_solutions() As String, farthest_cases() As String, solutions() As String, layers() As String, layer() As Layer_struct |
|||
Private Sub Form_Load() |
|||
start_x = 4350 |
|||
start_y = 135 |
|||
square_width = 777 |
|||
gap = 75 |
|||
loading = False |
|||
If debug_mode = True Then |
|||
Form_Detail.width = 10575 |
|||
Text_Debug.Visible = True |
|||
Else |
|||
Form_Detail.width = 8055 |
|||
Text_Debug.Visible = False |
|||
End If |
|||
If on_top = True Then |
|||
SetWindowPos Me.hwnd, -1, 0, 0, 0, 0, 1 Or 2 |
|||
Else |
|||
SetWindowPos Me.hwnd, -2, 0, 0, 0, 0, 1 Or 2 |
|||
End If |
|||
ReDim min_solutions(0) |
|||
ReDim farthest_cases(0) |
|||
ReDim solutions(0) |
|||
ReDim layers(0) |
|||
ReDim layer(0 To 0) |
|||
Combo_Detail.AddItem "最少步解" |
|||
Combo_Detail.AddItem "所有的解" |
|||
Combo_Detail.AddItem "最远的布局" |
|||
Combo_Detail.AddItem "各步数的布局" |
|||
wait_file_name = start_code & ".txt" |
|||
If Dir(start_code & ".txt") <> "" Then Kill start_code & ".txt" |
|||
Shell "Engine.exe -a " & start_code |
|||
wait_cancel = False |
|||
waiting = True |
|||
wait_data = True |
|||
Form_Wait.Show 1 |
|||
Print_Block start_x, start_y, square_width * 4 + gap * 5, square_width * 5 + gap * 6, case_line_width, case_color, case_line_color |
|||
End Sub |
|||
Private Sub Combo_Detail_Click() |
|||
Dim i As Long |
|||
List_Detail.Clear |
|||
If Combo_Detail.ListIndex = 0 Then |
|||
If min_steps = -1 Then |
|||
List_Detail.AddItem "无解" |
|||
Else |
|||
List_Detail.AddItem Combo_Detail.Text & "(" & min_steps & "步,共" & UBound(min_solutions) & "个)" |
|||
End If |
|||
ElseIf Combo_Detail.ListIndex = 1 Then |
|||
List_Detail.AddItem Combo_Detail.Text & "(共" & UBound(solutions) & "个)" |
|||
ElseIf Combo_Detail.ListIndex = 2 Then |
|||
List_Detail.AddItem Combo_Detail.Text & "(" & farthest_steps & "步,共" & UBound(farthest_cases) & "个)" |
|||
ElseIf Combo_Detail.ListIndex = 3 Then |
|||
For i = 0 To UBound(layer) |
|||
List_Detail.AddItem "第" & i & "步(共" & layer(i).size & "个)" |
|||
Next i |
|||
End If |
|||
List_Detail.ListIndex = 0 |
|||
End Sub |
|||
Private Sub List_Detail_Click() |
|||
Dim i As Long, n As Integer |
|||
loading = True |
|||
List_Data.Clear |
|||
If Combo_Detail.ListIndex = 0 Then |
|||
For i = 1 To UBound(min_solutions) |
|||
If Not min_steps = -1 Then List_Data.AddItem min_solutions(i) & "(" & min_steps & "步)" |
|||
Next i |
|||
ElseIf Combo_Detail.ListIndex = 1 Then |
|||
For i = 1 To UBound(solutions) |
|||
n = n + 1 |
|||
If n = 200 Then n = 0: DoEvents |
|||
List_Data.AddItem Left(solutions(i), 7) & Mid(solutions(i), 9, Len(solutions(i)) - 9) & "步)" |
|||
Next i |
|||
ElseIf Combo_Detail.ListIndex = 2 Then |
|||
For i = 1 To UBound(farthest_cases) |
|||
List_Data.AddItem farthest_cases(i) & "(" & farthest_steps & "步)" |
|||
Next i |
|||
ElseIf Combo_Detail.ListIndex = 3 Then |
|||
For i = 0 To UBound(layer(List_Detail.ListIndex).layer_dat) |
|||
List_Data.AddItem layer(List_Detail.ListIndex).layer_dat(i) & "(" & List_Detail.ListIndex & "步)" |
|||
Next i |
|||
End If |
|||
If Not min_steps = -1 Then |
|||
List_Data.ListIndex = 0 |
|||
Else |
|||
If Combo_Detail.ListIndex = 2 Or Combo_Detail.ListIndex = 3 Then List_Data.ListIndex = 0 |
|||
End If |
|||
loading = False |
|||
End Sub |
|||
Private Sub List_Data_Click() |
|||
Call Analyse_Code(Left(List_Data.List(List_Data.ListIndex), 7)) |
|||
Call Output_Graph |
|||
End Sub |
|||
Private Sub Timer_Get_Data_Timer() |
|||
Dim dat As String |
|||
Combo_Detail.Enabled = Not loading |
|||
If wait_data = True And waiting = False Then |
|||
wait_data = False |
|||
MsgBox Form_Game.Label_Title, , "> _ <" |
|||
Call Get_Data(start_code & ".txt") |
|||
dat = "共衍生出" & group_size & "种布局" & vbCrLf & "最远为" & farthest_steps & "步" & vbCrLf |
|||
If min_steps = -1 Then dat = dat & "无解" Else dat = dat & "最少需要" & min_steps & "步" |
|||
MsgBox dat, , "> _ <" |
|||
Combo_Detail.ListIndex = 0 |
|||
End If |
|||
End Sub |
|||
Private Sub Command_Analyse_Click() |
|||
MsgBox "还没做好呢QAQ", , "> _ <" |
|||
End Sub |
|||
Private Sub Get_Data(file_name As String) |
|||
Dim temp As String |
|||
ReDim min_solutions(0) |
|||
ReDim farthest_cases(0) |
|||
ReDim solutions(0) |
|||
ReDim layers(0) |
|||
Open file_name For Input As #1 |
|||
Line Input #1, temp: Line Input #1, temp |
|||
group_size = temp |
|||
Line Input #1, temp: Line Input #1, temp |
|||
min_steps = temp |
|||
Line Input #1, temp: Line Input #1, temp |
|||
farthest_steps = temp |
|||
Line Input #1, temp: Line Input #1, temp |
|||
While (temp <> "[Farthest_cases]") |
|||
ReDim Preserve min_solutions(UBound(min_solutions) + 1) |
|||
min_solutions(UBound(min_solutions)) = temp |
|||
Line Input #1, temp |
|||
Wend |
|||
Line Input #1, temp |
|||
While (temp <> "[Solutions]") |
|||
ReDim Preserve farthest_cases(UBound(farthest_cases) + 1) |
|||
farthest_cases(UBound(farthest_cases)) = temp |
|||
Line Input #1, temp |
|||
Wend |
|||
Line Input #1, temp |
|||
While (temp <> "[List]") |
|||
ReDim Preserve solutions(UBound(solutions) + 1) |
|||
solutions(UBound(solutions)) = temp |
|||
Line Input #1, temp |
|||
Wend |
|||
Line Input #1, temp |
|||
While (temp <> "[Layer]") |
|||
ReDim Preserve layers(UBound(layers) + 1) |
|||
layers(UBound(layers)) = temp |
|||
Line Input #1, temp |
|||
Wend |
|||
Close #1 |
|||
Call split_layer |
|||
End Sub |
|||
Private Sub split_layer() |
|||
Dim i As Long, code As String, num As Integer, index As Integer |
|||
For i = 1 To UBound(layers) |
|||
code = Mid(layers(i), InStr(1, layers(i), ">") + 2, 7) |
|||
num = Mid(layers(i), InStr(1, layers(i), "(") + 1, InStr(1, layers(i), ",") - InStr(1, layers(i), "(") - 1) |
|||
index = Mid(layers(i), InStr(1, layers(i), ",") + 1, Len(layers(i)) - InStr(1, layers(i), ",") - 1) |
|||
ReDim Preserve layer(0 To num) |
|||
ReDim Preserve layer(num).layer_dat(0 To index) |
|||
layer(num).layer_dat(index) = code |
|||
layer(num).size = index + 1 |
|||
Next i |
|||
End Sub |
|||
Private Sub Output_Graph() |
|||
Dim m, X, Y As Integer |
|||
Dim width As Integer, height As Integer |
|||
Print_Block start_x, start_y, square_width * 4 + gap * 5, square_width * 5 + gap * 6, case_line_width, case_color, case_line_color |
|||
For m = 0 To 9 |
|||
If Block(m).address <> 25 Then |
|||
X = (Block(m).address Mod 4) * (square_width + gap) + gap + start_x |
|||
Y = Int(Block(m).address / 4) * (square_width + gap) + gap + start_y |
|||
If Block(m).style = 0 Or Block(m).style = 1 Then |
|||
width = square_width * 2 + gap |
|||
Else |
|||
width = square_width |
|||
End If |
|||
If Block(m).style = 0 Or Block(m).style = 2 Then |
|||
height = square_width * 2 + gap |
|||
Else |
|||
height = square_width |
|||
End If |
|||
Print_Block X, Y, width, height, block_line_width, block_color, block_line_color |
|||
End If |
|||
Next m |
|||
End Sub |
|||
Private Sub Print_Block(print_start_x, print_start_y, print_width, print_height, print_line_width, print_color, print_line_color) |
|||
If print_width < 0 Or print_height < 0 Then Exit Sub |
|||
FillStyle = 0 |
|||
DrawWidth = print_line_width |
|||
FillColor = print_color |
|||
Line (print_start_x, print_start_y)-(print_start_x + print_width, print_start_y + print_height), print_color, B |
|||
Line (print_start_x, print_start_y)-(print_start_x + print_width, print_start_y + print_height), print_line_color, B |
|||
End Sub |
|||
Private Sub Analyse_Code(code As String) |
|||
On Error Resume Next |
|||
Dim temp(1 To 12) As Integer |
|||
Dim i, addr, style As Integer |
|||
Dim type_1, type_2, type_3 As Integer |
|||
Dim Table(0 To 19) As Integer |
|||
Dim num As Integer, b1 As Integer, b2 As Integer |
|||
Dim dat As String |
|||
For i = 1 To 6 |
|||
dat = Mid(code, i + 1, 1) |
|||
If Asc(dat) >= 48 And Asc(dat) <= 57 Then num = Int(dat) |
|||
If Asc(dat) >= 65 And Asc(dat) <= 70 Then num = Asc(dat) - 55 |
|||
b1 = num Mod 4 |
|||
b2 = (num - b1) / 4 Mod 4 |
|||
temp(i * 2 - 1) = b2 |
|||
temp(i * 2) = b1 |
|||
Next i |
|||
type_1 = 0: type_2 = 0: type_3 = 5 |
|||
For i = 0 To 19 |
|||
Table(i) = 69 |
|||
Next i |
|||
For i = 0 To 9 |
|||
Block(i).address = 69 |
|||
Block(i).style = 69 |
|||
Next i |
|||
dat = Left(code, 1) |
|||
If Asc(dat) >= 48 And Asc(dat) <= 57 Then num = Int(dat) |
|||
If Asc(dat) >= 65 And Asc(dat) <= 70 Then num = Asc(dat) - 55 |
|||
Block(0).address = num |
|||
Block(0).style = 0 |
|||
If Block(0).address > 14 Then GoTo err |
|||
Table(Block(0).address) = 0 |
|||
Table(Block(0).address + 1) = 0 |
|||
Table(Block(0).address + 4) = 0 |
|||
Table(Block(0).address + 5) = 0 |
|||
addr = 0 |
|||
For i = 1 To 11 |
|||
Do While Table(addr) <> 69 |
|||
If addr < 19 Then |
|||
addr = addr + 1 |
|||
Else |
|||
Exit Do |
|||
End If |
|||
Loop |
|||
style = temp(i) |
|||
If style = 0 Then |
|||
Table(addr) = 10 |
|||
ElseIf style = 1 Then |
|||
If type_2 < 5 Then type_2 = type_2 + 1 |
|||
If addr > 18 Then GoTo err |
|||
Block(type_2).style = 1 |
|||
Block(type_2).address = addr |
|||
Table(addr) = type_2 |
|||
Table(addr + 1) = type_2 |
|||
ElseIf style = 2 Then |
|||
If type_2 < 5 Then type_2 = type_2 + 1 |
|||
If addr > 15 Then GoTo err |
|||
Block(type_2).style = 2 |
|||
Block(type_2).address = addr |
|||
Table(addr) = type_2 |
|||
Table(addr + 4) = type_2 |
|||
ElseIf style = 3 Then |
|||
If type_3 < 9 Then type_3 = type_3 + 1 |
|||
Block(type_3).style = 3 |
|||
Block(type_3).address = addr |
|||
Table(addr) = type_3 |
|||
End If |
|||
Next i |
|||
err: |
|||
End Sub |
|||
Private Sub Timer_Debug_Timer() |
|||
Dim debug_dat As String |
|||
debug_dat = debug_dat & "group_size=" & group_size & vbCrLf |
|||
debug_dat = debug_dat & "min_steps=" & min_steps & vbCrLf |
|||
debug_dat = debug_dat & "farthest_steps=" & farthest_steps & vbCrLf |
|||
debug_dat = debug_dat & vbCrLf |
|||
debug_dat = debug_dat & "min_solutions->" & UBound(min_solutions) & vbCrLf |
|||
debug_dat = debug_dat & "farthest_cases->" & UBound(farthest_cases) & vbCrLf |
|||
debug_dat = debug_dat & "solutions->" & UBound(solutions) & vbCrLf |
|||
debug_dat = debug_dat & "layers->" & UBound(layers) & vbCrLf |
|||
debug_dat = debug_dat & "layer->" & UBound(layer) & vbCrLf |
|||
Text_Debug = debug_dat |
|||
End Sub |
Binary file not shown.
@ -1,9 +1,10 @@ |
|||
Form_Game = 52, 52, 883, 479, , 26, 28, 857, 453, C |
|||
Module = 52, 52, 883, 479, |
|||
Form_Classic_Cases = 104, 104, 891, 531, Z, 104, 104, 937, 531, C |
|||
Form_Classic_Cases = 104, 104, 891, 531, , 104, 104, 937, 531, C |
|||
Form_Creator = 130, 130, 917, 557, , 104, 104, 891, 531, C |
|||
Form_Rand_Case = 78, 78, 855, 505, , 156, 156, 933, 583, C |
|||
Form_Favourite = 52, 52, 829, 479, , 26, 26, 803, 453, C |
|||
Form_Favourite_Add = 156, 156, 933, 583, , 182, 182, 959, 609, C |
|||
Form_Solution = 104, 104, 862, 531, , 0, 0, 758, 427, C |
|||
Form_Wait = 104, 104, 862, 531, , 78, 78, 836, 505, C |
|||
Form_Detail = 26, 26, 784, 453, , 78, 78, 836, 505, C |
|||
|
Loading…
Reference in new issue