九天雁翎的博客
如果你想在软件业获得成功,就使用你知道的最强大的语言,用它解决你知道的最难的问题,并且等待竞争对手的经理做出自甘平庸的选择。 -- Paul Graham

正则表达式测试器0.3(Boost Regex Tester 0.3) (老版保留)

主要功能是加入了替换模式,去掉了boost库的结果列表,有需要的可以去下载0.2,另外因为菜单太复杂,所以最后搞的都崩溃了,干脆不要菜单了。而且我发现用对话框作为客户区的程序实在是不太好调整,下一版本还是用普通的view类做客户区算了。 

使用方法上可以参考boost的在线文档,因为程序完全利用boost库制作,这个版本甚至取消了一些设置选项。

从这个角度来说,0.2在匹配上功能还要更强一些,但是替换的作用还是有的。

还是去http://groups.google.com/group/jiutianfile/files 下载程序及源代码

我本来想做的菜单如下:

程序截图如下:

阅读全文....

正则表达式测试器0.2(Boost Regex Tester 0.2)(老版保留)

昨晚痛苦的写了一个0.2版的程序,为什么痛苦?其实处理一些字符串,完成一些简单的菜单功能,让匹配的规则可以定义都不是什么难事。痛苦的是CEdit中用SetSel选择了文本以后MFC不会自动的为本文加亮,需要手动完成,我完成的想自杀,最后终于算是能够处理一排文本了,不过要处理多行文本还有待再努力,估计其中又会想自杀n次。没有想通这么我想都没有想到,很自然的问题,微软竟然不管,我发现这个问题都用了半天,总以为自己的SetSel索引搞错,哎。。。。难怪人都说MFC复杂。有高手知道怎么简单的处理SetSel文本加亮问题的请指教。最好自己先试试,网上这个问题我看了很多,用CtlColor函数响应反射消息的方法也试过,不过好像没有办法处理一段特定的文本。最后只能用最最原始的方法手动完成。痛苦。痛苦。

   不过0.2版的程序倒是感觉还比较满意,大家试试,一般的正则表达式学习和验证估计都够了,而且在用Boost编程之前用这个先试一下可以节省很多编译的时间,我当时就是为这个而编的这个程序。

   编译好的文件和源代码都在老地方http://groups.google.com/group/jiutianfile/files

有下,因为是MFC程序,比较大,我一般不列出来了。来个程序截图。

阅读全文....

正则表达式测试程序(Boost Regex Tester)0.1(老版保留)

在昨天我编写了一个制作图书检索的小程序后,真的发现人们说的话太正确了。程序员主要处理的就是两件事,数据库,字符串。在那么一个小程序里,我已经用上了STL的很多特征,而且基本操作起码都在string级别以上,但是还是繁复的要命。所以终于下定决心要学习一下极为有用却堪称天书的正则表达式。在学习的过程中用VS .NET IDE中的搜索实践了一下,发现还是不太方便,而且微软的东西好像不是很保险(主要指规范性上)。并且在C++中要用到正则表达式好像只能用boost库,那么我当然最好是熟悉boost库中的正则表达式的法则啦,干脆,做了一个利用boost实现的测试程序得了。

最后发现,正确的使用boost.regex 难度远远比正确使用regex还高,我没有见过设计的这么复杂的类和函数。硬是仔细的看了几遍在线文档才摸清头绪,最终好不容易完成了这个小程序。使用上没有什么好说的,上面输入正则表达式,下面为查找的字符串。另外,对于此boost库还有很多不明白的,比如不知道匹配的字符串在原字符串的位置怎么定位?

正则表达式 /bsss/b 可以匹配 ssss sss 中的最后三个sss,可是我怎么能从regex_search函数返回值中得到这样的结果呢?因为match_results中并没有包含位置信息,只有最后查到的字符串而已。因为这点不明,所以我没有办法把程序做成Windows中查找程序那样的结果表示方式。

编译好的程序和源代码都还是在http://groups.google.com/group/jiutianfile/files中有下载

程序截图如下:

阅读全文....

图书索引创建器

国外的C++图书很多有索引,这样使得这些书在看完后还有很大的参考价值,像《TC++PL》,《C++ Primer》等经典著作无一不是,像《The C++ Standard Library》一书更是因为书中交叉索引丰富而出名,也使得其成为经典之一。而国内的大部分书,国外一些经典著作都没有索引,这样在看完后要回过头来找一些资料不是很方便,这点在我看《Programming Windows with MFC》中感受最深,所以我决定写一个这样可以自己方便创造出索引的程序,对于网上很多看图形类电子书的查找更不方便的朋友,这个程序的作用应该会更大吧。这个程序特意用了wstring来表达字符串,以方便中文的处理,虽然个人感觉很多时候用string的确也可以处理中文。很简单的一个创建图书索引工具,用户只需要注意用以结束的’!’必须是英文标点,其次就是确保读入的文件的确是由此程序创建或者负责其创建的格式,不然运行结果得不到保证。假如每个人都为自己的书创建索引未免麻烦,但是假如大家愿意共享大家创建后的结果,那么人人都可以用,就像共享资源一样。另外,希望假如有人对其修改的话保证原有索引的可用性,即向下兼容。假如程序本身有更好的处理方法,起码提供一个可以转换原有文件的程序。谢谢使用。你可以在http://groups.google.com/group/jiutianfile/files找到编译好的文件下载。

使用方法为BookIndexCreator filename.txt [ -s -i -c ]

filename.txt 参数为想处理的文件名

-s   参数表示在filename.txt中查找索引。

-i    参数表示在filename.txt中插入索引。

-c   参数表示在filename.txt中创建索引。

