纯C语言实现的俄罗斯方块预测ai(北京邮电大学2024程序设计课程期末作业)

前言:本人2024级北京邮电大学新生一枚,这个题目是计院和果园大一下程序设计课程的期末作业,题目的大意是让我们写一个自己玩俄罗斯方块的ai,据说老师的大意是让我们写一个决策树类型的东西,能跑就行,所以分数下线设的很低,测试样例10万块通过只需要5000分(消一行100分),但由于老师也设了前5%给满分,前15%给98的分数梯度,(意思就是说年级只有20个人满分),导致后来大家都开始卷ai,最后导致10万块的话最高好像是513万分,后来又举办了一个百万块的竞赛,最高好像是5000万出头,当时有两个5000万以上的,鄙人当时的数据是4993万分,问了老师应该是当时的第三名,故写这篇文章,为后面的学弟学妹们做一个参考。

一,题目

1,问题描述

俄罗斯方块(Tetris)作为风靡全球41年的现象级益智游戏,其简单易学但难于精通的特性使其成为游戏史上的不朽经典。以下是其核心游戏规则解析及我们的要求:

游戏界面由20行×10列的可视区域组成,7种不同形状的四格方块(I/T/O/J/L/S/Z型)随机从顶部生成下落(具体形状如下图所示),并且在一个方块生成后,玩家可以知道下一个方块的形状。

• 横向范围:X轴区间 [0, 9](共10列)
• 纵向范围:Y轴区间 [0, 19](共20行,Y=0为底部,Y=19为顶部)
• 死亡判定线:Y ≥ 15(当方块堆叠高度触达此线时游戏结束)

在本问题中玩家使用的是简化版的操作规则,只能进行以下三种操作:

• 横向移动:左右平移
• 旋转操作:支持0°、90°、180°、270°四向旋转
• 瞬间落地:瞬间抵达当前可到达的最底部位置

当一行及以上(Y轴坐标相同)的全部位置被完全填充时,这些行将被消除,并且玩家将根据同时消除的行数获得分数:

• 单行消除:100分
• 双行消除:300分
• 三行消除:500分
• 四行消除:800分

本次作业要求做两个版本,一个OJ版,一个图形化版。其中OJ版需个人独立完成,图形化版需由3人组成的小组合作完成。

图形化版界面及玩法自行设计,但至少要完成单人游戏及记分功能。

OJ版为交互式题目。要求在每次输出数据后、读入下一个数据前使用fflush(stdout)刷新输出缓冲区。

2,OJ版输入

第一行为两个单字符,即S0S1S0S1I,T,O,J,L,S,ZS0S1 分别表示第一个要下落的方块和第二个要下落的方块的形状。交互过程中,你每次依次输出一个方块的放置方案。在你每次输出放置方块的方案后,交互程序将再给你一行新的输入,该输入为一个单字符,即SiSiI,T,O,J,L,S,Z,X,E )。表示一个新的下个要下落的方块的形状。若Si 为X表示X前边的方块是最后一个方块,程序应该在输出最后一个方块的放置方案后结束。若Si 为E表示玩家的放置方案已导致游戏结束(堆叠高度触达死亡判定线),程序应直接退出。测试数据均采用随机种子生成,且保证输入数据的总长度(即掉落的方块总数)小于1,000,000。

3,输出

每次交互需输出两行,第一行为用一个空格分隔的两个整数,依次为c和x。其中c表示方块顺时针旋转的角度(0、90、180、270),x表示方块在x坐标上的最小值。第二行为一个整数,表示放置完方块后玩家当前的得分。

4,一次交互过程示例

交互程序会首先提供给你1行2个输入:
IT
表示第一个方块为I,下一个方块为T。

然后你的程序输出将方块I旋转的角度和放置的位置及当前玩家得分,比如:
0 0
0
表示顺时针旋转0度、最左端放置在x=0的列上,当前得分为0。

然后交互程序提供新的下一个方块的形状:
T
此时当前方块形状变为T、下一个方块的形状也为T。

你的程序在输出将方块T旋转的角度和放置的位置及当前玩家得分,比如:
0 4
0

然后交互程序提供输入X告诉你方块已经终止:
X

最后你输出最后一个T的放置方式及当前玩家得分,比如:
0 7
100

此时最下面一行刚好被放满,因此玩家当前得分为100分。

5,OJ版评分规则

假设:1、OJ版满分为100分,其中基础分90分,奖励分10分;2、你的程序在学术诚信、代码风格等项上没有被扣分。

如果你的程序中的玩家得分大于等于我们设定的基础分数(这个基础分数待确定),则可以得到基础分90分;如果小于我们设定的基础分数,则OJ版成绩为0分(将不参与奖励分排名)。

我们会将全年级同学中所有玩家得分大于等于我们设定的基础分数的程序根据玩家得分进行排名。排名前5%的程序得奖励分10分;排名前5%到15%的程序得奖励分8分;排名前15%到30%的程序得奖励分6分;排名前30%到50%的程序得奖励分4分;排名前50%到75%的程序得奖励分2分。注意:因为直接计算每档的人数未必是整数,且可能有同分的程序(同分的程序得分一定相同),所以同学们不要死扣这个比例,这不是一个精准的范围。

提交分两次。第一次为验证数据,是给大家调试程序用的,不限提交次数。你的程序基于验证数据的分数与OJ版最终得分无关;第二次为测试数据,OJ版最终得分由你的程序在测试数据上的表现决定。注意:在测试数据上我们允许的正常提交次数为3次。超过3次之后每多提交一次扣10分,扣到OJ版得0分为止。

说明:测试数据和验证数据基于相同的方式生成,但会使用不同的随机种子。

补充说明

以上是当时我们拿到的题目的原文,学弟学妹们看了应该感觉挺简单的,毕竟现在AI都基本上随便写,而且有非常多的开源项目,比如说CSDN上就可以搜到很多像python直接调用模型的或者是直接抄Pierre Dellacherie算法,隔壁北大同期五子棋AI题目看着就比这个难很多,但是哈但是,个人感觉这个题目要想拿到一个比较高的分数还是比较困难的,一者是必须用C/C++来写,所以想用各种模型的同学就可以不用想了,(不排除有人用C/C++手搓大模型,虽说好像难度也还好),二者是有时间限制,要求是10组100k的测试数据每组必须在10s内跑完,而且必须全部通过才算有效成绩(一共只有三次机会)(这个预测速度还是比较快的,有点难搞定的)(ps:实测如果采用时间到了就自动卡断的策略的话,实际只有8.4s可以使用,再多就会TLE),三者是AI不靠谱,去年有很多人吐槽难的原因之一就是拿AI写的代码,甚至是自己写的,在本地运行的好好的,测试也都没问题,上了OJ平台就报错,关键是不给具体报错结果,就直接红温,(笔者也是改了1-2次才通过的)(下面会有写介绍如何查错,以及如何用AI查错(应该吧...))

二,编程经过

1,准备工作

当时是3月份发布的这个作业,笔者和笔者的一个同学就感觉这个题目比较有意思,因为主要是当时教的都比较简单所以就想在这个题目上多搞点事情,所以很早就开始写了(偏题了)我们当时查到的资料就只有一篇鹅厂入职题的开源还算比较有用(好像现在链接也打不开了),主要是关于Pierre Dellacherie算法的,笔者就在这个基础上开始写纯C语言的代码了,下面是第一版的代码

2,思路介绍

笔者和同学当时的思路就是依据Pierre Dellacherie先做一版出来看看效果,事实证明PD的效果并不好,非常不稳定(虽然非常快,这些特点是单层预测的通病...)。

简单介绍一下Pierre Dellacherie算法:这个算法相当于一个遍历算法,在玩俄罗斯方块时你手上有一块正在下落的方块,所以这个算法就想:我只需要把这块放下去,然后比较各种情况,找最好的那个放,而这个好的标准,PD算法则是定义了六个参数,用于评估当前块都放下去后的地形是否比较好,(注意这里我们只关心放下去后的地形)这六个参数分别是:

LandingHeight(下落后的高度):即方块下落后的高度(方块最后不能再下落时,方块中心的高度),这个高度肯定是值越小越好。(这个参数其实相当于地形的总高度,笔者的代码里直接写成了总高度,方便后面修改并添加相关的其他参数(其实是适当偷懒...))

ErodedPieceCellsMetric(消除贡献度):即该方块下落后消行层数与该方块被消掉的块数的乘积,这个肯定越大越好。

BoardRowTransitions(行变换):一行中若出现左边是空的右边是实的或者左边是实的右边是空的的情况算一次变换。这个参数和下面的列变换主要是为了评估整个Map地形里的平整程度,所以肯定越小越好。

BoardColTrabsitions(列变换):和行变换类似,一列中若出现上边和下边一个空的一个实的的情况的话算一次变换。

BoardBuriedHoles(空洞数):即每一列从最高的实格向下数有多少个空的格数的总和。这个参数弥补了行变换和列变换无法评估具体的空腔情况的不足。

BoardWells(井数):即看每一列若是两侧都比本列高,即算一个井,BoardWells+=左高差+右高差。这个参数强化了表面的平整度,这个参数肯定越小越好。

具体的实现可以参考以下链接

链接:基于Pierre Dellacherie的俄罗斯方块-05Pierre Dellacherie算法 - sky灬饮冰 - 博客园基于Pierre Dellacherie的俄罗斯方块-05Pierre Dellacherie算法 Pierre Dellacherie算法感觉上像是一个遍历算法,给与各个参数不同的权重,使得更加合理的摆放方块 评估主要是6个参数: LandingHeight:下落后的高度,方块最后不能下落之后,方https://www.cnblogs.com/ice1992/p/17221759.html

现在我们稍微改造一下这个算法,PD算法只算了当前下落的一块,而在玩俄罗斯方块时你手上有一块正在下落的方块以及你能且仅能看到下一块,所以实际上你能看到两块,所以我们就想:只需要把两块都放下去,然后看那种情况好就行了,而这个好的标准,笔者则是定义了10个参数,用于评估两块都放下去后的地形是否比较好,(注意这里我们只关心放下去后的地形)(且这个比较好的解的含义是当前第二块的保底解,避免了单块预测时第一块的确是最优解了,但第二块没法放了的情况)这十个参数如下:

h_difference_max(最大高差):即最大高差,字面意思,这个程序当时主要是为了活得久,毕竟目标主要是为了百万块竞赛去的,所以比较注重减少最高高度,添加这个参数是为了向程序强调这件事。

hole_count(空洞数)同上面的BoardBuriedHoles。

columnwithhole_count(带洞列数):即带有空洞的列数,字面意思,添加这个参数是期望减少带洞的列,(可能与列变换和行变换有点功能重复)。

bumpiness(表面不平整度):即左右高差的总和,用来衡量(不懂的话可以去看代码),添加是为了加强对表面平整度,笔者认为表面平整度越高,下一次消行的概率越高,能存活的时间也就越长。

row_transitions(行变换)同上面的BoardRowTransitions。

column_transitions(列变换)同上面的BoardColumnTransitions。

