mytank

作者在 2014-01-08 00:05:57 发布以下内容

// 坦克大战.cpp : Defines the entry point for the console application.
//思路简介:坦克大战采用的是面向对象的编程思想,其中离不开对类及线程的理解和使用
#include "stdafx.h"
#include<windows.h>
#include <process.h>
#include"pic_.h"

#define uchar unsigned char //坐标类型   屏幕上所有的坐标均为非负
#define uint    unsigned int
//----全局变量
#define TK  20//最大坦克数量 
HANDLE hThread[TK];//多线程句柄
//------------
uint Base_coefficient=0;//当前基址系数
uchar vanish=0;//坦克消失消息,用来改变基址
//----pic请求变量,当每一个类装好pic之后,则改变对应变量值,发出请求,等到请求满后便调用picture(),显示一帧图片
uchar tk_pic[TK]={0};
uchar bt_pic[TK]={0};
//------------
class BULLET;
class TANK//坦克总类              
{
  public:
    uint BASE; //内部基址
    uint base_coefficient; //外部基址系数
    uchar x,y;//坦克中心位置
       uchar a,b;//a是炮筒的九宫格代码,b是空格的九宫格代码 
    uchar tankcode[8];//借助坦克代码(九宫格旋转公式),控制坦克形状(转向)
    TANK(uchar tx,uchar ty)
    {
    x=tx;y=ty;
       a=2;b=8;
    base_coefficient=Base_coefficient++;//将全局量转化为类体私有量,再将该系数加一抛出
    BASE=9*base_coefficient;
    shape(); 
    }//构造函数 给出坦克中心点,绘出坦克图形
       friend  BULLET;//将子弹类声明为坦克类的友元类,这样的话友元类BULLET中的所有函数都是TANK的友元函数,可以访问TANK的所有公共成员
    void shape(void)//  shape-塑形   
    {
      uchar i,j;
   tankcode[0]=a;
   for(i=1,j=1;i<10;i++)//i=1,2,...,9
   {
    if(i==a||i==b) continue;
    tankcode[j++]=i;
   }
    }
    void tank_vanish(void)
    {
   uint i=base_coefficient;
   //for()//修改其后tank的基址
   //{
   
  // }
   Base_coefficient--;
   //vanish=1;//发送消影消息
    }
    void tank_dis(uchar f)
    {  
     uint i;
     //---------------------
     //if(vanish&&BASE>=9) {BASE-=9;vanish=0;}
     //if(vanish&&BASE==0) {vanish=0;}
     p0.pnum=BASE;
        switch(tankcode[0])//炮筒
     {
        case 2: p0.x[BASE]=x-1;p0.y[BASE]=y;p0.z[BASE]=2;break;
        case 4: p0.x[BASE]=x;p0.y[BASE]=y-1;p0.z[BASE]=3;break;
        case 6: p0.x[BASE]=x;p0.y[BASE]=y+1;p0.z[BASE]=3;break;
     case 8: p0.x[BASE]=x+1;p0.y[BASE]=y;p0.z[BASE]=2;break;
     }
     p0.pnum++;
     for(i=1;i<8;i++)
     {
      switch(tankcode[i])
      {
         case 1:   p0.x[BASE+i]=x-1;p0.y[BASE+i]=y-1;break;
      case 2:   p0.x[BASE+i]=x-1;p0.y[BASE+i]=y;break;
      case 3: p0.x[BASE+i]=x-1;p0.y[BASE+i]=y+1;break; 
      case 4:   p0.x[BASE+i]=x;p0.y[BASE+i]=y-1;break;
      case 5: p0.x[BASE+i]=x;p0.y[BASE+i]=y;break;
      case 6: p0.x[BASE+i]=x;p0.y[BASE+i]=y+1;break;
      case 7: p0.x[BASE+i]=x+1;p0.y[BASE+i]=y-1;break;
      case 8: p0.x[BASE+i]=x+1;p0.y[BASE+i]=y;break;
      case 9: p0.x[BASE+i]=x+1;p0.y[BASE+i]=y+1;break;
      }
      p0.z[BASE+i]=1;//设置显示的样式
      p0.pnum++;
     }
          //---------------------
     if(f) 
     {
       tk_pic[base_coefficient]=1;//发出请求---设置一个专门的线程来满足这些请求
       while(tk_pic[base_coefficient]); 
     }
    }
    void tank_rotate(uchar direction)
    {
    a=direction;//2\8\6\4  上 下左 右
       b=10-a;
    shape();
       return;
    }
    void tank_con(char c)
    {
     if(c==0x50)//向下
     {
    if(x>=30)  x=30;
       else  x++;
    if(a!=8) tank_rotate(8);
    tank_dis(1);  
     }                      
    else if(c==0x4b)//向左
    {  
       if(y<=1)  y=1;
    else y--;
    if(a!=4) tank_rotate(4);
    tank_dis(1);
    }
          else if(c==0x4d)//向右
    {
     if(y>=72) y=72;
       else  y++;
       if(a!=6) tank_rotate(6);
    tank_dis(1);
    }
    else if(c==0x48)//向上
    {
     if(x<=1) x=1;
    else x--;
    if(a!=2)  tank_rotate(2);
    tank_dis(1);
    }
    c='\0';
 }
};
class TANK_derive_1:public TANK//第二控制坦克
{
   public: 
    TANK_derive_1(uchar tx,uchar ty):TANK(tx,ty) {  }
       void tank_con(char c) //重载基类中的该函数
    {
     if(c=='s'||c=='S')//向下
     {
    if(x>=30)  x=30;
       else  x++;
    if(a!=8) tank_rotate(8);
    tank_dis(1);  
     }                      
    else if(c=='a'||c=='A')//向左
    {  
       if(y<=1)  y=1;
    else y--;
    if(a!=4) tank_rotate(4);
    tank_dis(1);
    }
          else if(c=='d'||c=='D')//向右
    {
     if(y>=72) y=72;
       else  y++;
       if(a!=6) tank_rotate(6);
    tank_dis(1);
    }
    else if(c=='w'||c=='W')//向上
    {
     if(x<=1) x=1;
    else x--;
    if(a!=2)  tank_rotate(2);
    tank_dis(1);
    }
    c='\0';
 }
};
class TANK_derive_2:public TANK//敌人坦克
{
   public:
    TANK_derive_2(uchar tx,uchar ty):TANK(tx,ty) { }
     void enemy_autogo(void)
  {
   if(y>=72) y=5;
   TANK::tank_con(0x4d);Sleep(20);
  }
};
uchar Thread_Exit[TK]={0};//线程退出数组
class BULLET//子弹类
{
   public:
    uchar X,Y;//子弹坐标   
    void bullet_destroy(uchar x,uchar y,TANK &tank_t)//子弹线程关闭其他线程
    {
         int i;int t=0;
   for(i=0;i<p0.pnum;i++)
    if(p0.x[i]==x&&p0.y[i]==y)   {t++; if(t==2)  {tank_t.tank_vanish();Thread_Exit[0]=1;t=0;return;} }
    }
    void bullet_dis(TANK &tank_t)
    {
     uint i,j;
     j=tank_t.BASE;tank_t.BASE++;
           if(tank_t.a==2)//前射子弹
     { 
      X=tank_t.x-2;Y=tank_t.y;//初始位置
      for(i=X;i>0;i--)
      {
       p0.x[j]=i;p0.y[j]=Y; p0.z[j]=4;//子弹属性
       Sleep(10);
       tank_t.tank_dis(1);
       bullet_destroy(i,Y,tank_t);
      }
     }
     if(tank_t.a==8)//后射子弹
     {
      X=tank_t.x+2;Y=tank_t.y;
      for(i=X;i<30;i++)
      {
       p0.x[j]=i;p0.y[j]=Y;p0.z[j]=4;//子弹属性
       Sleep(10);
       tank_t.tank_dis(1);
       bullet_destroy(i,Y,tank_t);
      }
     }
     if(tank_t.a==4)//左射子弹
     {
    X=tank_t.x;Y=tank_t.y-2;
    for(i=Y;i>0;i--)
    {
       p0.x[j]=X;p0.y[j]=i; p0.z[j]=4;//子弹属性
                   Sleep(10);
       tank_t.tank_dis(1);
       bullet_destroy(X,i,tank_t);
    }
     }
     if(tank_t.a==6)//右射子弹
     {
    X=tank_t.x;Y=tank_t.y+2;
    for(i=Y;i<72;i++)
    {
       p0.x[j]=X;p0.y[j]=i; p0.z[j]=4;//子弹属性
       Sleep(10);
       tank_t.tank_dis(1);
       bullet_destroy(X,i,tank_t);
    }
     }
     tank_t.BASE--;
     tank_t.tank_dis(1);
    }
};
//-----------全局变量------------
TANK tank0(8,8);
//TANK_derive_1 tank1(20,20);
TANK_derive_2 tank_0(4,20);
uchar tkbt[TK]={0};//用于坦克线程和子弹线程间通信
uchar tkbt_[TK]={0};
//---------------线程入口函数
UINT WINAPI  pic_ex(LPVOID lpParam)
{//显示输出控制
 while(1)
 {
  if((Thread_Exit[0]||tk_pic[1])&&(tk_pic[0]))
  {   //坦克大炮位置数组首地址,BASE基址数组首地址
     tank0.tank_dis(0);//不产生申请只装值,此时是被动式显示,如发送请求则是主动式显示
     if(!Thread_Exit[0])  tank_0.tank_dis(0);
     picture();
     tk_pic[0]=0;tk_pic[1]=0;
  }
 }
 return 0;
}
UINT WINAPI bullet_con(LPVOID lpParam)
{
  BULLET bullet0,bullet1;
  while(1)
  {
   if(tkbt[0])  {  tkbt[0]=0;bullet0.bullet_dis(tank0); }
   //if(tkbt[1])  {  tkbt[1]=0;bullet1.bullet_dis(tank1);}
  }
  return 0;
}
UINT WINAPI tank_enemy(LPVOID lpParam)

 while(!Thread_Exit[0])
 {
     tank_0.enemy_autogo(); 
 }
    return 0; 
}
UINT WINAPI  tank_soldier(LPVOID lpParam)
{  
 tkbt[0]=tkbt[1]=0;
    tank0.tank_dis(1);
    //tank1.tank_dis(1);
    while(!Thread_Exit[1])
 {
     tank0.tank_dis(1);
 }
 return 0;
}
UINT WINAPI global_con(LPVOID lpParam)
{
 Thread_Exit[1]=1;
 while(1)
 {
    uchar c;
    if(kbhit())
    {
        c=getch();
     if(c==0X0D)  {   tkbt[0]=1;  }
     //if(c=='q'||c=='Q') {tkbt[1]=1;}
     tank0.tank_con(c);
     //tank1.tank_con(c);
     c='\0';
    }
    /*if(Thread_Exit[0])  
    {
     Sleep(30);Thread_Exit[0]=0;
     hThread[3]=(HANDLE)::_beginthreadex(NULL,NULL,tank_enemy,NULL,0,NULL);
    
    }     */
    tank0.tank_dis(1);
 }
 return 0;
}
int main(int argc, char* argv[])
{
    //---------------
 hThread[0]=(HANDLE)::_beginthreadex(NULL,NULL,pic_ex,NULL,0,NULL);//立即执行的线程
 hThread[1]=(HANDLE)::_beginthreadex(NULL,NULL,global_con,NULL,0,NULL);
 hThread[2]=(HANDLE)::_beginthreadex(NULL,NULL,bullet_con,NULL,0,NULL);
    hThread[3]=(HANDLE)::_beginthreadex(NULL,NULL,tank_enemy,NULL,0,NULL);
    hThread[4]=(HANDLE)::_beginthreadex(NULL,NULL,tank_soldier,NULL,0,NULL);
    //-----------------------
 ::WaitForMultipleObjects(5,hThread,TRUE,INFINITE);
 ::CloseHandle(hThread[0]);
 ::CloseHandle(hThread[1]);
 ::CloseHandle(hThread[2]);
 ::CloseHandle(hThread[3]);
 ::CloseHandle(hThread[4]);
    //---------------------
 return 0;
}

