魔方游戏是个经典的游戏,在当前AI人工智能发展的大好形势下,机器人还原魔方成为一个很好的课题。由我对游戏和编程的兴趣爱好,设计编制了此魔方游戏程序。
魔方游戏程序设计几个方面的要点:
一、游戏界面设计
我是在手机上编制程序的,故游戏界面是手机界面。由于界面限制,只能将各个游戏功能紧凑在小界面上。
上部设置常用按钮:开始游戏,还原魔方,自动演示,退出程序,加 版本信息。
另加下拉菜单,可选六步解法,查看走步记录等。
中部设置魔方状态图,前右上三个面采用伪3D的图样,另加左后底三个面的平面图样。特别一点,图框下面加了六个面的一体图样。这样可使游戏者比较直观的看到魔方的状态。
下部设置操作按钮的功能块,设置了全部的12个操作按钮,和3个旋转翻转按钮。便于游戏的操作。
游戏的图形界面的是用简单的C语言编制的,全是用编译器绘图方法绘制的。一般的游戏图形都是方形和圆形为主,此魔方游戏图形是用伪3D的方法绘制的。此方法是用平面绘画方法画出许多菱形拼接成魔方的立体图像,给你的观感是立体的魔方。你可看到魔方的三个面,前面右面和上面。这样你能根据小方块的颜色来判断而操作魔方。一般人玩魔方都是左手拿魔方,右手做各种旋转动作。此设计就是左手拿手机,右手转魔方。游戏的视觉效果就这样。
二、游戏功能设置
游戏功能最主要的就是15个操作按钮的功能,我设计了一个比较直观的易于理解的算法。用一个数组存储六个面的54个小块的颜色值,一切的操作变换都是颜色的变换。我写了15个操作键的函数就能让魔方转起来。直接可以玩了,就这么简单。又写了判断完成的函数,这是游戏的基本的东西。
另外我做了隐藏按钮,在图框的右下角,可以查看4个重要的公式(助记),以及操作ABCD 公式。这些都是增加游戏可操控性。
算法解释:
int B[70] ; //存储颜色值,值为1-6,预设置为:蓝红绿 黄橙白
左面:基色蓝,编码B11- B19, 前面:基色红,编码 B21 - B29,
右面:基色绿,编码B31- B39, 上面:基色黄,编码 B41 - B49,
后面:基色橙,编码B51- B59, 下面:基色白,编码 B61 - B69,
(附图)
Frontright (){ //前右 F
sw[1]=B[47]; sw[2]=B[48]; sw[3]=B[49]; //sw = swap
B[47]=B[19]; B[48]=B[16]; B[49]=B[13];
B[19]=B[63]; B[16]=B[62]; B[13]=B[61];
B[63]=B[31]; B[62]=B[34]; B[61]=B[37];
B[31]=sw[1]; B[34]=sw[2]; B[37]=sw[3];
sw[1]=B[21]; B[21]=B[27]; B[27]=B[29]; B[29]=B[23]; B[23]=sw[1];
sw[2]=B[22]; B[22]=B[24]; B[24]=B[28]; B[28]=B[26]; B[26]=sw[2];
ts=ts+1; //走步计数
drawbox (); //画出
print "do> F Front right "; //打印走步记录
}//Frontright()
这算法很简单,没什么深奥的理论。就是按规则转换颜色值,画出魔方。
三、AI 智能解魔方
演示套用说明书经典公式,即六步公式:上层四棱,上层四角,翻转,二层四棱,上层十字,顶面同色,六面同色。同时还提供操作记录,查看操作复盘。自动演示的记录就套用六步公式以供学习和研究用。我感觉用手机玩魔方的手感有种别样的味道。
上层四棱按基本操作完成,上层四角有3个公式来完成,二层四棱有2个公式来完成。接下来有易于记忆的ABCD 4个公式,上层十字用A 公式,顶面同色用B C 公式,六面同色用D公式。公式简单易于理解,不作解释了。用公式时如何调整状态,代码里有解释。此方法公式量少,完成魔方要140-200步。演示解魔方用时也就70到100秒。
手机魔方游戏我见过国外编的仿3D动画式的游戏,用手指滑动旋转操作,蛮酷的。我这个游戏是很小巧的,它的优点是简单和有趣,希望大家喜欢。
四、魔方游戏设计前瞻
现在大家在玩用程序来解魔方,还有机器人解魔方。
输入打乱的魔方的颜色图,由程序来解,输出解法步骤。此程序扩展一下就行。
至于机器人解魔方,此程序就能输出解法步,开一个数组存储解法步,如:
String A[300] = ( R1, U, R, U, R, U, R1, U, ............),
传导给机器人程序,由它来解释操作。程序中打印记录抽出操作步就可以。
机器手设计,魔方翻转较难,那么就在第一步上层四棱和第二步上层四角操作倒着做,改为底层十字底层四角直接操作倒下面。后面的操作时底部白色中心和上面的黄色中心块是不变的。这样设计机器操作就简单了。
至于机器的速度,我手机操作用时70-100秒,大多时间是画出图样,我估计机器动作也就10秒吧。网上看到机器手解魔方视频,有兴趣的小伙伴可试验下。
本程序用的C语言是MySpringC v.2.7是目前的完善版本。
下面的游戏代码就是用它在安卓手机上编写的,可制作成安卓手机桌面app应用程序。此样例可复制黏贴到编译器直接使用,亦可用 VB6 ,C++ , java 改写。
以下是源码:
// 最简单的 C 语言编程
// myspringc v2.7 可编译安卓本机 app 应用
// 此样例可复制黏贴到 myspringc 编译
// 此文档可用 VB,VC , java 改写
//***********************
// 魔方 Magic Cube
// [email protected]
// 2024-07-15 修订
//***********************
Canvas cs ;
string sBarDes[10];
int nBarId[10];
string sMenu[50];
int nMenu[50];
float pi=3.1415926535;
float src[4]; //ClearDraw (cls) clear screen
string s,s1,s2,s3,s4,s5,s6,s7,s8,ss1,ss2,ss3;
float sx,sy,dx,dy;
float px,py;
FileInput filebox;
string fname; //filename
int i,j,n,t,k,ts; //t = times
int context,obj,id,event; //canvasProc
int bx,by; //button x y position
int bn; //button id number
int B[70]; //1 left 2 front 3 right 4 up 5 back 6 down
int cr1,cg1,cb1; //set color rgb
int scolor; // switch cube color
int sw[9]; //switch B color
int donum; //calculate do step number
int kn,cando;
double Rn; //random number
int tim[3]; //get Time
int hh,mm,ss;
int oldhh,oldmm,oldss;
int newhh,newmm,newss;
int mms; //show using time
string hhts,mmts,ssts, tim$;
string fs1,fs2,fs3,fs4,fs5,fs6; //show formula
string fma,fmb,fmc,fmd, fmd1; //formula
int ms, uf; //using formula
main(){
setDisplay(1);
cs.Active();
cs.SetProc (context, mycanvasProc); //getProc
sBarDes[0]="开始游戏";
nBarId[0]=100;
sBarDes[1]="还原魔方";
nBarId[1]=101;
sBarDes[2]="自动演示";
nBarId[2]=102;
sBarDes[3]="显示图板";
nBarId[3]=103;
sBarDes[4]="退出程序";
nBarId[4]=104;
sBarDes[5]="V.";
nBarId[5]=105;
setToolBarHeight(6);
setButtonTextSize(13);
setButtonColor(255,0,0,250);
setButtonTextColor(255,255,255,0);
setToolBar(100,myToolBarProc,sBarDes,nBarId,6);
sMenu[0]="开始游戏";
nMenu[0]=200;
sMenu[1]="第一步:上层四棱";
nMenu[1]=201;
sMenu[2]="第二步:上层四角";
nMenu[2]=202;
sMenu[3]="第三步:二层四棱";
nMenu[3]=203;
sMenu[4]="第四步:顶层十字";
nMenu[4]=204;
sMenu[5]="第五步:顶面同色";
nMenu[5]=205;
sMenu[6]="第六步:六面同色";
nMenu[6]=206;
sMenu[7]="自动演示";
nMenu[7]=207;
sMenu[8]="查看走步记录";
nMenu[8]=208;
sMenu[9]="显示游戏图板";
nMenu[9]=209;
sMenu[10]="退出";
nMenu[10]=210;
setMenu(200,myMenuProc,sMenu,nMenu,11);
setTitle("魔方 Magic Cube ");
//*****************************
getTime (tim);
oldhh=tim[0];
oldmm=tim[1];
oldss=tim[2];
kn=2;
drawcover ();
while (){}
}//main ()
mycanvasProc (int context,int obj,int id,int event,float x,float y){
bn=0; //init buttom
if (event==0||event==2||event==10){ //get touch
kn=kn+1;
if ((kn-kn/2*2)==0){ //触控防闪烁
cando=1; }else{cando=0; return; }
//设置界面操作按钮 12 cmd + 3 转向
if (x>20&&x<110){ //select button btn number
if (y>770&&y<840)bn=1;
if (y>840&&y<910)bn=2;
if (y>920&&y<980)bn=9; }
if (x>310&&x<400){ //select button btn number
if (y>760&&y<840)bn=3;
if (y>840&&y<920)bn=4;
if (y>920&&y<990)bn=10; }
if (x>120&&x<210&&y>750&&y<810)bn=5;
if (x>210&&x<300&&y>750&&y<810)bn=6;
if (x>120&&x<210&&y>815&&y<885)bn=7;
if (x>210&&x<300&&y>815&&y<885)bn=8;
if (x>120&&x<210&&y>890&&y<960)bn=11;
if (x>210&&x<300&&y>890&&y<960)bn=12;
//左旋转,右旋转,上翻转
if (x>430&&x<555&&y>750&&y<840)bn=13;
if (x>560&&x<690&&y>750&&y<840)bn=14;
if (x>490&&x<620&&y>850&&y<950)bn=15;
//** 设置操作标记
cs.SetFillMode (1);
cs.SetTextSize (20);
cs.SetColor(255,255,255,255);
//cs.DrawCircle (160,280,5); //L
// cs.DrawCircle (160,440,5);
if (x>120&&x<200&&y>240&&y<320)bn=1;
if (x>120&&x<200&&y>400&&y<480)bn=2;
cs.DrawText ("L1",150,290);
cs.DrawText ("L",150,450);
// cs.DrawCircle (320,370,2); //R
// cs.DrawCircle (320,520,2);
if (x>280&&x<360&&y>330&&y<410)bn=3;
if (x>280&&x<360&&y>480&&y<560)bn=4;
cs.DrawText ("R",310,370);
cs.DrawText ("R1",305,530);
// cs.DrawCircle (260,270,40); //U
//cs.DrawCircle (450,270,40);
if (x>220&&x<300&&y>230&&y<310)bn=5;
if (x>410&&x<490&&y>230&&y<310)bn=6;
cs.DrawText ("U",270,270);
cs.DrawText ("U1",430,270);
//cs.DrawCircle (400,370,2); //F
//cs.DrawCircle (400,520,2);
if (x>360&&x<440&&y>330&&y<410)bn=7;
if (x>360&&x<440&&y>480&&y<560)bn=8;
cs.DrawText ("F1",390,370);
cs.DrawText ("F",395,530);
//cs.DrawCircle (560,280,5); //B
//cs.DrawCircle (560,440,5);
if (x>520&&x<600&&y>240&&y<320)bn=10;
if (x>520&&x<600&&y>400&&y<480)bn=9;
cs.DrawText ("B",550,290);
cs.DrawText ("B1",550,450);
// cs.DrawCircle (230,490,4); //D
// cs.DrawCircle (480,490,4);
if (x>190&&x<270&&y>450&&y<530)bn=11;
if (x>440&&x<520&&y>450&&y<530)bn=12;
cs.DrawText ("D1",230,490);
cs.DrawText ("D",470,490);
cs.SetColor (255,0,0,250) ;
cs.DrawCircle (648,700, 21) ;
cs.DrawCircle (660,700, 21) ;
cs.SetColor (255,250,250,0) ;
cs.DrawText ("Fm", 640, 705 ) ;
//公式解法:
//顶层十字:至多三次即可
//公式 A:R1 U1 F1 U F R
// 顶面同色,顶面十字四角无色块,下角色块置左;
// 有一块,田置左下; 有二块,黄色置前左
//公式 B:R U R1 U R U U R1
//顶面同色:调整角块,两角块同色置前
//公式 C:R B1 R FF R1 B R FF RR
//六面同色:顶棱同色置后B,
//公式 D:R U1 R U R U R U1 R1 U1 RR
fma="A = R1 U1 F1 U F R ";
fmb="B = R U R1 U R U U R1 ";
fmc="C = R B1 R FF R1 B R FF RR ";
fmd="D = R U1 R U R U R U1 ";
fmd1=" R1 U1 RR";
if (x>640&&x<720&&y>650&&y<720){
uf=uf+1; }
if (uf>1) uf=0;
cs.SetColor (255,80,80,80) ; //clear text
cs.DrawRect (485, 525, 710, 625) ;
cs.DrawRect (667, 210, 685, 325) ;
cs.DrawRect (667, 380, 685, 520) ;
if (uf==1) {
cs.SetColor (255,250,250,250) ;
cs.SetTextSize (16);
cs.DrawText (fma,490,540);
cs.DrawText (fmb,490,560);
cs.DrawText (fmc,490,580);
cs.DrawText (fmd,490,600);
cs.DrawText (fmd1,490,620); }
cs.DrawCircle (675,220,5); //A
cs.DrawCircle (675,300,5); //B
cs.DrawCircle (675,400,5); //C
cs.DrawCircle (675,480,5); //D
cs.DrawText ("A",670,245);
cs.DrawText ("B",670,325);
cs.DrawText ("C",670,425);
cs.DrawText ("D",670,505);
if (x>660&&x<720&&y>180&&y<260){
ms=41; Formula (); } //A
if (x>660&&x<720&&y>260&&y<340){
ms=51; Formula (); } //B
if (x>660&&x<720&&y>380&&y<460){
ms=53; Formula (); } //C
if (x>660&&x<720&&y>460&&y<540){
ms=61; Formula (); } //D
cs.SetFillMode (1);
cs.Update ();
//********* get button position x y
bx=(int)(x);
by=(int)(y);
if (bn==1){
s3="走步: 左上 L ' ";
Leftup (); }
if (bn==2){
s3="走步: 左下 L ";
Leftdown (); }
if (bn==3){
s3="走步: 右上 R ";
Rightup (); }
if (bn==4){
s3="走步: 右下 R ' ";
Rightdown (); }
if (bn==5){
s3="走步: 上左 U ";
Upleft (); }
if (bn==6){
s3="走步: 上右 U ' ";
Upright (); }
if (bn==7){
s3="走步: 前左 F ' ";
Frontleft (); }
if (bn==8){
s3="走步: 前右 F ";
Frontright (); }
if (bn==9){
s3="走步: 后左 B ' ";
Backleft (); }
if (bn==10){
s3="走步: 后右 B ";
Backright (); }
if (bn==11){
s3="走步: 下左 D ' ";
Downleft (); }
if (bn==12){
s3="走步: 下右 D ";
Downright (); }
if (bn==13){
s3="走步: 左旋转 ";
Turnleft (); }
if (bn==14){
s3="走步: 右旋转 ";
Turnright (); }
if (bn==15){
s3="走步: 翻转 ";
Turnup (); }
} //screen touch and select button ********
cs.SetFillMode (1);
cs.SetColor(255,240,240,240);
cs.DrawRect (0,3,700,30); // clear print x y
s=intToString(bn);
s4="按钮(bn) > "+s;
s=intToString(bx);
s1="X= "+s;
s=intToString(by);
s2="Y= "+s;
cs.SetColor (255,50,20,120);
cs.SetTextSize (26);
// cs.DrawText (s1,25,25);
// cs.DrawText (s2,120,25);
// cs.DrawText (s4,220,25);
cs.DrawText (s3,445,25);
cs.Update ();
checkfinished ();
}//mycanvasProc ()
//***************************************
Autoplay (){
clearOutput ();
print "User select >>> 自动演示";
newgame ();
Astep1 (); //上层四棱
//sleep (1000);
Astep2 (); //上层四角
//sleep (1000);
Turnup (); Turnup ();
//sleep (1000);
Astep3 (); //二层四棱
//sleep (1000);
Astep4 (); //顶层十字
//sleep (1000);
Astep5 (); //顶面同色
//sleep (1000);
Astep6 (); //六面同色
}//Autoplay ()
Formula (){
//界面设置四公式操作,可直接使用公式
if (ms==41){ goto ms41; } //公式 A
if (ms==51){ goto ms51; } //公式 B
if (ms==53){ goto ms53; } //公式 C
if (ms==61){ goto ms61; } //公式 D
ms41: //顶层十字
//fma= A:R1 U1 F1 U F R
print "公式 A ";
Rightdown (); Upright (); Frontleft ();
Upleft (); Frontright (); Rightup ();
return; //************
ms51: //顶面同色:同色
//fmb= B:R U R1 U R U U R1
print "公式 B ";
Rightup (); Upleft (); Rightdown ();
Upleft (); Rightup ();
Upleft (); Upleft (); Rightdown ();
return; //************
ms53: //顶面同色:调整角块
//fmc= C:R B1 R FF R1 B R FF RR
print "公式 C";
Rightup (); Backleft (); //R B1
Rightup (); Frontright (); Frontright (); //R FF
Rightdown (); Backright (); //R1 B
Rightup (); Frontright (); Frontright (); //R FF
Rightup (); Rightup (); //RR
return; //顶面同色 *************
ms61: //六面同色:完成
//fmd= D:R U1 R U R U R U1 R1 U1 RR
print "公式 D "