假如文件名中有空格,应该用括号括起来。以上三个参数同时只能用一个。

 

以下为源代码:

// ================================================================
//
//  Copyright (C) 2007  九天雁翎
//
//  \---------------------------------------------------------------
//  这是一个开源的免费软件,希望对你有用或对你的学习有帮助,你可以
//  在GNU General Public License的协议下对它进行任何修改,本人不对
//  该软件运行造成的问题承担任何责任。
// ################################################################
//
//  作者: 九天雁翎
//    软件: 图书索引创建器(Book Index Creator)
//  文件: BookIndexCreator.cpp
//    版本: 0.1
//    描述:
//               很简单的一个创建图书索引工具,用户只需要注意用以结束的'!'必须是英文
//    标点,其次就是确保读入的文件的确是由此程序创建或者负责其创建的格式,不然运行
//    结果得不到保证。假如每个人都为自己的书创建索引未免麻烦,但是假如大家愿意共享
//    大家创建后的结果,那么人人都可以用。另外,希望假如有人对其修改的话保证原有索
//    引的可用性,即向下兼容。假如程序本身有更好的处理方法,起码提供一个可以转换原
//    有文件的程序。谢谢使用。
//  Download Webs: groups/google.com/group/jiutianfile
//  Blog: blog.csdn.net/vagrxie
//  E-mail: vagr@163.com
//  QQ      : 375454
//
//  欢迎大家在上述网页发帖或来信探讨,或说明该软件的BUG和可以改进之处
//
//    最后修改时间: 2007年月日
// ################################################################
 
#include <iostream>
#include <iomanip>
#include <fstream>
#include <string>
#include <cstring>
#include <algorithm>
#include <map>
#include <set>
#include <boost/lexical_cast.hpp>  
using namespace std;
 
wfstream file; //主文件
enum argumentType{TYPEC,TYPEI,TYPES};  //确定参数类型
 
///////////////////////////////////////////////////////////////////////////////
//输出帮助内容
///////////////////////////////////////////////////////////////////////////////
 
void printHelp() 
{
      cout << "How to use the BookIndexCreator:/n"
           << "bic filename.txt [ -s | -i | -c ]/n"
           << "No argument: Display like this.(the same to ?)/n"
           << " -s      Search a index from the filename.txt/n"
           << " -i       Insert more index to the filename.txt/n"
           << " -c           Create a new index as the filename.txt/n"
           << " Notice: As a tradition,if the filename has any space you must bracket it."
           << "**********************************************************************/n"
           << "Let's make books more useful!/n"
           << "You can search a index in any text editors after it created"<<endl;
}
 
///////////////////////////////////////////////////////////////////////////////
//检验输入的Y/N
///////////////////////////////////////////////////////////////////////////////
 
bool checkYN(string wrongInformation)
{
      string tempInput;
      cin >> tempInput;
      transform(tempInput.begin(),tempInput.end(),tempInput.begin(),tolower);
      while(tempInput != "y" && tempInput != "yes")
      {
           static int i = 0;
           if(tempInput == "n" || tempInput == "no")
           {
                 return false;
           }
           cout <<wrongInformation <<"? (Yes/No)" <<endl;
           if(++i > 5) //允许重试的次数
           {
                 return false;
           }
           cin >> tempInput;
      }
      return true;
}
 
///////////////////////////////////////////////////////////////////////////////
//识别输入的参数
///////////////////////////////////////////////////////////////////////////////
 