pit_count(空列数):即空的列的个数,字面意思,添加这个参数是因为测试时发现程序为了避免空洞的产生总是产生很多空列,不好好消行。

pit_deepest(最大井深):即每列与左右两列的高差绝对值的较小值中的最大值(这个还是看代码去吧,笔者的笔墨不太顺畅,解释不清...)。

bury_depth_sum(总埋藏深度):即每个空洞到表面的距离的和,笔者个人感觉这个添加的参数是最有用的,当一个空洞越浅时越可能在下一次消行中暴露到表面,而原有PD算法并没有对空洞的深浅进行限制,只是衡量了空洞在整个地形上是否整齐,所以这个参数笔者最建议添加。

第一版代码

  • 注:当时笔者比较头铁,用的是DEV C++,不支持中文注释,所以代码里面都是纯英文的注释,还请谅解...
  • 注:这个代码是当时的初代最终版本的代码,也是笔者所有这个项目里编写的第一个程序项目,最开始是用来调试全局变量、初始化、常用函数以及规则代码部分有没有问题的程序(第一版代码太难看了,还是不放出来污染大家的眼睛了,虽然这版也不好看,后面应该会有一版最新的吧(应该吧...))
  • 注:不管是所有主代码(即提交版代码)的常用函数、规则代码、可视化代码基本就不变了,全区变量和初始化部分会增删weight(参数权重)。
  • 注:如果代码是在读不懂的话,可以丢到AI里让它去辅助解读,(但笔者认为自己的代码已经够好读了,毕竟实在DEV上写的,不好读的话自己都查不出错来...)
头文件+全局变量部分
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>
#include<time.h>
#define INF 0x3f3f3f3f
#define data_L 10000


//main data(changing)
int Map[21][12];//x1-10,y1-20,deadline->y16
int Map_1[21][12];//graph_a0 drop
int Map_2[21][12];//graph_a0 and graph_a1 drop
int Score;//score
int h[12];//height of columns

//factors' coefficient
float Weight[11];

//other data(fixed)
const int X[4]={-1,0,1,0};//direction x
const int Y[4]={0,1,0,-1};//direction y
const int P[7]={2,4,1,4,4,2,2};//the number of graphs' posture
struct graph
{
	int X[4];
	int Y[4];
}G[7][4];//graphs' coordinates, benchmark -> the lowwer left corner
int W[7][4];//width of graphs
int H[7][4];//height of graphs

笔者设的全局变量主要分为三类:

  • Map类Map->主地图,Map_1->用于预测的模拟地图,相当于主地图+一块下落的方块,Map_2->用于预测的模拟地图,相当于Map_1+一块未下落的方块
  • factor类:Weight_>包含了10个参数的权重。
  • other data(fixed)类X、Y->移动键(笔者的习惯),P->每个方块的姿态数(主要是用于减少由姿态重复导致的重复计算用的),G->七个方块中四个小方格的具体位置,以左下角为基准,W->方块的宽度,H->方块的高度

注:这里的Map是笔者自定义的,比题目要求的地图多了最左侧和最右侧完全填充的两列,最下面完全填充的一行和最上面四行空行,目的是方面写参数函数时不必考虑边缘问题。

常用函数部分
//common invoking functions
int Max(int x,int y)
{
	return x>y?x:y;
}
int Min(int x,int y)
{
	return x<y?x:y;
}
void Map_copy(int M1[][12],int M2[][12])
{
	for(int i=0;i<21;i++)
	    for(int j=0;j<12;j++)
	        M2[i][j]=M1[i][j];
}
void H_messure(int M[][12])
{
	for(int i=0;i<=11;i++)
	    for(int j=20;j>=0;j--)
	        if(M[j][i]){h[i]=j;break;}
}
int Translate(char a)
{
	if(a=='I'){return 0;}
	if(a=='T'){return 1;}
	if(a=='O'){return 2;}
	if(a=='J'){return 3;}
	if(a=='L'){return 4;}
	if(a=='S'){return 5;}
	if(a=='Z'){return 6;}
}

char translate(int x)
{
	if(x==0){return 'I';}
	if(x==1){return 'T';}
	if(x==2){return 'O';}
	if(x==3){return 'J';}
	if(x==4){return 'L';}
	if(x==5){return 'S';}
	if(x==6){return 'Z';}
}

这里应该没什么要说的吧...

初始化部分
//setting initial data Map,Score,G,W,H and factors' coefficient
void initial_set()
{
	for(int i=0;i<=20;i++)
	    memset(Map[i],0,sizeof(int));
	for(int i=0;i<=20;i++)
        Map[i][0]=1;
    for(int i=0;i<=20;i++)
        Map[i][11]=1;
    for(int i=1;i<=10;i++)
        Map[0][i]=1;
	Score=0;
	//G[i][j],j=0->0,j=1->90,j=2->180,j=3->270,clockwise
    //coordinate benchmark->graphs' lower left corner
	//i=0->I
	memcpy(G[0][0].X,(int[4]){0,1,2,3},sizeof(int)*4);memcpy(G[0][0].Y,(int[4]){0,0,0,0},sizeof(int)*4);
	memcpy(G[0][1].X,(int[4]){0,0,0,0},sizeof(int)*4);memcpy(G[0][1].Y,(int[4]){0,1,2,3},sizeof(int)*4);
	W[0][0]=4;H[0][0]=1;
	W[0][1]=1;H[0][1]=4;
	//i=1->T
	memcpy(G[1][0].X,(int[4]){0,1,1,2},sizeof(int)*4);memcpy(G[1][0].Y,(int[4]){0,1,0,0},sizeof(int)*4);
	memcpy(G[1][1].X,(int[4]){0,0,0,1},sizeof(int)*4);memcpy(G[1][1].Y,(int[4]){0,1,2,1},sizeof(int)*4);
	memcpy(G[1][2].X,(int[4]){0,1,1,2},sizeof(int)*4);memcpy(G[1][2].Y,(int[4]){1,1,0,1},sizeof(int)*4);
	memcpy(G[1][3].X,(int[4]){0,1,1,1},sizeof(int)*4);memcpy(G[1][3].Y,(int[4]){1,2,1,0},sizeof(int)*4);
	W[1][0]=3;H[1][0]=2;
	W[1][1]=2;H[1][1]=3;
	W[1][2]=3;H[1][2]=2;
	W[1][3]=2;H[1][3]=3;
	//i=2->O
	memcpy(G[2][0].X,(int[4]){0,0,1,1},sizeof(int)*4);memcpy(G[2][0].Y,(int[4]){1,0,1,0},sizeof(int)*4);
	W[2][0]=2;H[2][0]=2;
	//i=3->J
	memcpy(G[3][0].X,(int[4]){0,0,1,2},sizeof(int)*4);memcpy(G[3][0].Y,(int[4]){1,0,0,0},sizeof(int)*4);
	memcpy(G[3][1].X,(int[4]){0,0,0,1},sizeof(int)*4);memcpy(G[3][1].Y,(int[4]){2,1,0,2},sizeof(int)*4);
	memcpy(G[3][2].X,(int[4]){0,1,2,2},sizeof(int)*4);memcpy(G[3][2].Y,(int[4]){1,1,1,0},sizeof(int)*4);
	memcpy(G[3][3].X,(int[4]){0,1,1,1},sizeof(int)*4);memcpy(G[3][3].Y,(int[4]){0,2,1,0},sizeof(int)*4);
	W[3][0]=3;H[3][0]=2;
	W[3][1]=2;H[3][1]=3;
	W[3][2]=3;H[3][2]=2;
	W[3][3]=2;H[3][3]=3;
	//i=4->L
	memcpy(G[4][0].X,(int[4]){0,1,2,2},sizeof(int)*4);memcpy(G[4][0].Y,(int[4]){0,0,1,0},sizeof(int)*4);
	memcpy(G[4][1].X,(int[4]){0,0,0,1},sizeof(int)*4);memcpy(G[4][1].Y,(int[4]){2,1,0,0},sizeof(int)*4);
	memcpy(G[4][2].X,(int[4]){0,0,1,2},sizeof(int)*4);memcpy(G[4][2].Y,(int[4]){1,0,1,1},sizeof(int)*4);
	memcpy(G[4][3].X,(int[4]){0,1,1,1},sizeof(int)*4);memcpy(G[4][3].Y,(int[4]){2,2,1,0},sizeof(int)*4);
	W[4][0]=3;H[4][0]=2;
	W[4][1]=2;H[4][1]=3;
	W[4][2]=3;H[4][2]=2;
	W[4][3]=2;H[4][3]=3;
	//i=5->S
	memcpy(G[5][0].X,(int[4]){0,1,1,2},sizeof(int)*4);memcpy(G[5][0].Y,(int[4]){0,1,0,1},sizeof(int)*4);
	memcpy(G[5][1].X,(int[4]){0,0,1,1},sizeof(int)*4);memcpy(G[5][1].Y,(int[4]){2,1,1,0},sizeof(int)*4);
	W[5][0]=3;H[5][0]=2;
	W[5][1]=2;H[5][1]=3;
	//i=6->Z
	memcpy(G[6][0].X,(int[4]){0,1,1,2},sizeof(int)*4);memcpy(G[6][0].Y,(int[4]){1,1,0,0},sizeof(int)*4);
	memcpy(G[6][1].X,(int[4]){0,0,1,1},sizeof(int)*4);memcpy(G[6][1].Y,(int[4]){1,0,2,1},sizeof(int)*4);
	W[6][0]=3;H[6][0]=2;
	W[6][1]=2;H[6][1]=3;
	
	//initialize factors' coefficient
	Weight[1]=-0.5;//h_difference_max, negative
	Weight[2]=-5.0;//hole_count, negative
	Weight[3]=-0.1;//columnwithhole_count, negative
	Weight[4]=-0.5;//bumpiness, neutral
	Weight[5]=-1.0;//row_transitions, negative
	Weight[6]=-1.0;//column_transitions, negative
	Weight[7]=-0.2;//pit_count, neutral
	Weight[8]=-0.2;//pit_deepest, neutral
	Weight[9]=-0.3;//bury_depth_sum, negative
	Weight[10]=5.0;//line_clear_count, positive
	
}

非常长的初始化部分...

规则函数部分
//tetromino rules
int Y_limit(int M[][12])
{
	for(int i=1;i<=10;i++)
	    if(M[16][i]){return 0;}
	return 1;
}

void Line_clear(int y,int M[][12])
{
	for(int i=y;i<20;i++)
	    for(int j=1;j<=10;j++)
	        M[i][j]=M[i+1][j];
}

int Line_clear_all(int M[][12])
{
	int sum;
	int count=0;
	for(int i=19;i>=1;i--)
	{
		sum=0;
	    for(int j=1;j<=10;j++)
	    	sum+=Map[i][j];
	    if(sum==10)
		{
		    count++;
		    Line_clear(i,Map);
		}
    }
    return count;
}

