如图:

下面一排人名随机排列,小球随机从上面弹出,方向速度位置等等都是随机的,反复碰撞最后停在谁头上谁就付钱。这是我感觉最完美的一种算法,最公平,因为整个计算过程都是可视的,不存在作弊嫌疑。另外考大家一下,有谁还记得加速度运动的公式?
代码公开以受监督:
#define 更新日期 "更新日期:2007年7月9日"
#define 版本 "版本:1.2"
#include "/pocketc/commctrl.h"
#include "/pocketc/winuser.h"
#define dt 0.004 //物理运动刷新时间(秒),数字越小运动越精确。推荐0.004。数值太小会导致跳帧,运动速度会变慢
#define ddt 0.04 //画面刷新时间(秒),数字越大图像越不闪。推荐0.04
#define dddt 2 //画面修复刷新时间(秒),推荐2
#define g 980 //重力加速度,数值越小越慢,推荐980
#define pi 3.1415926 //圆周率
#define r 4 //小球的半径(象素),推荐4
#define R 12 //柱子的半径(象素),推荐12
#define pillarnum 15 //柱子数量,数量越多越影响计算速度,推荐15
#define slow 0.9 //每次碰撞后速度损失到原来的多少,推荐0.9
#define roll 0.99 //滚动摩擦系数,推荐0.99
#define vstop 1 //速度停止阈值,低于这个速度就算停止,推荐1
#define vytoroll 50 //在底部时从快速弹跳状态变为滚动的vy阈值,推荐50
#define astop 10 //加速度停止阈值,低于这个加速度就算停止,推荐10
#define width 240 //运动区域宽度,QVGA为240
#define length 225 //运动区域长度,推荐225
//#define debug //输出调试信息
//屏幕上的绘图区域是x:0~240,y:0~268
int i;
int renshu; //本次计算参与的人数
string nn[20];
float x=10,y=10; //当前坐标的浮点值
int x0,y0; //上一个刷新时间的坐标
float vx=10,vy=1; //当前速度,分量xy。每秒几象素
float ax=0,ay=g; //当前加速度,分量xy
int pillarx[pillarnum],pillary[pillarnum]; //柱子的坐标
int pillarG[pillarnum]; //柱子颜色的G值
int hit; //0代表没有碰到柱子,1代表碰到后正在弹开
int pause; //0运行,1暂停
//==========================================================================================
make(int mode) //mode==0是乱序列表显示,1是弹珠台下面的显示
{
string mm[20];
int a,b,t,k;
//打乱nn[]的顺序
for(i=0;i!=1;1)
{
a=random(renshu);
b=random(renshu);
if(mm[a]==""&&nn[b]!="")
{
mm[a]=nn[b];
nn[b]="";
}
for(t=0;t {
i=1;
if(mm[t]=="") {i=0;break;}
}
}
for(i=0;i {
nn[i]=mm[i];
}
//显示输出
clearg();
if(mode==0)
{
settextcolor(0,0,0);
setfontattr("tahoma",0,0,0,900,8,23);
for(i=0;i {
text(70,10+i*20,(string)(i+1)+"."+mm[i]);
sleep(100);
}
}
else if(mode==1)
{
settextdefault();settextcolor(0,0,0);
for(i=0;i {
text((float)((i+0.5)*width/renshu-7),230,strgetc(nn[i],0));
text((float)((i+0.5)*width/renshu-7),242,strgetc(nn[i],1));
text((float)((i+0.5)*width/renshu-7),254,strgetc(nn[i],2));
}
settextdefault();
}
}
//==========================================================================================
gui()
{
//以下是正常界面,ID:101~130
createctrl("BUTTON", "xx", WS_CHILD|BS_AUTOCHECKBOX|WS_VISIBLE, 0x0, 10, 10, 50, 16, 101) ;
createctrl("BUTTON", "xx", WS_CHILD|BS_AUTOCHECKBOX|WS_VISIBLE, 0x0, 10, 30, 50, 16, 102) ;
createctrl("BUTTON", "xx", WS_CHILD|BS_AUTOCHECKBOX|WS_VISIBLE, 0x0, 10, 50, 50, 16, 103) ;
cbxset(103,1);
createctrl("BUTTON", "xx", WS_CHILD|BS_AUTOCHECKBOX|WS_VISIBLE, 0x0, 10, 70, 50, 16, 104) ;
createctrl("BUTTON", "xx", WS_CHILD|BS_AUTOCHECKBOX|WS_VISIBLE, 0x0, 10, 90, 50, 16, 105) ;
createctrl("BUTTON", "xx", WS_CHILD|BS_AUTOCHECKBOX|WS_VISIBLE, 0x0, 10, 110, 50, 16, 106) ;
createctrl("BUTTON", "xx", WS_CHILD|BS_AUTOCHECKBOX|WS_VISIBLE, 0x0, 10, 130, 50, 16, 107) ;
createctrl("BUTTON", "xx", WS_CHILD|BS_AUTOCHECKBOX|WS_VISIBLE, 0x0, 10, 150, 50,16, 108) ;
createctrl("BUTTON", "xx", WS_CHILD|BS_AUTOCHECKBOX|WS_VISIBLE, 0x0, 10, 170, 50, 16, 109) ;
createctrl("BUTTON", "xx", WS_CHILD|BS_AUTOCHECKBOX|WS_VISIBLE, 0x0, 10, 190, 50, 16, 110) ;
createctrl("BUTTON", "xx", WS_CHILD|BS_AUTOCHECKBOX|WS_VISIBLE, 0x0, 10, 210, 50, 16, 111) ;
createctrl("BUTTON", "其他1", WS_CHILD|BS_AUTOCHECKBOX|WS_VISIBLE, 0x0, 10, 230, 50, 16, 112) ;
createctrl("BUTTON", "其他2", WS_CHILD|BS_AUTOCHECKBOX|WS_VISIBLE, 0x0, 10, 250, 50, 16, 113) ;
createctrl("BUTTON", "其他3", WS_CHILD|BS_AUTOCHECKBOX|WS_VISIBLE, 0x0, 10, 270, 50, 16, 114) ;
createctrl("STATIC", "付钱:", WS_CHILD|WS_VISIBLE, 0x0, 150, 100, 140, 20, 121) ;
//createctrl("STATIC", "", WS_CHILD|WS_VISIBLE, 0x0, 150, 120, 140, 20, 202) ;
createctrl("STATIC", "个数:", WS_CHILD|WS_VISIBLE, 0x0, 150, 150, 140, 20, 122) ;
createctrl("COMBOBOX", "", 0x50a10043, 0x0, 150, 170, 50, 65,123) ;
cbaddstr(123,"1");cbaddstr(123,"2");cbaddstr(123,"3");cbsetcur(123,0);
createctrl("STATIC", "方式:", WS_CHILD|WS_VISIBLE, 0x0, 150, 200, 140, 20, 124) ;
createctrl("COMBOBOX", "", 0x50a10043, 0x0, 150, 220, 80, 65,125) ;
cbaddstr(125,"乱序列表");cbaddstr(125,"随机生成");cbaddstr(125,"弹珠台");cbsetcur(125,0);
createctrl("BUTTON","计算",WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON, 0x0, 150, 20, 80, 50,130) ;
}
//==========================================================================================
show(string a)
{
clearg();
settextcolor(0,0,0);
setfontattr("tahoma",0,0,0,900,8,23);
text(150-textw(a)/2,120,a);
}
//==========================================================================================
drawballgame(int mode) //mode:0绘制全部,1绘制柱子,2绘制地板
{
if(mode==0||mode==2)
{
for(i=0;i {
rect((float)(i*width/renshu),length,(float)((i+1)*width/renshu),length+5);
}
}
if(mode==0||mode==1)
{
for(i=0;i {
setpenattr(1,1,0,0,0);
setbrushattr(0,pillarG[i],255);
circle(pillarx[i],pillary[i],R);
setbrushattr(255,255,255);
}
}
}
//==========================================================================================
ballgame()
{
int k,t;
int dd[20];
delallgui(100,130);
make(1);
//以下是弹珠台界面,ID:201~
#ifdef debug
createctrl("STATIC", "vx:", WS_CHILD|WS_VISIBLE, 0x0, 1, 1, 40, 15, 201) ;
createctrl("STATIC", "vy:", WS_CHILD|WS_VISIBLE, 0x0, 50, 1, 80, 15, 202) ;
createctrl("STATIC", "x:", WS_CHILD|WS_VISIBLE, 0x0, 1, 16, 40, 15, 203) ;
createctrl("STATIC", "y:", WS_CHILD|WS_VISIBLE, 0x0, 50, 16, 40, 15, 204) ;
createctrl("STATIC", "", WS_CHILD|WS_VISIBLE, 0x0, 1, 31, 240, 15, 205) ;
#endif
createctrl("BUTTON","停止",WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON, 0x0, 209, 1, 30, 16,210) ;
createctrl("BUTTON","暂停",WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON, 0x0, 209, 17, 30, 16,211) ;
settimer(10,1000*dt); //运动刷新时间,注意参数必须传入int型,*dt要写在后面
settimer(20,1000*ddt); //画面刷新时间
settimer(30,1000*dddt); //画面修复刷新时间
for(i=0;i {
for(t=0;t<1000;t++)
{
pillarx[i]=R+random(width-2*R);
pillary[i]=R+random(length-2*R);
for(k=0;k if((pillarx[i]-pillarx[k])*(pillarx[i]-pillarx[k])+(pillary[i]-pillary[k])*(pillary[i]-pillary[k])<4*R*R)
break;
if(k==i) break;
}
}
drawballgame(0);
x=r+random(width-2*r);
y=r+1;
while(abs(vx)<10*vstop) //vx最好大一点
vx=random(40)-20;
vx=vx*6;
vy=random(60);vy=vy*6;
ax=0;
ay=g;
hit=0;
}
//==========================================================================================
stopballgame()
{
for(i=0;i {
if((float)(i*width/renshu)>x) break;
}
alert("恭喜:"+nn[i-1]);
killtimer(10);killtimer(20);killtimer(30);
clearg();
delallgui(200,220);
gui();
flushevent(); //假如刷新速度过快,可能堆积定时器事件
}
//==========================================================================================
ovement() //刷新物理运动状态信息,程序必须追求效率
{
float a1,a2,a3;
if(abs(vx) {
stopballgame();
return;
}
if(x+(float)r>=(float)width||x-(float)r<=(float)0) //碰到画面边缘反弹,不写float可能会在屏幕边缘速度反复减慢至0
{
vx=vx*-1*slow;
if(x+(float)r>(float)width)
x=width-r;//防止嵌入墙壁而粘住
else if(x-(float)r<(float)0)
x=r;
}
if(y+(float)r>=(float)length||y-(float)r<=(float)0)
{
if(vy<1.5*vytoroll&&vy>vytoroll) //在底部反复弹跳的情况,让vy快速降下来
{
vy=vy*-1*slow*slow;y=2*length-y-2*r;
}
else if(vy>vytoroll) //vy朝下且速度够快
{
//editset(205,"反弹1,vy="+vy);sleep(1000);
vy=vy*-1*slow;
//editset(205,"反弹2,vy="+vy);sleep(1000);
y=2*length-y-2*r; //嵌入相当于反向弹出
drawballgame(2);
}
else if(vy<-vytoroll) //vy朝上且速度够快
{
vy=vy*-1*slow;
y=2*y+r; //嵌入相当于反向弹出
drawballgame(2);
}
else if(abs(vy)<=vytoroll&&y>=(float)length-r) //在底部滚动,重力抵消。由于可能会出现快速频繁的弹跳,所以对vy下限增大
{
vy=0;ay=0;vx=vx*roll;y=(float)length-r;
// editset(205,"滚动");
}
}
for(i=0;i {
if((x-(float)pillarx[i])*(x-(float)pillarx[i])+(y-(float)pillary[i])*(y-(float)pillary[i])<=(float)(r+R)*(r+R))
break;
}
if(i==pillarnum) //没有碰到柱子
{
hit=0; //防止陷到柱子里面去或粘在柱子上。在碰到柱子的期间速度只能变一次
}
else if(i { //碰撞瞬间,速度矢量沿2球心连线的分量反向了。具体计算方法见草稿纸。注意角度坐标系和屏幕坐标系y相差一个负号。这里貌似有bug,不符合能量守恒定律
hit=1;
if(vx==0)
{if(vy>=0) a1=-pi*0.5;else a1=pi*0.5;}
else if(vx>0) //在1、4象限的情况
a1=atan(-vy/vx); //原速度方向
else //2、3象限
a1=atan(-vy/vx)-pi;
if((float)pillarx[i]>x)
a2=atan(-((float)pillary[i]-y)/((float)pillarx[i]-x)); //2球心连线的角度,指向柱子
else
a2=atan(-((float)pillary[i]-y)/((float)pillarx[i]-x))-pi;
a3=2*a2-a1+pi; //碰撞后速度方向
vx=sqrt(vx*vx+vy*vy)*cos(a3)*slow;
vy=-sqrt(vx*vx+vy*vy)*sin(a3)*slow;
setpenattr(1,1,0,0,0);pillarG[i]=pillarG[i]+80;setbrushattr(0,pillarG[i],255);
circle(pillarx[i],pillary[i],R); //重绘一下柱子
setbrushattr(255,255,255);
#ifdef debug
editset(205,"运动角度="+(int)(a1*180/pi)+",球心角度="+(int)(a2*180/pi)+",反弹角度="+(int)(a3*180/pi));
setpenattr(1,1,0,0,0);clearg();drawballgame();
line(x-30*sin(a2),y-30*cos(a2),x+30*sin(a2),y+30*cos(a2));
line(x,y,x+40*cos(a1),y-40*sin(a1));text(x+40*cos(a1),y-40*sin(a1),"a1(入射方向)"); //注意y坐标系和数学方向相反
line(x,y,x+40*cos(a3),y-40*sin(a3));text(x+40*cos(a3),y-40*sin(a3),"a3(出射方向)");
//killtimer(10);killtimer(20);killtimer(30);pause=1;editset(211,"继续");
sleep(100);
#endif
}
x=x+vx*dt+0.5*ax*dt*dt; //公式为x=v0*t+0.5*a*t^2
y=y+vy*dt+0.5*ay*dt*dt;
vx=vx+ax*dt;
vy=vy+ay*dt;
}
//==========================================================================================
events()
{
int eve,who,n1,n2,n3;
int dd[20];
int tmid;
eve=(event(1));
who=guiid();
if(eve==PM_TIMER)
{
tmid=timerid(); //timerid取一次就会消除掉上一次的时间事件,所以不能反复使用timerid
if(tmid==10) //刷新物理运动状态信息
{
movement();
}
else if(tmid==20) //刷新画面
{
setpenattr(1,1,255,255,255);
circle(x0,y0,r);
setpenattr(1,1,255,0,0);
setbrushattr(255,0,0);
circle(x,y,r);
setbrushattr(255,255,255);
setpenattr(1,1,0,0,0);
x0=x;y0=y;
#ifdef debug
editset(201,"vx="+(int)vx);
editset(202,"vy="+(int)vy);
editset(203,"x="+(int)x);
editset(204,"y="+(int)y);
editset(205,"a1="+(int)(atan(-vy/vx)*180/pi));
#endif
}
else if(tmid==30) //修复画面
{
drawballgame(0);
}
}
if(who==210&&eve==PM_BUTTONUP)
{
stopballgame(); //停止
}
if(who==211&&eve==PM_BUTTONUP)
{ //暂停
flushevent();
if(pause==0) //运行
{
killtimer(10);killtimer(20);killtimer(30);
pause=1;editset(211,"继续");
}
else if(pause==1) //暂停
{
settimer(10,1000*dt); //运动刷新时间,注意参数必须传入int型,*dt要写在后面
settimer(20,1000*ddt); //画面刷新时间
settimer(30,1000*dddt); //画面修复刷新时间
pause=0;editset(211,"暂停");
}
}
if(who==130&&eve==PM_BUTTONUP)
{
renshu=0;
for(i=101;i<=114;i++)
if(cbxget(i)) { dd[renshu]=i;nn[renshu]=editget(i);renshu++;}
if(renshu==0) {alert("请先选择!");show("");return;}//editset(202,"");
if(renshu-1 if(cbgetcur(125)==0) //方式1
make(0);
else if(cbgetcur(125)==1) //方式2
{
if(cbgetcur(123)==0) //个数:1
show(editget(dd[random(renshu)])); //editset(202,editget(dd[random(k)])); //0~n-1
else if(cbgetcur(123)==1) //个数:2
{
n1=random(renshu);
for(i=0;i<1000;i++)
{n2=random(renshu);if(n2!=n1) break;}
show(editget(dd[n1])+"、"+editget(dd[n2])); //0~n-1
}
else if(cbgetcur(123)==2) //个数:3
{
n1=random(renshu);
for(i=0;i<1000;i++)
{n2=random(renshu);if(n2!=n1) break;}
for(i=0;i<1000;i++)
{n3=random(renshu);if(n3!=n1&&n3!=n2) break;}
show(editget(dd[n1])+"、"+editget(dd[n2])+"、"+editget(dd[n3])); //0~n-1
}
}
else if(cbgetcur(125)==2) //方式3
ballgame();
}
}
//=================================================================================
exiting() //程序退出时
{
//killtimer(10);killtimer(20);killtimer(30);
}
//==========================================================================================
main()
{
atexit(exiting);
clearg();
menudel(0, 0x0, 40003) ; //控制台
gui();
title("打的");
about("谢文彬编写。\nxwb558@126.com\n"+更新日期+"\n"+版本);
devicesip(0);
while(1){sleep(0);events();}
}