argumentType checkArgument(int argc, char *argv[])
{
      if(argc < 3)
      {
           printHelp();
           exit(EXIT_FAILURE);
      }
      string filename(argv[1]);
      if(!strcmp(argv[2], "-c") || !strcmp(argv[2], "-C"))
      {
           ifstream ifile(filename.c_str()); //临时文件检验是否已有文件
           if(ifile)
           {
                 ifile.close();
                 cout<<"There was exist a same name file,Overwrite "<<filename<<"? (Yes/No)"<<endl;
                
                 //不覆盖就只能退出程序
                 if(!checkYN("There was exist a same name file,Overwrite"))
                 {
                      cerr << "Haven't Created file!" <<endl;
                      exit(EXIT_FAILURE);
                 }
           }
 
           //打开文件
           file.open(filename.c_str(), ios::out | ios::trunc);
           if(file)
           {
                 cout << "Created an index named /""<<filename<<"/""<<endl;
           }
           else
           {
               cerr << "Created file error!" <<endl;
                 exit(EXIT_FAILURE);
           }
           return TYPEC;     //返回参数确定的类型,表示参数为-c
      }
      else if(!strcmp(argv[2], "-i") || !strcmp(argv[2], "-I"))
      {
           file.open(filename.c_str());
           if(!file)
           {
                 cerr << "There wasn't a file named " <<filename <<"!"<<endl;
                 exit(EXIT_FAILURE);
           }
           return TYPEI; //返回参数确定的类型,表示参数为-i
      }
      else if(!strcmp(argv[2], "-s") || !strcmp(argv[2], "-S"))
      {
           file.open(filename.c_str(), ios::in);
           if(!file)
           {
                 cerr <<L"There wasn't a file named " <<filename <<"!"<<endl;
                 exit(EXIT_FAILURE);
           }
           return TYPES;      //返回参数确定的类型,表示参数为-s
      }
      else      //参数不正确
      {
           printHelp();
           exit(EXIT_FAILURE);
      }
}
int main(int argc, char *argv[])
{
      switch(checkArgument(argc,argv))
      {
 
///////////////////////////////////////////////////////////////////////////////
//当参数为-c时,进行文件的创建
///////////////////////////////////////////////////////////////////////////////
 
           case TYPEC:
                 {
 
                      typedef map< wstring, set<int> >::iterator MapIter;
                       typedef set<int>::iterator SetIter;
                      map< wstring, set<int> > index;
                      cout<<"Input index like /"xxxxx 123/" type/n"
                            <<"xxxxx means the content wanted to be searched./n"
                            <<"123 means the xxxxx's page number in the book./n"
                            <<"And end inputing by '!'"<<endl;
                      wstring content;
                      int pageNumber;
 
                      //重复输入
                      while(true)
                      {
                            while(wcin >>content)
                            {
 
                                  //假如输入为'!'结束
                                  if(content == L"!")
                                  {
                                       break;
                                  }
 
                                  //预防输入错误,当输入错误时退出
                                  if( !(wcin >>pageNumber))
                                  {
                                       break;
                                  }
                                  set<int> setNumbers;  //以set来保存页码,自动排序及去除重复
                                 
                                  //假如已有此索引key,先将set初始化为已有值
                                  MapIter pos = index.find(content);
                                  if(pos != index.end())
                                  {
                                       setNumbers = pos->second;
                                  }
                                  setNumbers.insert(pageNumber);
 
                                  //添加索引,假如原来有则改变值为新增页码的set
                                  index[content] = setNumbers;
                                  cout<<"OK,inserted,input next:" <<endl;
                                  content.clear();
                            }
                            wcin.clear();    //输入错误则允许重新输入
 
                            //假如输入为'!'结束
                            if(content == L"!")
                            {
                                  break;
                            }
                            else      //不然将输入错误的数据再读一次,然后清理,以便重新输入
                            {
                                  wcin >> content;
                                  content.clear();
                            }
                            cout <<"Wrong input last line and continue:" <<endl;
                      }
 
                      //写入文件
                      for(MapIter iter = index.begin(); iter != index.end(); ++iter)
                      {
                            file <<left <<setw(40)<<iter->first ;
                            for(SetIter pos = iter->second.begin(); pos != iter->second.end(); ++pos)
                            {
                                  file <<*pos <<L",";
                            }
                            file <<endl;
                      }
                      break;
                 }
 
///////////////////////////////////////////////////////////////////////////////
//当参数为-i时,进行索引的插入
///////////////////////////////////////////////////////////////////////////////
 
           case TYPEI:
                 {
                      typedef map<wstring, wstring>::iterator MapIter;
                      map<wstring, wstring> index;
                      wstring content;
                      int pageNumber;
                      wstring strNumbers;
 
                      //先读入原来的文件
                      while(file >>content >>strNumbers)
                      {
                            index[content] = strNumbers;
                            static int progress = 0;
                            ++progress;
                            if( !(progress % 50) )
                            {
                                  cout<<".";
                            }
                      }
                      cout<<endl;
                      cout<<"OK,read file success,input content you want to insert:" <<endl
                            <<"Input index like /"xxxxx 123/" type/n"
                            <<"xxxxx means the content wanted to be searched./n"
                            <<"123 means the xxxxx's page number in the book./n"
                            <<"And end inputing by '!'"<<endl;
 
                      //重复输入
                      while(true)
                      {
                            while(wcin >>content)
                            {
 
                                  //假如输入为'!'结束
                                  if(content == L"!")
                                  {
                                       break;
                                  }
 
                                  //输入错误中止
                                   if( !(wcin >>pageNumber))
                                  {
                                       break;
                                  }
 
                                  //是否已有同样的索引key,有则改为添加
                                  MapIter pos = index.find(content);
                                  if(pos != index.end())
                                  {
                                       set<int> intSet;  //用临时set保存,自动排序,自动去重复值
 
                                       //以下为输入的以','分割的wstring转换为int的过程
                                       wstring::size_type idx1 = 0;
                                       wstring::size_type idx2 = pos->second.find(L',');
                                       while(idx2 != wstring::npos)
                                       {
                                             intSet.insert(boost::lexical_cast<int>(pos->second.substr(idx1,idx2 - idx1)));
                                             idx1 = idx2 + 1;
                                             idx2 = pos->second.find(L',',idx1);
                                       }
 
                                       //转换完后添加新的页码
                                       intSet.insert(pageNumber);
                                       strNumbers.clear();
 
                                       //临时set再转换为以','分割的wstring,比较麻烦
                                       for(set<int>::iterator iter = intSet.begin();
                                               iter != intSet.end(); ++iter)
                                       {
                                             strNumbers.append(boost::lexical_cast<wstring>(*iter));
                                             strNumbers.push_back(',');
                                       }
                                  }
 
                                  //索引中没有同样的key,直接添加页码
                                  else
                                  {
                                       strNumbers = boost::lexical_cast<wstring>(pageNumber);
                                  }
                                  index[content] = strNumbers;
                                  cout<<"/nOK,inserted,input next:" <<endl;
                                  content.clear();
                                  strNumbers.clear();
                                  pageNumber = 0;
                            }
                            wcin.clear();  //保证可以重复输入
 
                            //假如输入为'!'结束
                            if(content == L"!")
                            {
                                  break;
                            }
                            else
                            {
                                  wcin >> content;   //清除错误输入,以方便接下来的输入
                                  content.clear();
                            }
                            cout <<"Wrong input last line and continue:" <<endl;
                      }
 
                      //清理文件,假如再次打开文件失败,原有索引丢失,恐怖的问题
                      file.clear();
                      file.close();
                      file.open(argv[1], ios::out | ios::trunc);
                      if(!file)
                      {
                            cerr<<"file input error"<<endl;    //出现这个表示原有索引丢失,待改进
                            exit(EXIT_FAILURE);
                      }
 
                      //将新增索引输入
                      for(MapIter iter = index.begin(); iter != index.end(); ++iter)
                      {
                            file <<left <<setw(40) <<iter->first <<iter->second <<endl;
                      }
                      break;
                 }
 
///////////////////////////////////////////////////////////////////////////////
//当参数为-s时,进行索引的查找
///////////////////////////////////////////////////////////////////////////////
 
           case TYPES:
                 {
                      typedef map<wstring, wstring>::iterator MapIter;
                      map<wstring, wstring> index;
                      wstring content;
                      wstring strNumbers;
 
                      //原文件读入
                      while(file >>content >>strNumbers)
                      {
                            index[content] = strNumbers;
                            static int progress = 0;
                            ++progress;
                            if( !(progress % 50) )
                            {
                                  cout<<".";
                            }
                      }
 
                      cout<<endl;
                      cout<<"OK,read file success,input content you want to search:" <<endl
                            <<"and end input by '!'." <<endl;
                      MapIter pos;
 
                      //重复输入
                      while(true)
                      {
                            content.clear();
                            wcin >>content;
 
                            //输入为'!'结束
                            if(content ==L"!")
                            {
                                  break;
                            }
 
                            //查找
                            pos = index.find(content);
 
                            //查找失败处理
                            if(pos == index.end())
                            {
                                  wcout<<L"There isn't the index included " <<content <<L"." <<endl
                                  <<L"Input next:" <<endl;
                                  continue;
                            }
 
                            //查找成功,输出页码
                            wcout <<left <<setw(40) <<pos->first <<pos->second.substr(0, pos->second.size() - 1 ) <<endl
                                  <<L"/nInput next:" <<endl;
                      }
                      break;
                 }
           default:
                 break;
      }
      
      //程序结束
      cout<<"OK,this program ended."<<endl;
      file.close();      //不忘关闭文件,似乎已无必要
      return 0;
}