void Score_add(int count)
{
    if(count==1){Score+=100;}
    else if(count==2){Score+=300;}
    else if(count==3){Score+=500;}
    else if(count==4){Score+=800;}
}
void Drop_1(int M[][12],int A,int c,int x)//map drop
{
	H_messure(M);//messure h[i]
	
	int min=INF;//the min of y_distance between graph and all the 1 of map (x is 1 to 10)
	
	for(int i=0;i<4;i++)
	{
		int xg=x+G[A][c].X[i];
		int yg=16+G[A][c].Y[i];
	    min=Min(min,yg-h[xg]);
	}
	for(int i=0;i<4;i++)
    {
    	int xg=x+G[A][c].X[i];
    	int yg=16-min+G[A][c].Y[i]+1;
    	M[yg][xg]=1;
	}
}

int Drop_2(int M1[][12],int M2[][12],int A,int c,int x)
{
	H_messure(M1);//messure h[i]
	
	int min=INF;//the min of y_distance between graph and all the 1 of map (x is 1 to 10)
	
	for(int i=0;i<4;i++)
	{
		int xg=x+G[A][c].X[i];
		int yg=16+G[A][c].Y[i];
	    min=Min(min,yg-h[xg]);
	}
	Map_copy(M1,M2);
	for(int i=0;i<4;i++)
    {
    	int xg=x+G[A][c].X[i];
    	int yg=16-min+G[A][c].Y[i]+1;
    	M2[yg][xg]=1;
	}
	return Y_limit(M2);
}

这里的6个函数笔者简单介绍一下:

  • Y_limit(高度限制):即高度限制,字面意思,当主地图在消行完后仍超过地图的16行(笔者自定义的地图)
  • Line_clear(消行):即消行,字面意思。
  • Line_clear_all(全图消行):即全图消行,字面意思,输出消行总数。
  • Score_add(加分器):即加分器,字面意思,用于修改分数。
  • Drop_1(Map1的方块下落函数):将Map复制到Map_1里,并放置当前下落方块。
  • Drop_2(Map2的方块下落函数):将Map_1复制到Map_2里,并放置当前未下落方块。
参数函数部分
//factors function
int h_difference_max(int M[][12])//factor1
{
	int max=0;
	for(int i=1;i<10;i++)
	    for(int j=i+1;j<=10;j++)
	        if(max<abs(h[i]-h[j])){max=abs(h[i]-h[j]);}
	return max;
}

int hole_count(int M[][12])//factor2
{
	int sum=0;
	for(int i=1;i<=10;i++)
	    for(int j=1;j<=h[i];j++)
	        if(!M[j][i]){sum++;}
	return sum;
}

int columnwithhole_count(int M[][12])//factor3
{
	int count=0;
	int sum=0;
	for(int i=1;i<=10;i++)
    {
    	sum=0;
    	for(int j=0;j<=h[i];j++)
    	{
    		sum+=M[j][i]^M[j+1][i];
		}
		if(sum!=1){count++;}
	}
	return count;
}

int bumpiness(int M[][12])//factor4
{
	int sum=0;
	for(int i=1;i<10;i++)
	    sum+=abs(h[i]-h[i+1]);
	return sum;
}

int row_transitions(int M[][12])//factor5
{
	int count=0;
	for(int i=0;i<11;i++)
	    for(int j=1;j<=16;j++)
	        count+=M[j][i]^M[j][i+1];
	return count;
}

int column_transitions(int M[][12])//factor6
{
	int count=0;
	for(int i=1;i<=10;i++)
	    for(int j=0;j<=16;j++)
	        count+=M[j][i]^M[j+1][i];
	return count;
}

int pit_count(int M[][12])//factor7
{
	int count=0;
	for(int i=2;i<10;i++)
	    if(!h[i]){count++;}
	return count;
}

int pit_deepest(int M[][12])//factor8
{
	int deepest=0;
	int deep;
	for(int i=1;i<=10;i++)
	{
		deep=Min(abs(h[i-1]-h[i]),abs(h[i]-h[i+1]));
	    if(deepest<deep){deepest=deep;}
	}
	return deepest;
}

int bury_depth_sum(int M[][12])//factor9
{
	int sum=0;
	for(int i=1;i<=10;i++)
	    for(int j=h[i];j>=1;j--)
	    	if(!M[j][i]){sum+=h[i]-j;}
	return sum;
}

int line_clear_count(int M[][12])//factor10
{
	int sum;
	int count=0;
	for(int i=16;i>=1;i--)
	{
		sum=0;
	    for(int j=1;j<=10;j++)
	    	sum+=M[i][j];
	    if(sum==10)
	    {
		    count++;
		    Line_clear(i,M);
		}
    }
    return count;
}

float line_clear_count_weights(int M[][12])
{
	float price=0;
	if(line_clear_count(M)==1)
	    price=0.1;
	else if(line_clear_count(M)==2)
	    price=0.3;
	else if(line_clear_count(M)==3)
	    price=0.5;
	else if(line_clear_count(M)==4)
	    price=0.8;
	return price;
}

float main_factor_price(int M[][12],float clear_count_price)
{
	float price=0;
	H_messure(M);//messure h[i]
	
    //factor1 h_difference_max -
	price+=Weight[1]*(1.0*h_difference_max(M));
	//factor2 hole_count -
	price+=Weight[2]*(1.0*hole_count(M));
	//factor3 columnwithhole_count -
	price+=Weight[3]*(1.0*columnwithhole_count(M));
	//factor4 bumpiness 
	price+=Weight[4]*(1.0*bumpiness(M));
	//factor5 row_transitions
	price+=Weight[5]*(1.0*row_transitions(M));
	//factor6 column_transitions
	price+=Weight[6]*(1.0*column_transitions(M));
	//factor7 pit_count
	price+=Weight[7]*(1.0*pit_count(M));
	//factor8 pit_deepest
	price+=Weight[8]*(1.0*pit_deepest(M));
	//factor9 bury_depth_sum
	price+=Weight[9]*(1.0*bury_depth_sum(M));
	//factor10 line_clear_count
	price+=Weight[10]*(1.0*clear_count_price);
	
	return price;
}
可视化部分
//visualize functions
char Ptran(int x)
{
	if(x){return '1';}
	else{return '0';}
}

void Print_H()
{
	printf("\n");
	
	for(int i=0;i<12;i++)
	{
	    if(i==11){printf("%d\n",h[i]);}
	    else{printf("%d ",h[i]);}
	}
}

void Print_H_File(FILE* fp)
{
	for(int i=0;i<12;i++)
    {
    	if(i==11){fprintf(fp,"%d\n",h[11]);}
    	else{fprintf(fp,"%d ",h[i]);}
	}
}

void Print_M(int M[][12])
{
	for(int i=20;i>=0;i--)
	    for(int j=0;j<12;j++)
	    {
	    	if(j==11){printf("%d\n",M[i][j]);}
	    	else{printf("%d ",M[i][j]);}
		}
	printf("\n");
}

void Print_M_File(int M[][12],FILE* fp)
{
	fputc('\n',fp);
	for(int i=20;i>=0;i--)
	    for(int j=0;j<12;j++)
	    {
	    	if(j==11){fputc(Ptran(M[i][j]),fp);fputc('\n',fp);}
	    	else{fputc(Ptran(M[i][j]),fp);fputc(' ',fp);}
		}
	fputc('\n',fp);
}

void Print_G(int a,int c)
{
	printf("\n");
	int M[4][4];
	memset(M,0,sizeof(M));
	for(int i=0;i<4;i++)
    {
    	int x=G[a][c].X[i];
    	int y=G[a][c].Y[i];
    	M[y][x]=1;
	}
	for(int i=3;i>=0;i--)
	    for(int j=0;j<4;j++)
	    {
	    	if(j==3){printf("%d\n",M[i][j]);}
	    	else{printf("%d",M[i][j]);}
		}
	printf("\n");
}
预测函数部分
//Predict function
int predict_2(FILE* fp_output,char a,char b,int p)
{
	int A=Translate(a);
	int B=Translate(b);
	int c,x;//c->degree of rotation, x->the column of the graph's leftest 
	
	float max=-1000;
	for(int i=0;i<P[A];i++)
		for(int j=1;j<=11-W[A][i];j++)
		{
			Drop_2(Map,Map_1,A,i,j);
			float clear_count_price=0;
			clear_count_price+=line_clear_count_weights(Map_1);
		    if(Y_limit(Map_1))
		    {
		    	for(int k=0;k<P[B];k++)
		    	    for(int l=1;l<=11-W[B][k];l++)
		    	    {
		    	    	Drop_2(Map_1,Map_2,B,k,l);
		    	    	clear_count_price+=line_clear_count_weights(Map_2);
		    	        if(Y_limit(Map_2))
		    	        {
		    	            float price=main_factor_price(Map_2,clear_count_price);
		                    if(max<price){max=price;c=i;x=j;}
		                }
		            }
		    }
		}

	Drop_1(Map,A,c,x);
	
	int count=Line_clear_all(Map);//line_clear	
	
	if(!Y_limit(Map)){return 0;}
	
	Score_add(count);//Score change
	
	printf("%d %d %d\n%d\n",p-1,c,x,Score);
	//fflush(stdout);
	
	//fprintf(fp_output,"%d %c %d %d %d\n",p-1,a,c,x,Score);
	//Print_H_File(fp_output);
	//Print_M_File(Map,fp_output);
	
	return 1;
}

int predict_1(FILE* fp_output,char a,int p)
{
	int A=Translate(a);
	int c,x;//c->degree of rotation, x->the column of the graph's leftest 
	
	float max=-1000;
	for(int i=0;i<P[A];i++)
		for(int j=1;j<=11-W[A][i];j++)
		{
			Drop_2(Map,Map_1,A,i,j);
			float clear_count_price=0;
			clear_count_price+=line_clear_count_weights(Map_1);
		    if(Y_limit(Map_1))
		    {
		        float price=main_factor_price(Map_2,clear_count_price);
		        if(max<price){max=price;c=i;x=j;}
		    }
		}
	
	
	Drop_1(Map,A,c,x);
		
	int count=Line_clear_all(Map);//line_clear
	
	if(!Y_limit(Map)){return 0;}
	
	Score_add(count);//Score change
	
	printf("%d %d %d\n%d\n",p-1,c,x,Score);
	//fflush(stdout);
    
	//fprintf(fp_output,"%d %c %d %d %d\n",p-1,a,c,x,Score);
	//Print_H_File(fp_output);
	//Print_M_File(Map,fp_output);
	
	return 1;
}
测试数据生成函数
//data_creat
void data_create()
{
	FILE* fp_data;
	fp_data=fopen("test_data.txt","w");
	
    srand((unsigned)time(NULL));//set random function of rand()
	
	char data[data_L+5];
	int temp;
	
    for(int i=0;i<data_L;i++)
    {
	    temp=rand()%7;
        data[i]=translate(temp);
        fprintf(fp_data,"%c",translate(temp));
    }
    data[data_L]='X';
    fputc('X',fp_data);
    data[data_L+1]='\n';
    fputc('\n',fp_data);
    
    fclose(fp_data);
}
主函数部分
//main function
int main()
{
	initial_set();//set Map Graphs Score
	
	data_create();
	
	FILE* fp_data;
	fp_data=fopen("test_data.txt","r");
	
	char temp[data_L+5];
	char a[3];//graphs
	fscanf(fp_data,"%s",temp);
	
	fclose(fp_data);
		
	a[0]=temp[0];
	a[1]=temp[1];
	int p=2;
	
	FILE* fp_output;
	fp_output=fopen("test_output.txt","w");
	
	while(a[1]!='X')
	{
		if(!predict_2(fp_output,a[0],a[1],p)){break;}
		
		a[0]=a[1];
		a[1]=temp[p];
		p++;
	}
	if(a[1]=='X')
	{
		predict_1(fp_output,a[0],p);		
	}
	
	fclose(fp_output);
	
	return 0;
}
完整代码
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>
#include<time.h>
#define INF 0x3f3f3f3f
#define data_L 10000


