看实例玩编程(23):用程序妙解九宫图
软件世界
重温金庸的《射雕英雄传》,读至“第二十九回 黑沼隐女”时,号称神算子的瑛姑居然不会九宫图,甚感好笑。但九宫图是什么呢?记得小学的寒假生活上就有这么一道题:将一至九这九个数字排成三列,使之不论纵横斜角,每三个相加和都是十五,这就是九宫图。当然3的平方9个数是很容易填的,不过5的平方呢?7的平方呢?经过一番摸索,我终于用程序实现了n的平方(n为奇数)的表格自动填写,使之不论纵横斜角,数字之和必等。用它来解九宫图,自然不在话下。
界面设计
我使用的软件是Borland C++ Builder,程序界面如图1。输入一个奇数n并回车后将出现一个行列数为n+1的表格。点击“快速填数字”按钮,程序将迅速把1~n2填到相应表格中并做统计。点击“学习填数字”按钮,程序将每隔0.7秒依次填入一个数字,犹如放动画一般,直到填完为止。图1的n为3。
程序的主要步骤及代码
1.启动Borland C++ Builder,新建一个程序,并在窗体上依次添加一个Label 、一个Edit、5个Button、一个StringGrid、一个Image,一个Timer(见图1)。
说明:
[快速填数字]按钮的name:Button1
[学习填数字]按钮的name:Button3
StringGrid的name: grid
其余控件的name为默认
2.定义全局变量:
int num,num_temp;
int ti,tj,temp_ti,temp_tj,tcishu;
3.窗体初始化时“快速填数字”、“学习填数字”、Timer1均不可用,因此代码为:
void __fastcall TForm1::FormCreate(TObject *Sender)
{
Timer1->Enabled =false;
Button1->Enabled =false;
Button3->Enabled =false;
grid->Height =0; //让表格先不做显示
grid->Width =0;
}
4.双击Edit1,其OnChange事件代码为:
void __fastcall TForm1::Edit1Change(TObject *Sender)
{
if(Edit1->Text==“”)Edit1->Text=“3”;
//////////防止多次点击时数字变化
for(int i=1;i<=StrToInt(Edit1->Text)+1;i++)
for(int j=1;j<=StrToInt(Edit1->Text)+1;j++)
grid->Cells[i][j]=“”;//将相应表格置空。
}
5.当在Edit1内输入数字并回车后将触发什么事件呢?首先要判断输入的是否大于0,是否为偶数,确定为奇数后,把“快速填数字”、“学习填数字”设为可用,并根据输入的值做好表格,代码如下:
void __fastcall TForm1::Edit1KeyPress(TObject *Sender, char &Key)
{
if(Key==VK_RETURN)
{
num=Edit1->Text.ToIntDef(0);
num_temp=num%2;//判断是否为偶数
if(num<0)//输入数值小于0的处理
{
Timer1->Enabled =false;
Button1->Enabled =false;
Button3->Enabled =false;
ShowMessage(“必须是大于1的奇数”);
}
Else//输入数值为偶数的处理
{
if(num_temp==0)
{
Timer1->Enabled =false;
Button1->Enabled =false;
Button3->Enabled =false;
ShowMessage(“不能为偶数”);
}
Else
{
Button3->Enabled =true;
Button1->Enabled =true;
grid->Height =(num+2)*36+4;//表格的位置
grid->Width =(num+2)*36+4;
if(grid->Width >=662 )
{
grid->Width =640;
grid->Height =640;
grid->ScrollBars =ssBoth;
}
grid->ColCount=num+2;//表格的行列数
grid->RowCount=num+2;
for (int i=1;i<=num+1;i++) //做表头
{
grid->Cells[0][i]=IntToStr(i);
}
grid->Cells[0][num+1]=“合计”;
for (int i=1;i<=num+1;i++) //做表头
{
grid->Cells[i][0]=IntToStr(i);
}
grid->Cells[num+1][0]=“合计”;
}
}
}
}
填写九宫图核心代码
1.“快速填数字”按钮的代码如下:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Timer1->Enabled =false;
//防止多次点击时数字变化
for(int i=1;i<=StrToInt(Edit1->Text)+1;i++)
for(int j=1;j<=StrToInt(Edit1->Text)+1;j++)
grid->Cells[i][j]=“”;
int i,j,temp_i,temp_j,col,row,s;
i=(num+1)/2; //代表列
j=1; //代表行
temp_i=0;
temp_j=0;
for (int cishu=1;cishu<=num*num;cishu++)//填数字
{
grid->Cells[i][j]=IntToStr(cishu);
temp_i=i;
temp_j=j;
i=i+1;
if(i>num)i=1;
j=j-1;
if(j==0)j=num;
if(grid->Cells[i][j]!=“”)
{
i=temp_i;
j=temp_j+1;
}
}
//填完数字后做统计
for (row=1;row<=num;row++) //对每一行进行统计
{
s=0;
for(col=1;col<=num;col++)
{
s=s+StrToInt(grid->Cells[col][row]);
}
grid->Cells[col][row]=IntToStr(s);
}
for (col=1;col<=num;col++) //对每一列进行统计
{
s=0;
for(row=1;row<=num;row++)
{
s=s+StrToInt(grid->Cells[col][row]);
}
grid->Cells[col][row]=IntToStr(s);
}
}
2.“学习填数字”代码为:
void __fastcall TForm1::Button3Click(TObject *Sender)
{
//////////防止填了数字后再次填数字不动作
for(int i=1;i<=StrToInt(Edit1->Text)+1;i++)
for(int j=1;j<=StrToInt(Edit1->Text)+1;j++)
grid->Cells[i][j]=“”;
/////////////时钟初始化
ti=(num+1)/2; //代表列
tj=1; //代表行
temp_ti=0;
temp_tj=0;
tcishu=1;
Timer1->Enabled =true;
}
3.时钟控件Timer1的Interval属性改为700,其OnTimer事件代码为:
void __fastcall TForm1::Timer1Timer(TObjec
t *Sender)
{
if(tcishu<=num*num)
{
grid->Cells[ti][tj]=IntToStr(tcishu);
temp_ti=ti;
temp_tj=tj;
ti=ti+1;
if(ti>num)ti=1;
tj=tj-1;
if(tj==0)tj=num;
if(grid->Cells[ti][tj]!=“”)
{
ti=temp_ti;
tj=temp_tj+1;
}
}
tcishu=tcishu+1;
if(tcishu>num*num)
{
Timer1->Enabled =false;
}
}
小结:程序完成后的效果如图2所示,让计算机为你填写九宫图的感觉是不是很棒呢?
编后: “快速填数字”的按钮里的代码值得读者细读一下。通过简短的代码准确地描述出了在填写表格的每一步中,数字在横纵方向的变化规律,并巧妙地利用了两个临时变量,解决了数字在将要填的下个表格已经被占据的情况下如何回退的问题。这是本程序的精华所在。