阅读全文....

C++学习之道

C++学习之道

 

学习C++有一段时间了,其中走了不少弯路,也的确了解了很多学习的方法,今天回过头来回顾一下,以示后来者。

 

学习大的方针:具体方法,使用书籍很多人都讲过,也讲的很详细,但是大的方针却时常看到有讨论,比如有的人提倡多看书,有的人提倡直接编,利用书为参考,甚至有的人说他编程就是从MSDN上学的。这些问题我也曾迷茫过,也曾偏激过,当学了很久却感觉没有什么收获,结果一天的实际编程却感觉收获匪浅的时候我曾以为学习编程不就是人们说的“编!编!编!”,“学习编程就是去编” 吗?于是丢开数本,投入自己感兴趣的东西的开发。可是编程碰到疑惑之处网上四处求解,CSDN,google USENET上四处发帖询问,却常常难以释疑之时,有的时候看了看书,却豁然开朗,原来大师们都已经对此问题习以为常,并当常识讲解出来,一时大叹,读一本好书真是如拨云雾。又常常长时间看书,让实际编程荒废。

 

      现在回过头来想想,常常是矫枉过正,徘徊在编与学之间。孔子谈学习之道:“学而不思则罔,思而不学则殆。”其实可以套用到C++的学习中来,那就是“编而不学则罔,学而不编则殆”。也就是说,光去编程而忽视了学习,就会学编越糊涂,光是学习,却不去实际编写程序,那么学到后面忘了前面,相当于什么都没有学到。个人提倡是将所有的时间都对半分,一半时间用来仔细研读书本对照实践,一半时间用来对你自己感兴趣的题目进行实际的编程。这样一方面保证了学习进度,让你感觉到每天的收获,而且能在实际编程中找到成就感,运用所学并积累解决实际中碰到问题的经验,两者互为补充。这样才是最好的学习C++的方法,应该也适用于所有的编程当中。

 

对于是否先学C语言是有很多争议的,个人认为看情况。看个人想法,志向。目标只在编写日常普通应用程序的,可以不学C,甚至个人感觉学C++也不见得好,直接去学JAVA,C#或者Python这样的脚本语言都要更好,但是对于目标在编写效率较高的程序,如系统开发的话,那么学习之路坎坷异常,别的不说,个人推荐首先从C学起,并且一边学习一些基础知识,比如电脑硬件系统相关的知识,甚至基础的汇编语言,甚至操作系统原理,编译原理。当然个人推荐数学学好,以方便研究算法。同时在C时代就开始学习各类算法,假如要学windows编程的话这时也可以学习Windows SDK编程了。因为C++对于算法的学习和windows编程的理解不见得有甚好处。这时最困难的时期,也是决定你真正将来的时期,毕竟这些都是非常基础的,却也是非常关键的东西。就像建房高度关键在于地基牢不牢固一样。当然,假如已经错过了这样的时期,已经学习了C++和MFC的话,那么将来结束完一段时间的C++,MFC学习,也推荐回头看看算法知识和windows SDK。有需要加快简单程序的编写,后期开发windows程序,利用C#.Net技术,稍微的学习一下应该不难了。个人感觉如此,我从不知道C#是什么,尚能编写蹩脚的.Net程序,对于掌握了这样的基础,再学习过C#的话,编写起来应该不成问题。当然有人也说可以用C++编写.NET程序,说实话个人也有过这样的尝试,但是总感觉大堆的::和^不如C#来的方便,既然都已经到这份上了,为什么不直接用C#算了呢?

 

后面的一大堆都是个人初步的经验,不一定正确,前面的实在是个人的实际经验。希望对大家有帮助。

阅读全文....

多鼠标技术的应用,请有多个鼠标的兄弟帮我测试一下