//main data(changing)
int Map[21][12];//x1-10,y1-20,deadline->y16
int Map_1[21][12];//graph_a0 drop
int Map_2[21][12];//graph_a0 and graph_a1 drop
int Score;//score
int h[12];//height of columns

//factors' coefficient
float Weight[11];

//other data(fixed)
const int X[4]={-1,0,1,0};//direction x
const int Y[4]={0,1,0,-1};//direction y
const int P[7]={2,4,1,4,4,2,2};//the number of graphs' posture
struct graph
{
	int X[4];
	int Y[4];
}G[7][4];//graphs' coordinates, benchmark -> the lowwer left corner
int W[7][4];//width of graphs
int H[7][4];//height of graphs

//common invoking functions
int Max(int x,int y)
{
	return x>y?x:y;
}
int Min(int x,int y)
{
	return x<y?x:y;
}
void Map_copy(int M1[][12],int M2[][12])
{
	for(int i=0;i<21;i++)
	    for(int j=0;j<12;j++)
	        M2[i][j]=M1[i][j];
}
void H_messure(int M[][12])
{
	for(int i=0;i<=11;i++)
	    for(int j=20;j>=0;j--)
	        if(M[j][i]){h[i]=j;break;}
}
int Translate(char a)
{
	if(a=='I'){return 0;}
	if(a=='T'){return 1;}
	if(a=='O'){return 2;}
	if(a=='J'){return 3;}
	if(a=='L'){return 4;}
	if(a=='S'){return 5;}
	if(a=='Z'){return 6;}
}

char translate(int x)
{
	if(x==0){return 'I';}
	if(x==1){return 'T';}
	if(x==2){return 'O';}
	if(x==3){return 'J';}
	if(x==4){return 'L';}
	if(x==5){return 'S';}
	if(x==6){return 'Z';}
}

//setting initial data Map,Score,G,W,H and factors' coefficient
void initial_set()
{
	for(int i=0;i<=20;i++)
	    memset(Map[i],0,sizeof(int));
	for(int i=0;i<=20;i++)
        Map[i][0]=1;
    for(int i=0;i<=20;i++)
        Map[i][11]=1;
    for(int i=1;i<=10;i++)
        Map[0][i]=1;
	Score=0;
	//G[i][j],j=0->0,j=1->90,j=2->180,j=3->270,clockwise
    //coordinate benchmark->graphs' lower left corner
	//i=0->I
	memcpy(G[0][0].X,(int[4]){0,1,2,3},sizeof(int)*4);memcpy(G[0][0].Y,(int[4]){0,0,0,0},sizeof(int)*4);
	memcpy(G[0][1].X,(int[4]){0,0,0,0},sizeof(int)*4);memcpy(G[0][1].Y,(int[4]){0,1,2,3},sizeof(int)*4);
	W[0][0]=4;H[0][0]=1;
	W[0][1]=1;H[0][1]=4;
	//i=1->T
	memcpy(G[1][0].X,(int[4]){0,1,1,2},sizeof(int)*4);memcpy(G[1][0].Y,(int[4]){0,1,0,0},sizeof(int)*4);
	memcpy(G[1][1].X,(int[4]){0,0,0,1},sizeof(int)*4);memcpy(G[1][1].Y,(int[4]){0,1,2,1},sizeof(int)*4);
	memcpy(G[1][2].X,(int[4]){0,1,1,2},sizeof(int)*4);memcpy(G[1][2].Y,(int[4]){1,1,0,1},sizeof(int)*4);
	memcpy(G[1][3].X,(int[4]){0,1,1,1},sizeof(int)*4);memcpy(G[1][3].Y,(int[4]){1,2,1,0},sizeof(int)*4);
	W[1][0]=3;H[1][0]=2;
	W[1][1]=2;H[1][1]=3;
	W[1][2]=3;H[1][2]=2;
	W[1][3]=2;H[1][3]=3;
	//i=2->O
	memcpy(G[2][0].X,(int[4]){0,0,1,1},sizeof(int)*4);memcpy(G[2][0].Y,(int[4]){1,0,1,0},sizeof(int)*4);
	W[2][0]=2;H[2][0]=2;
	//i=3->J
	memcpy(G[3][0].X,(int[4]){0,0,1,2},sizeof(int)*4);memcpy(G[3][0].Y,(int[4]){1,0,0,0},sizeof(int)*4);
	memcpy(G[3][1].X,(int[4]){0,0,0,1},sizeof(int)*4);memcpy(G[3][1].Y,(int[4]){2,1,0,2},sizeof(int)*4);
	memcpy(G[3][2].X,(int[4]){0,1,2,2},sizeof(int)*4);memcpy(G[3][2].Y,(int[4]){1,1,1,0},sizeof(int)*4);
	memcpy(G[3][3].X,(int[4]){0,1,1,1},sizeof(int)*4);memcpy(G[3][3].Y,(int[4]){0,2,1,0},sizeof(int)*4);
	W[3][0]=3;H[3][0]=2;
	W[3][1]=2;H[3][1]=3;
	W[3][2]=3;H[3][2]=2;
	W[3][3]=2;H[3][3]=3;
	//i=4->L
	memcpy(G[4][0].X,(int[4]){0,1,2,2},sizeof(int)*4);memcpy(G[4][0].Y,(int[4]){0,0,1,0},sizeof(int)*4);
	memcpy(G[4][1].X,(int[4]){0,0,0,1},sizeof(int)*4);memcpy(G[4][1].Y,(int[4]){2,1,0,0},sizeof(int)*4);
	memcpy(G[4][2].X,(int[4]){0,0,1,2},sizeof(int)*4);memcpy(G[4][2].Y,(int[4]){1,0,1,1},sizeof(int)*4);
	memcpy(G[4][3].X,(int[4]){0,1,1,1},sizeof(int)*4);memcpy(G[4][3].Y,(int[4]){2,2,1,0},sizeof(int)*4);
	W[4][0]=3;H[4][0]=2;
	W[4][1]=2;H[4][1]=3;
	W[4][2]=3;H[4][2]=2;
	W[4][3]=2;H[4][3]=3;
	//i=5->S
	memcpy(G[5][0].X,(int[4]){0,1,1,2},sizeof(int)*4);memcpy(G[5][0].Y,(int[4]){0,1,0,1},sizeof(int)*4);
	memcpy(G[5][1].X,(int[4]){0,0,1,1},sizeof(int)*4);memcpy(G[5][1].Y,(int[4]){2,1,1,0},sizeof(int)*4);
	W[5][0]=3;H[5][0]=2;
	W[5][1]=2;H[5][1]=3;
	//i=6->Z
	memcpy(G[6][0].X,(int[4]){0,1,1,2},sizeof(int)*4);memcpy(G[6][0].Y,(int[4]){1,1,0,0},sizeof(int)*4);
	memcpy(G[6][1].X,(int[4]){0,0,1,1},sizeof(int)*4);memcpy(G[6][1].Y,(int[4]){1,0,2,1},sizeof(int)*4);
	W[6][0]=3;H[6][0]=2;
	W[6][1]=2;H[6][1]=3;
	
	//initialize factors' coefficient
	Weight[1]=-0.5;//h_difference_max, negative
	Weight[2]=-5.0;//hole_count, negative
	Weight[3]=-0.1;//columnwithhole_count, negative
	Weight[4]=-0.5;//bumpiness, neutral
	Weight[5]=-1.0;//row_transitions, negative
	Weight[6]=-1.0;//column_transitions, negative
	Weight[7]=-0.2;//pit_count, neutral
	Weight[8]=-0.2;//pit_deepest, neutral
	Weight[9]=-0.3;//bury_depth_sum, negative
	Weight[10]=5.0;//line_clear_count, positive
	
}

//tetromino rules
int Y_limit(int M[][12])
{
	for(int i=1;i<=10;i++)
	    if(M[16][i]){return 0;}
	return 1;
}

void Line_clear(int y,int M[][12])
{
	for(int i=y;i<20;i++)
	    for(int j=1;j<=10;j++)
	        M[i][j]=M[i+1][j];
}

int Line_clear_all(int M[][12])
{
	int sum;
	int count=0;
	for(int i=19;i>=1;i--)
	{
		sum=0;
	    for(int j=1;j<=10;j++)
	    	sum+=Map[i][j];
	    if(sum==10)
		{
		    count++;
		    Line_clear(i,Map);
		}
    }
    return count;
}

void Score_add(int count)
{
    if(count==1){Score+=100;}
    else if(count==2){Score+=300;}
    else if(count==3){Score+=500;}
    else if(count==4){Score+=800;}
}
void Drop_1(int M[][12],int A,int c,int x)//map drop
{
	H_messure(M);//messure h[i]
	
	int min=INF;//the min of y_distance between graph and all the 1 of map (x is 1 to 10)
	
	for(int i=0;i<4;i++)
	{
		int xg=x+G[A][c].X[i];
		int yg=16+G[A][c].Y[i];
	    min=Min(min,yg-h[xg]);
	}
	for(int i=0;i<4;i++)
    {
    	int xg=x+G[A][c].X[i];
    	int yg=16-min+G[A][c].Y[i]+1;
    	M[yg][xg]=1;
	}
}

int Drop_2(int M1[][12],int M2[][12],int A,int c,int x)
{
	H_messure(M1);//messure h[i]
	
	int min=INF;//the min of y_distance between graph and all the 1 of map (x is 1 to 10)
	
	for(int i=0;i<4;i++)
	{
		int xg=x+G[A][c].X[i];
		int yg=16+G[A][c].Y[i];
	    min=Min(min,yg-h[xg]);
	}
	Map_copy(M1,M2);
	for(int i=0;i<4;i++)
    {
    	int xg=x+G[A][c].X[i];
    	int yg=16-min+G[A][c].Y[i]+1;
    	M2[yg][xg]=1;
	}
	return Y_limit(M2);
}

//factors function
int h_difference_max(int M[][12])//factor1
{
	int max=0;
	for(int i=1;i<10;i++)
	    for(int j=i+1;j<=10;j++)
	        if(max<abs(h[i]-h[j])){max=abs(h[i]-h[j]);}
	return max;
}

int hole_count(int M[][12])//factor2
{
	int sum=0;
	for(int i=1;i<=10;i++)
	    for(int j=1;j<=h[i];j++)
	        if(!M[j][i]){sum++;}
	return sum;
}