//下面是另外一个包含文件,我个人感觉他的功能很像console下的一个“显卡”,是我自己呕心沥血写成的,

//我写的控制台游戏都使用了这个头文件,当然除了自娱自乐没什么实际的意义。

 

//pic.h是从picture.h提取出使用的部分代码组成的头文件,主要因为picture.h太乱,不方便在别的程序里引用
#include<stdio.h>
#include<conio.h>
#include<stdlib.h>
#include<windows.h>
#define MAX  3200
typedef unsigned char uchar;
//----------------------结构体化------------------------------------------
typedef struct Pic{
  uchar x[MAX];
  uchar y[MAX]; //x,y为坐标
  uchar z[MAX];// pixel形状及颜色信息
  int pnum;
}PIC;//pixel的坐标为非负,但排序的序号i,j应设为int,插入排序时,负数为退出循环的界限
 //-----------------------------------------------------------------------
PIC p0={
{0},
{0},
{0},
0}; //未赋值的内存自动赋0
 PIC pt={
{0},
{0},
{0},
0}; //采用本地备份数据显示方式,不破坏原数据

//-----------------
void point_ex(int x,int y,char*p)//起始点(0,0)
{
 int i,dx=0,dy=0;
 static int x0=0,y0=0,j=1;//x0,y0表示当前行和当前列
 if(x==-1&&y==-1)  {x0=y0=0;j=1;}//用point(-1,-1)刷函数-相对于(0,0)点开始打点,未刷屏则相对于
 //当前点,一般用于重新绘图时
 if(x==0&&y==0&&j==1) { j=0; printf("%s",p);return;}
 if(x==0&&y==0&&j==0)  return;//打两次(0,0)时略去第二个
 dx=x-x0;
 if(!dx) dy=y-y0;  //dx=0,同一行
 else   dy=y;
 if(dy<0||dx<0)  return;
 if(x==0&&y!=0&&dy==y&&j)    printf("  ");//这个if语句的意思是:锁定第零行,除去第零列
 //所打的第一个点且保证未打(0,0)时,执行后面的语句
 if(dy==0&&y!=0)   return;//滤去空行以及相同的点
 for(i=0;i<dx;i++)     putchar('\n'); //i=0第一行为空白行
    if(dx&&y!=0)       printf("  ");//换行时如果这一行的第零列没打的话,则自动加一列
    for(i=1;i<dy;i++)      printf("  ");//打完一个点后光标已移到下一个点,因此dy=1时,这条语句应该不执行
    //putchar(4);
 //--------判断蛇头---------
     printf("%s",p);
 //---------------
 x0=x;
 y0=y;
}//注:每次打的点必须在前一个点后面,且第一次调用时必须先清屏
// ⊙★○▲△▽○◇□■☆♀♂㊣╲▕╱╲▏▏卐卍♀◢◥◤
//------------------------------------------ 
//-----------------
void pic_copy(uchar*,uchar* ,uchar *,int);
void pic_sort(uchar*,uchar*,uchar*,int);
void pic_debug()
{
 int i;
    //-----debug view----------
    printf("\n---------debug---------------\n");
 for(i=0;i<p0.pnum;i++)
 printf(i%18==17?"%d\n":"%d\t",p0.x[i]);
 printf("\n----------------------------\n");
    for(i=0;i<p0.pnum;i++)
 printf(i%18==17?"%d\n":"%d\t",p0.y[i]);
    putchar(10); 
}
void picture(void)
{
   int i;
   pic_copy(p0.x,p0.y,p0.z,p0.pnum);
   pic_sort(pt.x,pt.y,pt.z,pt.pnum);
   //------刷新--------
      point_ex(-1,-1,NULL);
      system("cls");
   for(i=0;i<pt.pnum;i++)
      {
    switch(pt.z[i])
    {
        case 1: point_ex(pt.x[i],pt.y[i],"■");
     case 2: point_ex(pt.x[i],pt.y[i],"||");
     case 3: point_ex(pt.x[i],pt.y[i],"--");
     case 4:
      {
       ::SetConsoleTextAttribute(::GetStdHandle(STD_OUTPUT_HANDLE),7);
          point_ex(pt.x[i],pt.y[i],"○");
       ::SetConsoleTextAttribute(::GetStdHandle(STD_OUTPUT_HANDLE),14);
      }
    }
   }
   putchar(10);    
   //pic_debug();
}
void pic_copy(uchar*tx,uchar*ty,uchar *tz,int pnum)
{
    int i;
 if(pnum==0)   return;
 pt.pnum=pnum;
 for(i=0;i<pnum;i++)
 {
      pt.x[i]=tx[i];
   pt.y[i]=ty[i];
   pt.z[i]=tz[i];
 }
 return;
}
//--------整理pic-------
void  pic_sort(uchar*tx,uchar*ty,uchar *tz,int pnum)//在原来的插入排序法上,使ty的值跟着tx的值移动,对于pixel进行整理
{
    int h;
 int i,j,t,k;
 int m,n,l;
    if(pnum==0)   return;
 //----第一步排tx(并保持tx与ty之间的对应关系)---
 for(i=1;i<pnum;i++)//只对数组赋值的区域排序
 {
  t=tx[i];
  k=ty[i];h=tz[i];
  for(j=i-1;j>=0;j--)
   if(t<tx[j]) {  tx[j+1]=tx[j];ty[j+1]=ty[j];tz[j+1]=tz[j];  }
   else break;
  tx[j+1]=t;
  ty[j+1]=k;tz[j+1]=h;
 }
 //--------第二步逐行排ty----------
 for(i=tx[0],j=0;i<=tx[pnum-1];i++)//从第tx[0]到tx[p0.pnum-1]行 
 { 
  l=0;  //j的最大值不能超过p0.pnum
  while(tx[j]==i)
     //此处只依靠tx[*]的值去做判断,容易出现超出数组寻址范围的情况,因为如果对于未定义的内存空间(此处是明确的,tx后的地址就是ty的地址),其值虽然相对
  //不变,但总体(理论上)应该是不确定的(这可能取决于各自的机器),因此尽管超出规定的范围,仍然有可能
  //满足这个判别式,从而造成寻址越界的情况
  {  
            if(j==p0.pnum)   break;//后来加的限制条件,没有它会出现一系列的奇葩现象
   j++;l++;
  }//得到tx[*]行的点数l
  //当tx[*]全赋0且ty[0]=0,此处寻址越界,由于tx[*]寻址越界后,寻到了ty[*]处,他们地址是连续的,
  //而此时的ty[0]正好满足ty[0]=tx[0]=0,这就造成了寻址越界,因此加上一条限制语句(如上),
  //也证明上面的猜想是正确的
  if(l==1||l==0)  continue;
        //*************************************
  for(m=j-l;m<j-1;m++)
  {
   k=m;
   for(n=m+1;n<j;n++)
    if(ty[n]<ty[k])  k=n;
      if(k!=m)
   {
     t=ty[k];ty[k]=ty[m];ty[m]=t;//ty移动
     t=tz[k];tz[k]=tz[m];tz[m]=t;//随ty移动tz
   } 
  }
  //************************************
  //必须说明被*号拦起来的代码相当奇葩,其中明显可以发现,程序并没有对p0.pnum进行赋值,
     //但其值其出现了奇葩式的间断连续改变,这个或许和上面寻址越界有关,毕竟po0.x[*]、p0.y[*]、p0.pnum
  //定义在一起(同一个结构体中),它们的地址应该是连续的
 }
 //当p0.pnum=3200时,由于寻址越界,它被当作了ty来进行排序,由上面分析可知,当时j=3021,越界一位正好
 //p0.pnum被加进来了,由于是用ty[3200]的形式加进来的,所以它的类型就由int变为了char,可以计算3200/256=12.5
 //也就是说,经过12次循环后其值变为0.5*256=128,而由于是char型,其正数最大为127,对应于-128,此时最小
 //这也就是为什么会在pic_debug()中出现一个负数的原因,同时也解释了为什么将结构体PIC中数组改为uchar后,
 //又出现了正确的pic_debug()界面(其值最大被放到了最后,也就是没有变动)。
 //最后int型的p0.pnum排到了char型里,char型的9排到了int型里,由于p0.[3200]的方式只调走了int型内存区域的256以内(即一个字节内)的数值
 //还有12*256保留在原处,所以会出现p0.pnum=12*256+9=3081的情况,,到此所有的谜团都解开了。
 //而问题的关键仅仅是一个寻址越界造成的,现做两点修改:
 //1.加入如上所示的限制条件
 //2.将PIC结构体中的数组改为uchar,因为我们不打出负点
}
//----使用帮助
//1.先为结构体PIC赋值,将一个个要显示的点装入数组,不分先后无需排序,只要x,y对应正确即可
//2.更新p0.pnum为当前总共要显示的pixel数
//3.直接调用picture()即可
//4.对于picture()的调试,一般采用pic_debug()函数找出问题所在

 

C++ | 阅读 1237 次
文章评论,共1条
时光易逝wh(作者)
2014-01-08 00:07
1
我必须承认这是一个失败的例子,相当失败.............
游客请输入验证码