最近对于一台电脑接多个鼠标有点兴趣,从框架软件到自己实现都进行了学习。自己从底层代码写了几个软件,用来读多鼠标数据和绘制指针,并且将以前那个五子棋改成两个鼠标一起玩的版本了,到http://groups.google.com/group/jiutianfile/files可以找到下载请大家帮我测试一下看看有没有BUG,对了,五子棋只能用两个鼠标,其他可以尽量多接。具体源代码以后放上来,包括我对多鼠标技术的理解。

阅读全文....

在Jeff Prosise井字棋的基础上做的一个五子棋

在Jeff Prosise井字棋的基础上做的一个五子棋,全手工代码输入,利用MFC实现,因为尚不知如何手工添加按钮,

所以暂时重新开始游戏的方式为双击标题栏。可以到http://groups.google.com/group/jiutianfile下载编译好的文件和Visual Studio.net 2005工程源代码。

源代码gobangSrc.rar,编译好的文件gobangRel.rar

头文件gobang.h

class CMyApp : public CWinApp
{
public:
      virtual BOOL InitInstance();
};

 

class CMainWindow : public CFrameWnd
{
protected:
      enum gridState { Unputed, PutedO, PutedX};     //enum格子的个状态
      enum Turn {OTurn,XTurn};  //enum轮到谁下的状态
      enum winnerLast {NoOne,OWin,XWin};      //enum有没有胜利者的状态
      Turn m_nextTurn; //下轮执棋者
      const static int nClientSize = 700; //客户区大小,可改变
      const static int nGridNum = 20;         //格数,可改变
      int m_countStep;  //目前所下步数
      void DrawBoard(CDC &dc); //画棋盘
      CRect m_rcGrid[nGridNum][nGridNum];     //棋盘的每个格子的矩形范围
      gridState m_stateGrid[nGridNum][nGridNum];   //棋盘每个格子的状态
      void DrawO(CDC &dc,int i,int j);      //下O
      void DrawX(CDC &dc,int i,int j); //下X
      void ResetGame();     //重新开始游戏
      void CheckForGameOver(Turn thisTurn,int i,int j); //查找需不需要结束游戏
      BOOL IsWinner(Turn thisTurn,int i,int j);      //是否有胜利者
      BOOL IsDraw();   //是否平局


public:
      CMainWindow();

protected:
      afx_msg void OnPaint();
      afx_msg void OnLButtonDown(UINT nFlags,CPoint point);
      afx_msg void OnNcLButtonDblClk(UINT nHitTest,CPoint point);
      afx_msg void OnRButtonDown(UINT nFlags,CPoint point);
      afx_msg BOOL OnSetCursor(CWnd *pWnd,UINT nHitTest, UINT message);
      DECLARE_MESSAGE_MAP()
};

主体文件gobang.cpp

#include <afxwin.h>
#include <cmath>
#include <memory>
#include "resource.h"
#include "gobang.h"

CMyApp myApp;

//

//CMyApp的成员函数
//

BOOL CMyApp::InitInstance()
{
      m_pMainWnd = new CMainWindow;
      m_pMainWnd->ShowWindow(m_nCmdShow);
      m_pMainWnd->UpdateWindow();
      return TRUE;
}

//

//CMainWindow的消息映射和成员函数定义
//

BEGIN_MESSAGE_MAP(CMainWindow,CFrameWnd)
      ON_WM_PAINT()
      ON_WM_LBUTTONDOWN()
      ON_WM_NCLBUTTONDBLCLK()
      ON_WM_RBUTTONDOWN()
      ON_WM_SETCURSOR()
END_MESSAGE_MAP()


CMainWindow::CMainWindow()
{
      //执圆形的先下棋,光标为圆型,并定下窗口固定宽度
      CString wndClassStr = ::AfxRegisterWndClass(CS_DBLCLKS,
           ::AfxGetApp()->LoadCursor(IDC_round));
      Create(wndClassStr,_T("五子棋by九天雁翎       (双击标题栏重新开始游戏)"),
           WS_OVERLAPPED | WS_SYSMENU | WS_CAPTION | WS_MINIMIZEBOX);
      CRect rect(0,0,nClientSize+2,nClientSize+2);
      CalcWindowRect(&rect);
      SetWindowPos(NULL,0,0,rect.Width(),rect.Height(),
           SWP_NOZORDER | SWP_NOMOVE | SWP_NOREDRAW);
     
      //初始化自定义的成员变量
      m_nextTurn = OTurn;
      m_countStep = 0;
     
      ::ZeroMemory(m_rcGrid,nGridNum * nGridNum * sizeof(CRect));
      ::ZeroMemory(m_stateGrid,nGridNum * nGridNum * sizeof(gridState));
     
 
 
 
}

void CMainWindow::OnPaint()
{
      CPaintDC dc(this);
      DrawBoard(dc);
}