int columnwithhole_count(int M[][12])//factor3
{
	int count=0;
	int sum=0;
	for(int i=1;i<=10;i++)
    {
    	sum=0;
    	for(int j=0;j<=h[i];j++)
    	{
    		sum+=M[j][i]^M[j+1][i];
		}
		if(sum!=1){count++;}
	}
	return count;
}

int bumpiness(int M[][12])//factor4
{
	int sum=0;
	for(int i=1;i<10;i++)
	    sum+=abs(h[i]-h[i+1]);
	return sum;
}

int row_transitions(int M[][12])//factor5
{
	int count=0;
	for(int i=0;i<11;i++)
	    for(int j=1;j<=16;j++)
	        count+=M[j][i]^M[j][i+1];
	return count;
}

int column_transitions(int M[][12])//factor6
{
	int count=0;
	for(int i=1;i<=10;i++)
	    for(int j=0;j<=16;j++)
	        count+=M[j][i]^M[j+1][i];
	return count;
}

int pit_count(int M[][12])//factor7
{
	int count=0;
	for(int i=2;i<10;i++)
	    if(!h[i]){count++;}
	return count;
}

int pit_deepest(int M[][12])//factor8
{
	int deepest=0;
	int deep;
	for(int i=1;i<=10;i++)
	{
		deep=Min(abs(h[i-1]-h[i]),abs(h[i]-h[i+1]));
	    if(deepest<deep){deepest=deep;}
	}
	return deepest;
}

int bury_depth_sum(int M[][12])//factor9
{
	int sum=0;
	for(int i=1;i<=10;i++)
	    for(int j=h[i];j>=1;j--)
	    	if(!M[j][i]){sum+=h[i]-j;}
	return sum;
}

int line_clear_count(int M[][12])//factor10
{
	int sum;
	int count=0;
	for(int i=16;i>=1;i--)
	{
		sum=0;
	    for(int j=1;j<=10;j++)
	    	sum+=M[i][j];
	    if(sum==10)
	    {
		    count++;
		    Line_clear(i,M);
		}
    }
    return count;
}

float line_clear_count_weights(int M[][12])
{
	float price=0;
	if(line_clear_count(M)==1)
	    price=0.1;
	else if(line_clear_count(M)==2)
	    price=0.3;
	else if(line_clear_count(M)==3)
	    price=0.5;
	else if(line_clear_count(M)==4)
	    price=0.8;
	return price;
}

float main_factor_price(int M[][12],float clear_count_price)
{
	float price=0;
	H_messure(M);//messure h[i]
	
    //factor1 h_difference_max -
	price+=Weight[1]*(1.0*h_difference_max(M));
	//factor2 hole_count -
	price+=Weight[2]*(1.0*hole_count(M));
	//factor3 columnwithhole_count -
	price+=Weight[3]*(1.0*columnwithhole_count(M));
	//factor4 bumpiness 
	price+=Weight[4]*(1.0*bumpiness(M));
	//factor5 row_transitions
	price+=Weight[5]*(1.0*row_transitions(M));
	//factor6 column_transitions
	price+=Weight[6]*(1.0*column_transitions(M));
	//factor7 pit_count
	price+=Weight[7]*(1.0*pit_count(M));
	//factor8 pit_deepest
	price+=Weight[8]*(1.0*pit_deepest(M));
	//factor9 bury_depth_sum
	price+=Weight[9]*(1.0*bury_depth_sum(M));
	//factor10 line_clear_count
	price+=Weight[10]*(1.0*clear_count_price);
	
	return price;
}

//visualize functions
char Ptran(int x)
{
	if(x){return '1';}
	else{return '0';}
}

void Print_H()
{
	printf("\n");
	
	for(int i=0;i<12;i++)
	{
	    if(i==11){printf("%d\n",h[i]);}
	    else{printf("%d ",h[i]);}
	}
}

void Print_H_File(FILE* fp)
{
	for(int i=0;i<12;i++)
    {
    	if(i==11){fprintf(fp,"%d\n",h[11]);}
    	else{fprintf(fp,"%d ",h[i]);}
	}
}

void Print_M(int M[][12])
{
	for(int i=20;i>=0;i--)
	    for(int j=0;j<12;j++)
	    {
	    	if(j==11){printf("%d\n",M[i][j]);}
	    	else{printf("%d ",M[i][j]);}
		}
	printf("\n");
}

void Print_M_File(int M[][12],FILE* fp)
{
	fputc('\n',fp);
	for(int i=20;i>=0;i--)
	    for(int j=0;j<12;j++)
	    {
	    	if(j==11){fputc(Ptran(M[i][j]),fp);fputc('\n',fp);}
	    	else{fputc(Ptran(M[i][j]),fp);fputc(' ',fp);}
		}
	fputc('\n',fp);
}

void Print_G(int a,int c)
{
	printf("\n");
	int M[4][4];
	memset(M,0,sizeof(M));
	for(int i=0;i<4;i++)
    {
    	int x=G[a][c].X[i];
    	int y=G[a][c].Y[i];
    	M[y][x]=1;
	}
	for(int i=3;i>=0;i--)
	    for(int j=0;j<4;j++)
	    {
	    	if(j==3){printf("%d\n",M[i][j]);}
	    	else{printf("%d",M[i][j]);}
		}
	printf("\n");
}

//Predict function
int predict_2(FILE* fp_output,char a,char b,int p)
{
	int A=Translate(a);
	int B=Translate(b);
	int c,x;//c->degree of rotation, x->the column of the graph's leftest 
	
	float max=-1000;
	for(int i=0;i<P[A];i++)
		for(int j=1;j<=11-W[A][i];j++)
		{
			Drop_2(Map,Map_1,A,i,j);
			float clear_count_price=0;
			clear_count_price+=line_clear_count_weights(Map_1);
		    if(Y_limit(Map_1))
		    {
		    	for(int k=0;k<P[B];k++)
		    	    for(int l=1;l<=11-W[B][k];l++)
		    	    {
		    	    	Drop_2(Map_1,Map_2,B,k,l);
		    	    	clear_count_price+=line_clear_count_weights(Map_2);
		    	        if(Y_limit(Map_2))
		    	        {
		    	            float price=main_factor_price(Map_2,clear_count_price);
		                    if(max<price){max=price;c=i;x=j;}
		                }
		            }
		    }
		}

	Drop_1(Map,A,c,x);
	
	int count=Line_clear_all(Map);//line_clear	
	
	if(!Y_limit(Map)){return 0;}
	
	Score_add(count);//Score change
	
	printf("%d %d %d\n%d\n",p-1,c,x,Score);
	//fflush(stdout);
	
	//fprintf(fp_output,"%d %c %d %d %d\n",p-1,a,c,x,Score);
	//Print_H_File(fp_output);
	//Print_M_File(Map,fp_output);
	
	return 1;
}

int predict_1(FILE* fp_output,char a,int p)
{
	int A=Translate(a);
	int c,x;//c->degree of rotation, x->the column of the graph's leftest 
	
	float max=-1000;
	for(int i=0;i<P[A];i++)
		for(int j=1;j<=11-W[A][i];j++)
		{
			Drop_2(Map,Map_1,A,i,j);
			float clear_count_price=0;
			clear_count_price+=line_clear_count_weights(Map_1);
		    if(Y_limit(Map_1))
		    {
		        float price=main_factor_price(Map_2,clear_count_price);
		        if(max<price){max=price;c=i;x=j;}
		    }
		}
	
	
	Drop_1(Map,A,c,x);
		
	int count=Line_clear_all(Map);//line_clear
	
	if(!Y_limit(Map)){return 0;}
	
	Score_add(count);//Score change
	
	printf("%d %d %d\n%d\n",p-1,c,x,Score);
	//fflush(stdout);
    
	//fprintf(fp_output,"%d %c %d %d %d\n",p-1,a,c,x,Score);
	//Print_H_File(fp_output);
	//Print_M_File(Map,fp_output);
	
	return 1;
}

//data_creat
void data_create()
{
	FILE* fp_data;
	fp_data=fopen("test_data.txt","w");
	
    srand((unsigned)time(NULL));//set random function of rand()
	
	char data[data_L+5];
	int temp;
	
    for(int i=0;i<data_L;i++)
    {
	    temp=rand()%7;
        data[i]=translate(temp);
        fprintf(fp_data,"%c",translate(temp));
    }
    data[data_L]='X';
    fputc('X',fp_data);
    data[data_L+1]='\n';
    fputc('\n',fp_data);
    
    fclose(fp_data);
}

//main function
int main()
{
	initial_set();//set Map Graphs Score
	
	data_create();
	
	FILE* fp_data;
	fp_data=fopen("test_data.txt","r");
	
	char temp[data_L+5];
	char a[3];//graphs
	fscanf(fp_data,"%s",temp);
	
	fclose(fp_data);
		
	a[0]=temp[0];
	a[1]=temp[1];
	int p=2;
	
	FILE* fp_output;
	fp_output=fopen("test_output.txt","w");
	
	while(a[1]!='X')
	{
		if(!predict_2(fp_output,a[0],a[1],p)){break;}
		
		a[0]=a[1];
		a[1]=temp[p];
		p++;
	}
	if(a[1]=='X')
	{
		predict_1(fp_output,a[0],p);		
	}
	
	fclose(fp_output);
	
	return 0;
}

(ps:这个程序下载下去后可以自己在电脑上跑着玩)

参数训练

  • 然后就是比较累电脑CPU的环节了,笔者和同学商量了一下,感觉遗传算法比较简单,就写了一个以遗传算法为基础的多进程训练器,以及相对应的调参用程序(当然屏幕前的你们也可以选择其他的算法,如模拟退火算法...)(其实笔者当时是个纯新手小白,不会GPU加速,(主要是当时没弄好NVIDIA的CUDA,献丑了...)所以才搞的多进程...)  。
  • 简单介绍一下遗传算法:遗传算法顾名思义,就是类似于遗传的一个迭代算法,其核心的思路是①先生成一些随机种子并进行多组10万块测试(可以自己设,最好比要求的块数多),②选出最好的若干个种子并剔除其余种子,③对这些留下的种子的内部参数进行随机组合,④对当前的所有种子进行多组10万块测试,⑤重复②③④直至选出一个最佳最稳定的种子。
  • 引用链接文章里非常精辟的一句话:遗传算法并不保证你能获得问题的最优解,但是使用遗传算法的最大优点在于你不必去了解和操心如何去“找”最优解。(你不必去指导袋鼠向那边跳,跳多远。)而只要简单的“否定”一些表现不好的个体就行了。(把那些总是爱走下坡路的袋鼠射杀,这就是遗传算法的精粹!)

链接:https://blog.csdn.net/zyx_bx/article/details/115188782https://blog.csdn.net/zyx_bx/article/details/115188782

完整代码
调参用主程序
  • 注:根据上面的主程序进行修改得到的用于测试的主程序。
    修改部分:主函数里添加了传参接口
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>
#include<time.h>
#define INF 0x3f3f3f3f

//main data(changing)
int Map[21][12];//x1-10,y1-20,deadline->y16
int Map_1[21][12];//graph_a0 drop
int Map_2[21][12];//graph_a0 and graph_a1 drop
int Score;//score
int h[12];//height of columns

//global data
int data_L;
int serial_number;

//factors' coefficient
double Weight[11];

//other data(fixed)
const int X[4]={-1,0,1,0};//direction x
const int Y[4]={0,1,0,-1};//direction y
const int P[7]={2,4,1,4,4,2,2};//the number of graphs' posture
struct graph
{
	int X[4];
	int Y[4];
}G[7][4];//graphs' coordinates, benchmark -> the lowwer left corner
int W[7][4];//width of graphs
int H[7][4];//height of graphs

//common invoking functions
int Max(int x,int y)
{
	return x>y?x:y;
}
int Min(int x,int y)
{
	return x<y?x:y;
}
void Map_copy(int M1[][12],int M2[][12])
{
	for(int i=0;i<21;i++)
	    for(int j=0;j<12;j++)
	        M2[i][j]=M1[i][j];
}
void H_messure(int M[][12])
{
	for(int i=0;i<=11;i++)
	    for(int j=20;j>=0;j--)
	        if(M[j][i]){h[i]=j;break;}
}
int Translate(char a)
{
	if(a=='I'){return 0;}
	if(a=='T'){return 1;}
	if(a=='O'){return 2;}
	if(a=='J'){return 3;}
	if(a=='L'){return 4;}
	if(a=='S'){return 5;}
	if(a=='Z'){return 6;}
}

char translate(int x)
{
	if(x==0){return 'I';}
	if(x==1){return 'T';}
	if(x==2){return 'O';}
	if(x==3){return 'J';}
	if(x==4){return 'L';}
	if(x==5){return 'S';}
	if(x==6){return 'Z';}
}

//setting initial data Map,Score,G,W,H and factors' coefficient
void initial_set(int argc,char *argv[])
{
	serial_number=atoi(argv[1]);
	data_L=atoi(argv[2]);
	
	for(int i=0;i<=20;i++)
	    memset(Map[i],0,sizeof(int));
	for(int i=0;i<=20;i++)
        Map[i][0]=1;
    for(int i=0;i<=20;i++)
        Map[i][11]=1;
    for(int i=1;i<=10;i++)
        Map[0][i]=1;
	Score=0;
	//G[i][j],j=0->0,j=1->90,j=2->180,j=3->270,clockwise
    //coordinate benchmark->graphs' lower left corner
	//i=0->I
	memcpy(G[0][0].X,(int[4]){0,1,2,3},sizeof(int)*4);memcpy(G[0][0].Y,(int[4]){0,0,0,0},sizeof(int)*4);
	memcpy(G[0][1].X,(int[4]){0,0,0,0},sizeof(int)*4);memcpy(G[0][1].Y,(int[4]){0,1,2,3},sizeof(int)*4);
	W[0][0]=4;H[0][0]=1;
	W[0][1]=1;H[0][1]=4;
	//i=1->T
	memcpy(G[1][0].X,(int[4]){0,1,1,2},sizeof(int)*4);memcpy(G[1][0].Y,(int[4]){0,1,0,0},sizeof(int)*4);
	memcpy(G[1][1].X,(int[4]){0,0,0,1},sizeof(int)*4);memcpy(G[1][1].Y,(int[4]){0,1,2,1},sizeof(int)*4);
	memcpy(G[1][2].X,(int[4]){0,1,1,2},sizeof(int)*4);memcpy(G[1][2].Y,(int[4]){1,1,0,1},sizeof(int)*4);
	memcpy(G[1][3].X,(int[4]){0,1,1,1},sizeof(int)*4);memcpy(G[1][3].Y,(int[4]){1,2,1,0},sizeof(int)*4);
	W[1][0]=3;H[1][0]=2;
	W[1][1]=2;H[1][1]=3;
	W[1][2]=3;H[1][2]=2;
	W[1][3]=2;H[1][3]=3;
	//i=2->O
	memcpy(G[2][0].X,(int[4]){0,0,1,1},sizeof(int)*4);memcpy(G[2][0].Y,(int[4]){1,0,1,0},sizeof(int)*4);
	W[2][0]=2;H[2][0]=2;
	//i=3->J
	memcpy(G[3][0].X,(int[4]){0,0,1,2},sizeof(int)*4);memcpy(G[3][0].Y,(int[4]){1,0,0,0},sizeof(int)*4);
	memcpy(G[3][1].X,(int[4]){0,0,0,1},sizeof(int)*4);memcpy(G[3][1].Y,(int[4]){2,1,0,2},sizeof(int)*4);
	memcpy(G[3][2].X,(int[4]){0,1,2,2},sizeof(int)*4);memcpy(G[3][2].Y,(int[4]){1,1,1,0},sizeof(int)*4);
	memcpy(G[3][3].X,(int[4]){0,1,1,1},sizeof(int)*4);memcpy(G[3][3].Y,(int[4]){0,2,1,0},sizeof(int)*4);
	W[3][0]=3;H[3][0]=2;
	W[3][1]=2;H[3][1]=3;
	W[3][2]=3;H[3][2]=2;
	W[3][3]=2;H[3][3]=3;
	//i=4->L
	memcpy(G[4][0].X,(int[4]){0,1,2,2},sizeof(int)*4);memcpy(G[4][0].Y,(int[4]){0,0,1,0},sizeof(int)*4);
	memcpy(G[4][1].X,(int[4]){0,0,0,1},sizeof(int)*4);memcpy(G[4][1].Y,(int[4]){2,1,0,0},sizeof(int)*4);
	memcpy(G[4][2].X,(int[4]){0,0,1,2},sizeof(int)*4);memcpy(G[4][2].Y,(int[4]){1,0,1,1},sizeof(int)*4);
	memcpy(G[4][3].X,(int[4]){0,1,1,1},sizeof(int)*4);memcpy(G[4][3].Y,(int[4]){2,2,1,0},sizeof(int)*4);
	W[4][0]=3;H[4][0]=2;
	W[4][1]=2;H[4][1]=3;
	W[4][2]=3;H[4][2]=2;
	W[4][3]=2;H[4][3]=3;
	//i=5->S
	memcpy(G[5][0].X,(int[4]){0,1,1,2},sizeof(int)*4);memcpy(G[5][0].Y,(int[4]){0,1,0,1},sizeof(int)*4);
	memcpy(G[5][1].X,(int[4]){0,0,1,1},sizeof(int)*4);memcpy(G[5][1].Y,(int[4]){2,1,1,0},sizeof(int)*4);
	W[5][0]=3;H[5][0]=2;
	W[5][1]=2;H[5][1]=3;
	//i=6->Z
	memcpy(G[6][0].X,(int[4]){0,1,1,2},sizeof(int)*4);memcpy(G[6][0].Y,(int[4]){1,1,0,0},sizeof(int)*4);
	memcpy(G[6][1].X,(int[4]){0,0,1,1},sizeof(int)*4);memcpy(G[6][1].Y,(int[4]){1,0,2,1},sizeof(int)*4);
	W[6][0]=3;H[6][0]=2;
	W[6][1]=2;H[6][1]=3;
	
	//initialize factors' coefficient
	/*Weight[1]=-0.5;//h_difference_max, negative
	Weight[2]=-5.0;//hole_count, negative
	Weight[3]=-0.1;//columnwithhole_count, negative
	Weight[4]=-1.0;//bumpiness, neutral
	Weight[5]=-1.0;//row_transitions, negative
	Weight[6]=-1.0;//column_transitions, negative
	Weight[7]=-0.2;//pit_count, neutral
	Weight[8]=-0.2;//pit_deepest, neutral
	Weight[9]=-0.1;//bury_depth_sum, negative
	Weight[10]=5.0;//line_clear_count, positive*/
	
	for(int i=1;i<=10;i++)
	    Weight[i]=atof(argv[i+2]);
}

//tetromino rules
int Y_limit(int M[][12])
{
	for(int i=1;i<=10;i++)
	    if(M[16][i]){return 0;}
	return 1;
}

void Line_clear(int y,int M[][12])
{
	for(int i=y;i<20;i++)
	    for(int j=1;j<=10;j++)
	        M[i][j]=M[i+1][j];
}

int Line_clear_all(int M[][12])
{
	int sum;
	int count=0;
	for(int i=19;i>=1;i--)
	{
		sum=0;
	    for(int j=1;j<=10;j++)
	    	sum+=Map[i][j];
	    if(sum==10)
		{
		    count++;
		    Line_clear(i,Map);
		}
    }
    return count;
}

void Score_add(int count)
{
    if(count==1){Score+=100;}
    else if(count==2){Score+=300;}
    else if(count==3){Score+=500;}
    else if(count==4){Score+=800;}
}
void Drop_1(int M[][12],int A,int c,int x)//map drop
{
	H_messure(M);//messure h[i]
	
	int min=INF;//the min of y_distance between graph and all the 1 of map (x is 1 to 10)
	
	for(int i=0;i<4;i++)
	{
		int xg=x+G[A][c].X[i];
		int yg=16+G[A][c].Y[i];
	    min=Min(min,yg-h[xg]);
	}
	for(int i=0;i<4;i++)
    {
    	int xg=x+G[A][c].X[i];
    	int yg=16-min+G[A][c].Y[i]+1;
    	M[yg][xg]=1;
	}
}

int Drop_2(int M1[][12],int M2[][12],int A,int c,int x)
{
	H_messure(M1);//messure h[i]
	
	int min=INF;//the min of y_distance between graph and all the 1 of map (x is 1 to 10)
	
	for(int i=0;i<4;i++)
	{
		int xg=x+G[A][c].X[i];
		int yg=16+G[A][c].Y[i];
	    min=Min(min,yg-h[xg]);
	}
	Map_copy(M1,M2);
	for(int i=0;i<4;i++)
    {
    	int xg=x+G[A][c].X[i];
    	int yg=16-min+G[A][c].Y[i]+1;
    	M2[yg][xg]=1;
	}
	return Y_limit(M2);
}

//factors function
int h_difference_max(int M[][12])//factor1
{
	int result=0;
	int max=0;
	int min=INF;
	
	for(int i=1;i<10;i++)
	{
		if(h[i]>max){max=h[i];}
		if(h[i]<min){min=h[i];}
	}
	result=max-min;
	return result;
}

int hole_count(int M[][12])//factor2
{
	int sum=0;
	for(int i=1;i<=10;i++)
	    for(int j=1;j<=h[i];j++)
	        if(!M[j][i]){sum++;}
	return sum;
}

int columnwithhole_count(int M[][12])//factor3
{
	int count=0;
	int sum=0;
	for(int i=1;i<=10;i++)
    {
    	sum=0;
    	for(int j=0;j<=h[i];j++)
    	{
    		sum+=M[j][i]^M[j+1][i];
		}
		if(sum!=1){count++;}
	}
	return count;
}

int bumpiness(int M[][12])//factor4
{
	int sum=0;
	for(int i=1;i<10;i++)
	    sum+=abs(h[i]-h[i+1]);
	return sum;
}