void CMainWindow::DrawBoard(CDC &dc)
{
      CRect rect;
      GetClientRect(&rect);

      //画背景
      CBrush brush(RGB(128,128,128));
      dc.FillRect(rect,&brush);

//开始画纵横线,利用rect大小来画线,一是代码重用度高,二是以前做好了
//效率稍微低点,不过将来要是改变客户区大小或用可缩放窗口就可以不改了
      
      //定义画线的画笔并选中
      CPen pen(PS_SOLID,2,RGB(0,0,0));
      CPen *pOldPen = dc.SelectObject(&pen);

      //求方格宽高
      int nGridWidth = nClientSize / nGridNum  ; 
      int nGridHeight = nClientSize / nGridNum ;
      

      //计算每个方格矩形范围
      for(int i = 0; i < nGridNum; ++i)
           for(int j = 0; j < nGridNum; ++j)
           {
                 m_rcGrid[i][j] = CRect(rect.left + (nGridWidth * j),
                                        rect.top + (nGridHeight * i),
                                        rect.left + nGridWidth +(nGridWidth * j),
                                        rect.top + nGridHeight + (nGridHeight * i));
           }


      for(int i = 0; i <= nGridNum; ++i)   //画横线
      {
           int y = (nGridHeight * i) + rect.top;
           dc.MoveTo(rect.left,y);
           dc.LineTo(rect.right,y);
      }

      for(int i = 0; i <= nGridNum; ++i)   //画竖线
      {
           int x = (nGridWidth * i) + rect.left;
           dc.MoveTo(x,rect.top);
           dc.LineTo(x,rect.bottom);
      }

      //画下已经下好的棋

      for(int i=0; i<nGridNum; ++i)
           for(int j=0; j<nGridNum; ++j)
           {
                 if(m_stateGrid[i][j] == Unputed)
                 {
                      continue;
                 }
                 else if(m_stateGrid[i][j] == PutedO)
                 {
                      DrawO(dc,i,j);
                 }
                 else if(m_stateGrid[i][j] == PutedX)
                 {
                      DrawX(dc,i,j);
                 }
           }

}

//左键下O
void CMainWindow::OnLButtonDown(UINT nFlags, CPoint point)
{
      //若本轮不属O,即不响应
      if(m_nextTurn != OTurn)
           return;
      for(int i = 0; i<nGridNum; ++i)
           for(int j = 0; j<nGridNum; ++j)
           {
                 if(m_rcGrid[i][j].PtInRect(point) && m_stateGrid[i][j] == Unputed)
                 {
                      CClientDC dc(this);
                      m_nextTurn = XTurn;
                      m_stateGrid[i][j] = PutedO;
                      DrawO(dc,i,j);
                      ++m_countStep;
                      CheckForGameOver(OTurn,i,j);
                 }
           }
}

//右键下X
void CMainWindow::OnRButtonDown(UINT nFlags,CPoint point)
{
      //若本轮不属X,即不响应
      if(m_nextTurn != XTurn)
           return;
      for(int i = 0; i<nGridNum; ++i)
           for(int j = 0; j<nGridNum; ++j)
           {
                 if(m_rcGrid[i][j].PtInRect(point) && m_stateGrid[i][j] == Unputed)
                 {
                      CClientDC dc(this);
                      m_nextTurn = OTurn;
                      m_stateGrid[i][j] = PutedX;
                      DrawX(dc,i,j);
                      CheckForGameOver(XTurn,i,j);
                 }
           }
}


void CMainWindow::DrawO(CDC &dc, int i, int j)
{
      CRect rect(m_rcGrid[i][j]);
      rect.DeflateRect(5,5);
      dc.SelectStockObject(NULL_BRUSH);
      CPen pen(PS_SOLID,4,RGB(128,64,64));
      CPen *pOldPen = dc.SelectObject(&pen);
      dc.Ellipse(rect);
      dc.SelectObject(pOldPen);
}

void CMainWindow::DrawX(CDC &dc, int i, int j)
{
      CRect rect(m_rcGrid[i][j]);
      rect.DeflateRect(5,5);
      CPen pen(PS_SOLID,4,RGB(128,64,64));
      CPen *pOldPen = dc.SelectObject(&pen);
      dc.MoveTo(rect.left,rect.top);
      dc.LineTo(rect.right,rect.bottom);
      dc.MoveTo(rect.right,rect.top);
      dc.LineTo(rect.left,rect.bottom);
      dc.SelectObject(pOldPen);

}

void CMainWindow::CheckForGameOver(Turn thisTurn,int i,int j)
{
      if(IsWinner(thisTurn,i,j))
      {
           if(thisTurn == OTurn)
           {
                 CString string;
                 string.Format(_T("GOOD! O Wins in %d steps."),m_countStep);
                 MessageBox(string,_T("Game Over!"));
                 ResetGame();
           }
           else if(thisTurn == XTurn)
           {
                 CString string;
                 string.Format(_T("GOOD! X Wins in %d steps."),m_countStep);
                 MessageBox(string,_T("Game Over!"));
                 ResetGame();
           }
          
      }
      else if(IsDraw())
      {
           MessageBox(_T("OK,It's draw."),_T("Draw Game!"));
           ResetGame();
      }

}

//此为本软件最主要的部分,即检测是否有五个棋连在一起
//以横纵和两条对角线的方向分别检测,感觉比较笨
//暂时不知道有没有更好的办法,望来信赐教
BOOL CMainWindow::IsWinner(Turn thisTurn,int i,int j)
{
      int count = 1;
      gridState checkFor; //状态对比值
      if(thisTurn == OTurn)
           checkFor = PutedO;
      else if(thisTurn == XTurn)
           checkFor = PutedX;

      //横方向检测
      for(int m=1; m<5; ++m)
      {
           if(j - m > 0 && m_stateGrid[i][j-m] == checkFor)
                 ++count;
           else
                 break;
      }
      for(int m=1; m<5; ++m)
      {
           if(j + m < nGridNum && m_stateGrid[i][j+m] == checkFor)
                 ++count;
           else
                 break;
      }
      if(count >=5)
           return TRUE;
      count = 1;

      //竖方向检测
      for(int m=1; m<5; ++m)
      {
           if(i-m>0 && m_stateGrid[i-m][j] == checkFor)
                 ++count;
           else
                 break;
      }
      for(int m=1; m<5; ++m)
      {
           if(i+m<nGridNum && m_stateGrid[i+m][j] == checkFor)
                 ++count;
           else
                 break;
      }
      if(count >=5)
           return TRUE;
      count = 1;

      //左上至右下方向检测
      for(int m=1; m<5; ++m)
      {
           if(i-m>0 && j-m>0 && m_stateGrid[i-m][j-m] == checkFor)
                 ++count;
           else
                 break;
      }
      for(int m=1; m<5; ++m)
      {
           if(i+m<nGridNum && j+m<nGridNum && m_stateGrid[i+m][j+m] == checkFor)
                 ++count;
           else
                 break;
      }
      if(count >=5)
           return TRUE;
      count = 1;

      //右上至左下方向检测
      for(int m=1; m<5; ++m)
      {
           if(i-m>0 && j+m<nGridNum && m_stateGrid[i-m][j+m] == checkFor)
                 ++count;
           else
                 break;
      }
      for(int m=1; m<5; ++m)
      {
           if(i+m<nGridNum && j-m>0 && m_stateGrid[i+m][j-m] == checkFor)
                 ++count;
           else
                 break;
      }
      if(count >=5)
           return TRUE;
      return FALSE;
}

BOOL CMainWindow::OnSetCursor(CWnd *pWnd, UINT nHitTest, UINT message)
{
      if(nHitTest == HTCLIENT)
      {
           if(m_nextTurn == OTurn)
           {
                 ::SetCursor(::AfxGetApp()->LoadCursor(IDC_round));
                 return TRUE;
           }
           else if(m_nextTurn == XTurn)
           {
                 ::SetCursor(::AfxGetApp()->LoadCursor(IDC_cross));
                 return TRUE;
           }
      }
      return CFrameWnd::OnSetCursor(pWnd,nHitTest,message);
}

//当都下满了,即为平局
BOOL CMainWindow::IsDraw()
{
      int i,j;
      for(i=0; i<nGridNum; ++i)
           for(j=0; j<nGridNum; ++j)
           {
                 if(m_stateGrid[i][j]==Unputed)
                      break;
           }
      if(i==nGridNum && j==nGridNum)
           return TRUE;
      else
           return FALSE;
}

void CMainWindow::ResetGame()
{
      m_nextTurn = OTurn;
      m_countStep = 0;
      ::ZeroMemory(m_stateGrid,nGridNum * nGridNum * sizeof(gridState));
      Invalidate();
}

void CMainWindow::OnNcLButtonDblClk(UINT nHitTest, CPoint point)
{
      if(nHitTest == HTCAPTION)
      {
           ResetGame();
      }

      return CFrameWnd::OnNcLButtonDblClk(nHitTest,point);
}

阅读全文....

Windows 下利用MFC实现的中国象棋棋盘绘制程序

最近在啃 Jeff Prosise《MFC Windows 程序设计》这本书,以前虽然也学过MFC,当时用的是孙鑫的视频教程和书,学完后,似乎感觉有收获,也的确可以编点小的MFC程序,不过总感觉没有吃透,所以下决心,还是学这本书好,当时急于学习,偏偏网上这本书缺货,所以买了大家评价都不错的孙鑫那本,结果感觉还是不太好。突然想起某人说过,凡是教Windows编程的,书中图片过多,基本不要看,说的虽然很过,不过的确感觉用IDE引导出来的程序自己还是不能完全吃透,《MFC Windows 程序设计》就真的是主要靠手工代码,最近也学完一部分了,突发奇想画个象棋棋盘,巩固一下知识。完全手工代码输入,仅以抛砖引玉,因为没有考虑太多的缩放和分辨率问题,所以程序在不同的机子上可能会有效果不好的情况,假如有时间再改改。不要奇怪我怎么会在.NET横行的时代还在学大家都认为已经不行的MFC,我在网上晃了很久,发现懂MFC是很多公司的基本要求,无奈。。。。。。。。。。

 

ChineseChessBoard.h

class CMyApp : public CWinApp
{
public:
      virtual BOOL InitInstance();
};

class CMainWindow : public CFrameWnd
{
public:
      CMainWindow();

protected:
      afx_msg void  OnPaint();
      DECLARE_MESSAGE_MAP()
};

 

ChineseChessBoard.cpp

#include <afxwin.h>
#include <cmath>
#include "Hello.h"

CMyApp myApp;

//CMyApp member functions

BOOL CMyApp::InitInstance()
{
      m_pMainWnd = new CMainWindow;
      m_pMainWnd->ShowWindow(SW_SHOWMAXIMIZED);
      m_pMainWnd->UpdateWindow();
      return TRUE;
}



BEGIN_MESSAGE_MAP(CMainWindow,CFrameWnd)
      ON_WM_PAINT()
END_MESSAGE_MAP()

CMainWindow::CMainWindow()
{
      Create(NULL,_T("象棋棋盘"),WS_OVERLAPPEDWINDOW);
}