int row_transitions(int M[][12])//factor5
{
	int count=0;
	for(int i=0;i<11;i++)
	    for(int j=1;j<=16;j++)
	        count+=M[j][i]^M[j][i+1];
	return count;
}

int column_transitions(int M[][12])//factor6
{
	int count=0;
	for(int i=1;i<=10;i++)
	    for(int j=0;j<=16;j++)
	        count+=M[j][i]^M[j+1][i];
	return count;
}

int pit_count(int M[][12])//factor7
{
	int count=0;
	for(int i=2;i<10;i++)
	    if(!h[i]){count++;}
	return count;
}

int pit_deepest(int M[][12])//factor8
{
	int deepest=0;
	int deep;
	for(int i=1;i<=10;i++)
	{
		deep=Min(abs(h[i-1]-h[i]),abs(h[i]-h[i+1]));
	    if(deepest<deep){deepest=deep;}
	}
	return deepest;
}

int bury_depth_sum(int M[][12])//factor9
{
	int sum=0;
	for(int i=1;i<=10;i++)
	    for(int j=h[i];j>=1;j--)
	    	if(!M[j][i]){sum+=h[i]-j;}
	return sum;
}

int line_clear_count(int M[][12])//factor10
{
	int sum;
	int count=0;
	for(int i=16;i>=1;i--)
	{
		sum=0;
	    for(int j=1;j<=10;j++)
	    	sum+=M[i][j];
	    if(sum==10)
	    {
		    count++;
		    Line_clear(i,M);
		}
    }
    return count;
}

double line_clear_count_weights(int M[][12])
{
	double price=0;
	int count=line_clear_count(M);
	if(count==1)
	    price=0.1;
	else if(count==2)
	    price=0.3;
	else if(count==3)
	    price=0.5;
	else if(count==4)
	    price=0.8;
	return price;
}

double main_factor_price(int M[][12],double clear_count_price)
{
	double price=0;
	H_messure(M);//messure h[i]
	
    //factor1 h_difference_max -
	price+=Weight[1]*(1.0*h_difference_max(M));
	//factor2 hole_count -
	price+=Weight[2]*(1.0*hole_count(M));
	//factor3 columnwithhole_count -
	price+=Weight[3]*(1.0*columnwithhole_count(M));
	//factor4 bumpiness 
	price+=Weight[4]*(1.0*bumpiness(M));
	//factor5 row_transitions
	price+=Weight[5]*(1.0*row_transitions(M));
	//factor6 column_transitions
	price+=Weight[6]*(1.0*column_transitions(M));
	//factor7 pit_count
	price+=Weight[7]*(1.0*pit_count(M));
	//factor8 pit_deepest
	price+=Weight[8]*(1.0*pit_deepest(M));
	//factor9 bury_depth_sum
	price+=Weight[9]*(1.0*bury_depth_sum(M));
	//factor10 line_clear_count
	price+=Weight[10]*(1.0*clear_count_price);
	
	return price;
}

//visualize functions
char Ptran(int x)
{
	if(x){return '1';}
	else{return '0';}
}

void Print_H()
{
	printf("\n");
	
	for(int i=0;i<12;i++)
	{
	    if(i==11){printf("%d\n",h[i]);}
	    else{printf("%d ",h[i]);}
	}
}

void Print_H_File(FILE* fp)
{
	for(int i=0;i<12;i++)
    {
    	if(i==11){fprintf(fp,"%d\n",h[11]);}
    	else{fprintf(fp,"%d ",h[i]);}
	}
}

void Print_M(int M[][12])
{
	for(int i=20;i>=0;i--)
	    for(int j=0;j<12;j++)
	    {
	    	if(j==11){printf("%d\n",M[i][j]);}
	    	else{printf("%d ",M[i][j]);}
		}
	printf("\n");
}

void Print_M_File(int M[][12],FILE* fp)
{
	fputc('\n',fp);
	for(int i=20;i>=0;i--)
	    for(int j=0;j<12;j++)
	    {
	    	if(j==11){fputc(Ptran(M[i][j]),fp);fputc('\n',fp);}
	    	else{fputc(Ptran(M[i][j]),fp);fputc(' ',fp);}
		}
	fputc('\n',fp);
}

void Print_G(int a,int c)
{
	printf("\n");
	int M[4][4];
	memset(M,0,sizeof(M));
	for(int i=0;i<4;i++)
    {
    	int x=G[a][c].X[i];
    	int y=G[a][c].Y[i];
    	M[y][x]=1;
	}
	for(int i=3;i>=0;i--)
	    for(int j=0;j<4;j++)
	    {
	    	if(j==3){printf("%d\n",M[i][j]);}
	    	else{printf("%d",M[i][j]);}
		}
	printf("\n");
}

//Predict function
int predict_2(FILE* fp_output,char a,char b,int p)
{
	int A=Translate(a);
	int B=Translate(b);
	int c,x;//c->degree of rotation, x->the column of the graph's leftest 
	
	double max=-1000000;
	for(int i=0;i<P[A];i++)
		for(int j=1;j<=11-W[A][i];j++)
		{
			Drop_2(Map,Map_1,A,i,j);
			double clear_count_price=0;
			clear_count_price+=line_clear_count_weights(Map_1);
		    if(Y_limit(Map_1))
		    {
		    	for(int k=0;k<P[B];k++)
		    	    for(int l=1;l<=11-W[B][k];l++)
		    	    {
		    	    	Drop_2(Map_1,Map_2,B,k,l);
		    	    	clear_count_price+=line_clear_count_weights(Map_2);
		    	        if(Y_limit(Map_2))
		    	        {
		    	            double price=main_factor_price(Map_2,clear_count_price);
		                    if(max<price){max=price;c=i;x=j;}
		                }
		            }
		    }
		}

	Drop_1(Map,A,c,x);
	
	int count=Line_clear_all(Map);//line_clear	
	
	if(!Y_limit(Map)){return 0;}
	
	Score_add(count);//Score change
	
	//printf("%d %d %d\n%d\n",p-1,c,x,Score);
	//fflush(stdout);
	
	fprintf(fp_output,"%d %c %d %d %d\n",p-1,a,c,x,Score);
	//Print_H_File(fp_output);
	//Print_M_File(Map,fp_output);
	
	return 1;
}

int predict_1(FILE* fp_output,char a,int p)
{
	int A=Translate(a);
	int c,x;//c->degree of rotation, x->the column of the graph's leftest 
	
	double max=-1000000;
	for(int i=0;i<P[A];i++)
		for(int j=1;j<=11-W[A][i];j++)
		{
			Drop_2(Map,Map_1,A,i,j);
			double clear_count_price=0;
			clear_count_price+=line_clear_count_weights(Map_1);
		    if(Y_limit(Map_1))
		    {
		        double price=main_factor_price(Map_1,clear_count_price);
		        if(max<price){max=price;c=i;x=j;}
		    }
		}
	
	
	Drop_1(Map,A,c,x);
		
	int count=Line_clear_all(Map);//line_clear
	
	if(!Y_limit(Map)){return 0;}
	
	Score_add(count);//Score change
	
	//printf("%d %d %d\n%d\n",p-1,c,x,Score);
	//fflush(stdout);
    
	fprintf(fp_output,"%d %c %d %d %d\n",p-1,a,c,x,Score);
	//Print_H_File(fp_output);
	//Print_M_File(Map,fp_output);
	
	return 1;
}

//data_creat
void data_create(int argc,char* argv[],char str[])
{
    srand((unsigned)(time(NULL)));//set random function of rand()
	
	int temp;
	
    for(int i=0;i<data_L;i++)
    {
	    temp=rand()%7;
        str[i]=translate(temp);
    }
    str[data_L]='X';
    str[data_L+1]='\n';
}

//main function
int main(int argc,char *argv[])
{
	initial_set(argc,argv);//set Map Graphs Score
	
	char temp[data_L+5];
	char a[3];//graphs
	data_create(argc,argv,temp);
		
	a[0]=temp[0];
	a[1]=temp[1];
	int p=2;
	
	char filename_test_output[256];
	sprintf(filename_test_output,"0_test_output_%s.txt",argv[1]);
	FILE* fp_test_output;
	//fp_output=fopen("test_output.txt","w");
	fp_test_output=fopen(filename_test_output,"w");
	
	clock_t start_t,finish_t;
	start_t=clock();
	
	while(a[1]!='X')
	{
		if(!predict_1(fp_test_output,a[0],p)){break;}
		
		a[0]=a[1];
		a[1]=temp[p];
		p++;
	}
	if(a[1]=='X')
	{
		predict_1(fp_test_output,a[0],p);		
	}
	
	finish_t=clock();
	
	double time_sum=(double)(finish_t-start_t)/CLOCKS_PER_SEC;
	
	//char filename_result[256];
	//sprintf(filename_result,"0_result_%d.txt",serial_number);
	//FILE* fp_result=fopen(filename_result,"w");
	
	char filename_output[256];
	sprintf(filename_output,"0_output_%d.txt",serial_number);
	FILE* fp_output=fopen(filename_output,"w");
	
	fprintf(fp_output,"%d %d",serial_number,data_L);
	for(int i=1;i<=10;i++)
	    fprintf(fp_output," %lf",Weight[i]);
	fprintf(fp_output," %d %d %lf",p-1,Score,(1.0*Score)/(1.0*p-1));
	
	/*for(int i=1;i<=10;i++)
	    fprintf(fp_result,"Weight%d: %f\n",i,Weight[i]);
	fprintf(fp_result,"brick sum: %d\n",data_L);
	fprintf(fp_result,"brick clear: %d\n",p-1);
	fprintf(fp_result,"Score: %d\n",Score);
	fprintf(fp_result,"Score/brick: %f\n",(1.0*Score)/(1.0*p-1));
	fprintf(fp_result,"time: %f\n",time_sum);
	fprintf(fp_result,"brick/time: %f\n",(1.0*(p-1))/time_sum);*/
	
	fclose(fp_test_output);
	//fclose(fp_result);
	fclose(fp_output);
	
	return 0;
}
并行训练程序
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>

#define NUM_CHILDREN 10

typedef struct {
    int serial_number;
    int data_L;
    double weight[10];
    int brick;
    int score;
    double density;
} Seed;