//CMainWindow mesage map and member functions
void CMainWindow::OnPaint()
{
      CPaintDC dc(this);

      CRect rect;
      GetClientRect(&rect);

      //画背景
      CBrush bkBrush(RGB(192,192,192));
      dc.FillRect(rect,&bkBrush);
     
      //确定画象棋棋盘的范围
      rect.DeflateRect(200,30);
      rect.OffsetRect(0,15);

      //画下象棋棋盘的背景
      CBrush brush(RGB(128,128,128));
      dc.FillRect(rect,&brush);

      //无聊,给点立体感
      rect.InflateRect(2,2);
      dc.Draw3dRect(rect,RGB(255,255,255),RGB(255,255,255));
      rect.DeflateRect(2,2);

      //开始画纵横线
      CPen pen(PS_SOLID,2,RGB(0,0,0));
      CPen *pOldPen = dc.SelectObject(&pen);
      int nGridWidth = rect.Width()/8;  //横向宽度,共格
      int nGridHeight = rect.Height()/9;  //纵向宽度,共格

      for(int i = 0; i < 10; ++i)   //画横线,10笔
      {
           int y = (nGridHeight * i) + rect.top;
           dc.MoveTo(rect.left,y);
           dc.LineTo(rect.right,y);
      }

      for(int i = 0; i < 8; ++i)   //画竖线,画笔,空下最右的竖线
      {
           int x = (nGridWidth * i) + rect.left;
      
           //中间为界限,无竖线
           dc.MoveTo(x,rect.top);
           dc.LineTo(x,rect.top + nGridHeight * 4);
           dc.MoveTo(x,rect.top + nGridHeight * 5);
           dc.LineTo(x,rect.bottom);
      }
     
      //补上左界限的竖笔及最右的竖线,此以rect.right画最右竖线,最重合
      dc.MoveTo(rect.left,rect.top + nGridHeight * 4);
      dc.LineTo(rect.left,rect.top + nGridHeight * 5);
      dc.MoveTo(rect.right,rect.top);
      dc.LineTo(rect.right,rect.bottom);
     
      //输出文字“楚河汉界”
      dc.SelectObject(pOldPen);
      CRect textRect(rect.left,rect.top + nGridHeight * 4,
                     rect.right,rect.top + nGridHeight * 5);
      CFont font;
      font.CreatePointFont(520,_T("宋体"));
      CFont *pOldFont = dc.SelectObject(&font);
     
      dc.SetBkMode(TRANSPARENT);
      dc.DrawText(_T("楚河    汉界"),-1,textRect,
           DT_SINGLELINE | DT_CENTER | DT_VCENTER);
      dc.SelectObject(pOldFont);

}

阅读全文....

vector成员转换为char输出的六种方法,STL的学习过程乱想

容器输出函数printCon测试过程中,想到的,那就是把vector容器换成char形式输出,一下子想到很多方法,都列出来,主要目的当然还是测试printCon函数,另外想到Linus提到的C++复杂到让人有心智障碍了,说是用C++选择太多,功能太多,导致不知道用什么好的一种耽误编程效率的东西,看到实现一个如此简单的东西,方法都如此之多,实在是可以想象,为什么有人,而且是如同Linus这样的牛人会提出这样的观点了。唉,选择多也是罪过啊。。。。。比如我好东西不学习,把时间浪费在想这个简单的东西到底还有别的方法实现没有上去了。。。。如此推知,可知为什么有人批评C++太过学院派,社团以钻研奇怪的看似尖端的技术为乐,实际应用程序编写的简化却一直没有太多进展。。。。。既然方法那么多,我不如不选。。想到怎么实现就怎么实现啊。。。。。作为C++程序员是那么注重效率,一定会研究哪个效率最高的。。。。于是时间又流失了。。。。。。啊门………….下面的printCon函数来自于myself.h这个我自己平时使用的库,可以参考置顶文件。

#include "stdafx.h"

#include <iostream>
#include <memory>
#include <vector>
#include "myself.h"

using namespace std;

int main()
{
    vector<int> ivec;
    for(int i = '1'; i <= 'z'; ++i)
        ivec.push_back( i );
    myself::printCon(ivec,"ASCII value: ");

    //利用char类型的流迭代器输出
    cout<<"ASCII type one: ";
    copy( ivec.begin(),ivec.end(),ostream_iterator<char>(cout," ") );
    cout<<endl;

    //不知道说利用了什么,其实本质就是利用operator<<的不同重载版本
    vector<char> cvec( ivec.size() );  //通过ivec的大小构建cvec再复制的方法
    copy( ivec.begin(),ivec.end(),cvec.begin() );//复制到cvec后输出就自然变成char了
    myself::printCon(cvec.begin(),cvec.end(),"ASCII type two: ");

    //考虑到上面提及的情况,应该可以直接调用operator<<的char版本
    //原理诈唬,其实不过一个强制转换
    cout<<"ASCII type three: ";
    vector<int>::iterator iIter;
    for(iIter = ivec.begin(); iIter != ivec.end(); ++iIter)
    {
        cout<< static_cast<char>(*iIter)<<" ";
    }
    cout<<endl;
//另外,发现没有强制容器转换的操作符,没有办法,实现一个函数。
//因为不能在main里面实现,所以在最后实现,并在这里声明。
    extern vector<char> ivtocv(const vector<int> &int_vec);
    myself::printCon(ivtocv(ivec),"ASCII type four: ");

//当然,其实,直接利用构造函数创建也不是不可以
    myself::printCon(vector<char>(ivec.begin(),ivec.end()),"ASCII type five: ");

//其实再考虑一下,不知道C++除了强制转换对象外,还有没有强制调用某重载函数的方法
//无聊之极,重载<<输出操作符试试,因为不能在main里面实现,并且不希望干扰前面的操作
//所以在最后实现,并在这里声明。
    extern ostream& operator<<(ostream &os, const vector<int> &int_vec);
//如此,则可以直接输出vector<int>为char
    cout<<"ASCII type five: ";
    cout<<ivec;
    return 0;
}

vector<char> ivtocv(const vector<int> &int_vec)
{
    vector<char> char_vec;  //另一种通过复制创建char_vec的方法
    copy( int_vec.begin(),int_vec.end(),back_inserter(char_vec));
    return char_vec;
}

ostream& operator<<(ostream &os, const vector<int> &int_vec)
{
    copy( int_vec.begin(),int_vec.end(),ostream_iterator<char>(os," ") );
    cout<<endl;
    return os;
}

阅读全文....