int main(int argc, char* argv[]) {
    if (argc != 3) {
        printf("Usage: %s L N\n", argv[0]);
        return 1;
    }

    const int L=atoi(argv[1]);
    const int N=atoi(argv[2]);

    // 读取种子文件
    FILE* fp_seeds=fopen("seeds.txt","r");
    if (!fp_seeds){
        printf("Failed to open seeds.txt\n");
        return 1;
    }

    Seed* S=(Seed*)malloc(N*sizeof(Seed));
    char*** childrenArgs_all=(char***)malloc(N*sizeof(char**));
    for (int i=0;i<N;i++){
        childrenArgs_all[i]=(char**)malloc(L*sizeof(char*));
        for (int j=0;j<L;j++){
            childrenArgs_all[i][j]=(char*)malloc(256*sizeof(char));
        }

        fscanf(fp_seeds,"%d%d",&S[i].serial_number,&S[i].data_L);
        for (int j=0;j<10;j++){
            fscanf(fp_seeds,"%lf",&S[i].weight[j]);
        }

        // 填充命令行参数
        sprintf(childrenArgs_all[i][0],"%d",S[i].serial_number);
        sprintf(childrenArgs_all[i][1],"%d",S[i].data_L);
        for (int j=0;j<10;j++){
            sprintf(childrenArgs_all[i][j+2],"%.15lf",S[i].weight[j]);
        }
    }
    fclose(fp_seeds);

    const char* childExe="tetromino_genetic_algorithm_1.exe";
    HANDLE hProcesses[NUM_CHILDREN]={NULL};
    int current_task = 0;
    int active_count = 0;

    // 初始启动进程
    for(int i=0;i<NUM_CHILDREN&&current_task<N;i++){
        char commandLine[4096]={0};
        int pos=0;
        pos+=snprintf(commandLine+pos,sizeof(commandLine)-pos,"\"%s\"",childExe);
        for(int k=0;k<L;k++){
            pos+=snprintf(commandLine+pos,sizeof(commandLine)-pos," \"%s\"",childrenArgs_all[current_task][k]);
        }

        STARTUPINFOA si={0};
        PROCESS_INFORMATION pi={0};
        si.cb=sizeof(si);
        if(CreateProcessA(NULL,commandLine,NULL,NULL,FALSE,0,NULL,NULL,&si,&pi)){
            hProcesses[active_count]=pi.hProcess;
            CloseHandle(pi.hThread);
            printf("[parent] Created child %d (PID: %d)\n   Command line: %s\n",current_task+1,GetProcessId(pi.hProcess),commandLine);
            current_task++;
            active_count++;
        }
		else{
            printf("Failed to create process for task %d. Error: 0x%08X\n", current_task, GetLastError());
        }
    }

    // 主循环管理进程
    while(active_count>0){
        DWORD waitResult=WaitForMultipleObjects(active_count,hProcesses,FALSE,INFINITE);
        if(waitResult>=WAIT_OBJECT_0&&waitResult<WAIT_OBJECT_0+active_count){
            int idx=waitResult-WAIT_OBJECT_0;
            DWORD exitCode;
            GetExitCodeProcess(hProcesses[idx],&exitCode);
            CloseHandle(hProcesses[idx]);
            printf("Child process (task %d) exited with code %d\n",current_task-active_count+idx+1,exitCode);

            // 替换或移除完成的进程
            if(current_task<N){
                char commandLine[4096]={0};
                int pos=0;
                pos+=snprintf(commandLine+pos,sizeof(commandLine)-pos,"\"%s\"",childExe);
                for (int k=0;k<L;k++) {
                    pos+=snprintf(commandLine+pos,sizeof(commandLine)-pos," \"%s\"",childrenArgs_all[current_task][k]);
                }

                STARTUPINFOA si={0};
                PROCESS_INFORMATION pi={0};
                si.cb = sizeof(si);
                if(CreateProcessA(NULL,commandLine,NULL,NULL,FALSE,0,NULL,NULL,&si,&pi)){
                    hProcesses[idx]=pi.hProcess;
                    CloseHandle(pi.hThread);
                    printf("[parent] Created child %d (PID: %d)\n   Command line: %s\n", 
                           current_task+1,GetProcessId(pi.hProcess),commandLine);
                    current_task++;
                }
				else{
                    printf("Failed to create process for task %d. Error: 0x%08X\n",current_task,GetLastError());
                    hProcesses[idx]=hProcesses[active_count-1];
                    active_count--;
                }
            }
			else{
                hProcesses[idx]=hProcesses[active_count-1];
                active_count--;
            }
        }
		else{
            printf("WaitForMultipleObjects failed. Error: 0x%08X\n",GetLastError());
            break;
        }
    }

    // 清理资源
    for (int i=0;i<N;i++){
        for (int j=0;j<L;j++){
            free(childrenArgs_all[i][j]);
        }
        free(childrenArgs_all[i]);
    }
    free(childrenArgs_all);
    free(S);

    return 0;
}
多组测试调用程序
#include<windows.h>
#include<stdio.h>
#include<stdlib.h>
#include<time.h>

const int Seed_L=12;//the lenth of a seed
const int Seed_N_s=50;//the number of seeds in seeds.txt and output_all.txt
const int Seed_N_p=15;//the number of seeds in processed.txt
const int Seed_N_m=35;//the number of mutation seeds
const int Data_L=1000000;//the lenth of data

typedef struct
{
	int serial_number;
	int data_L;
	double weight[10];
	int brick;
	int score;
	double density;
}Seed;

int cmp(const void *a,const void *b)
{
	return (*(Seed *)a).score>(*(Seed *)b).score?-1:1;
}

void Createseedsfile()
{
	FILE* fp_processed=fopen("processed.txt","r");
	FILE* fp_seeds=fopen("seeds.txt","w");
	Seed S[Seed_N_s];
	for(int i=0;i<Seed_N_p;i++)
	{
		fscanf(fp_processed,"%d%d",&S[i].serial_number,&S[i].data_L);
		for(int j=0;j<10;j++)
		    fscanf(fp_processed,"%lf",&S[i].weight[j]);
		fscanf(fp_processed,"%d%d%lf",&S[i].brick,&S[i].score,&S[i].density);
	}
	
	//create mutation seeds
	srand((unsigned)(time(NULL)));
    for(int i=Seed_N_p;i<Seed_N_s;i++)
    {
    	S[i].serial_number=i;
    	
    	int temp1;
    	temp1=rand()%10;
    	int temp2;
    	temp2=rand()%10;
    	for(int j=0;j<10;j++)
    	{
    		int temp3;
    		temp3=rand()%2;
    		if(temp3){S[i].weight[j]=S[temp1].weight[j];}
    		else{S[i].weight[j]=S[temp2].weight[j];}
    		
    		int temp4;
    		temp4=rand()%2;
    		int temp5;
    		temp5=((rand()*1007)%2001)-1000;
    		if(temp4==0){S[i].weight[j]+=0.01*temp5;}
		}
	}
	
	for(int i=0;i<Seed_N_s;i++)
	{
		fprintf(fp_seeds,"%d %d",i,Data_L);//serial_number,Data_L->seeds.txt
		
		for(int j=0;j<10;j++)//weight[0]-weight[9]->seeds.txt
	    	fprintf(fp_seeds," %lf",S[i].weight[j]);
		
		fputs("\n",fp_seeds);
	}

	fclose(fp_processed);
	fclose(fp_seeds);
}

void Collect_process_output()
{
	FILE* fp_output_all=fopen("output_all.txt","w");
	FILE* fp_processed=fopen("processed.txt","w");
	
	Seed Output[Seed_N_s];
	for(int i=0;i<Seed_N_s;i++)//read all the output file
	{
		char filename[256];
	    sprintf(filename,"0_output_%d.txt",i);
	    FILE* fp_output=fopen(filename,"r");//open the outputs
	    
	    //put outputs into Output[Seed_N_s]
	    fscanf(fp_output,"%d%d",&Output[i].serial_number,&Output[i].data_L);
		for(int j=0;j<10;j++)
		    fscanf(fp_output,"%lf",&Output[i].weight[j]);
		fscanf(fp_output,"%d%d%lf",&Output[i].brick,&Output[i].score,&Output[i].density);
		
		fclose(fp_output);
	}
	
	for(int i=0;i<Seed_N_s;i++)//Output[Seed_N_s]->output_all.txt
    {
    	fprintf(fp_output_all,"%d %d",Output[i].serial_number,Output[i].data_L);//serial_number,data_L->output_all.txt
		
		for(int j=0;j<10;j++)//weight[0]-weight[9]->output_all.txt
	    	fprintf(fp_output_all," %lf",Output[i].weight[j]);
				
		fprintf(fp_output_all," %d %d %lf\n",Output[i].brick,Output[i].score,Output[i].density);//brick,score,density->output_all.txt
}
	
    qsort(Output,Seed_N_s,sizeof(Seed),cmp);
	
	for(int i=0;i<Seed_N_p;i++)//Output[0]-Output[Seed_N_p]->processed.txt
	{
		fprintf(fp_processed,"%d %d",Output[i].serial_number,Output[i].data_L);//serial_number,data_L->processed.txt
		
    	for(int j=0;j<10;j++)//weight[0]-weight[9]->processed.txt
	    	fprintf(fp_processed," %lf",Output[i].weight[j]);
		
		fprintf(fp_processed," %d %d %lf\n",Output[i].brick,Output[i].score,Output[i].density);//brick,score,density->processed.txt
	}
	
	fclose(fp_output_all);
	fclose(fp_processed);
}

int main(){
    Createseedsfile();
		
    STARTUPINFOA si;
    PROCESS_INFORMATION pi;
    char commandLine[4096]={0};

    ZeroMemory(&si,sizeof(si));
    si.cb=sizeof(si);
    ZeroMemory(&pi,sizeof(pi));

    const char* childExe="tetromino_genetic_algorithm_concurrency_2_1.exe";
    char temp1[256];
    sprintf(temp1,"%d",Seed_L);
    char temp2[256];
    sprintf(temp2,"%d",Seed_N_s);
    const char* args[]={temp1,temp2};
    
    // 构建命令行(自动处理空格参数)
    sprintf_s(commandLine,sizeof(commandLine),"\"%s\"",childExe);

    for (int i=0;i<sizeof(args)/sizeof(args[0]);i++) {
        // 为每个参数添加双引号
        strcat_s(commandLine, sizeof(commandLine), " \"");
        strcat_s(commandLine, sizeof(commandLine), args[i]);
        strcat_s(commandLine, sizeof(commandLine), "\"");
    }

    printf("Final command line: %s\n", commandLine);

    // 创建进程
    BOOL success = CreateProcessA(
        NULL,           // 可执行文件路径已在命令行中
        commandLine,    // 可修改的命令行缓冲区
        NULL,           // 进程安全属性
        NULL,           // 线程安全属性
        FALSE,          // 不继承句柄
        0,              // 创建标志
        NULL,           // 环境变量
        NULL,           // 当前目录
        &si,
        &pi
    );

    if (!success) {
        DWORD error = GetLastError();
        char* errorMsg = NULL;
        FormatMessageA(
            FORMAT_MESSAGE_ALLOCATE_BUFFER | 
            FORMAT_MESSAGE_FROM_SYSTEM |
            FORMAT_MESSAGE_IGNORE_INSERTS,
            NULL,
            error,
            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
            (LPSTR)&errorMsg,
            0,
            NULL
        );
        fprintf(stderr, "CreateProcess failed (0x%08lX): %s\n", error, errorMsg);
        LocalFree(errorMsg);
        return 1;
    }

    // 等待子进程退出
    WaitForSingleObject(pi.hProcess, INFINITE);

    // 获取退出码
    DWORD exitCode;
    GetExitCodeProcess(pi.hProcess, &exitCode);
    printf("Child process exited with code: %lu\n", exitCode);

    // 清理句柄
    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);
    
    Collect_process_output();
    
    printf("Finish\n");

    return 0;
}

后言:因文章篇幅过长,所以内容会放到下一篇文章里。

未完待续...

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值