赖勇浩(恋花蝶)的博客

坐观天下风云涌,笑拥美人言语懒。

赖勇浩ID:lanphaday
300859次访问,排名171好友121人,关注者130
网络游戏程序员,略懂python、C++;不太懂网络编程、数据库编程;不懂C语言、3D。
lanphaday的文章
原创 57 篇
翻译 14 篇
转载 12 篇
评论 560 篇
赖勇浩的公告
Google
最近评论
Joyfish:DIP是我看完 Python Tutorial 后的第二个Python教程,其实感觉还不错,可能和在这之前我有多年的C++ & Java基础有关,支持博主
ghostjava:不要人云亦云,一群跟屁虫!你看着好就是好,管别人说什么,我就是用DIP入门的,虽然我是SPSS的,没在实际中使用过python,我就觉得很棒,初学者和穷人都来用。入门后推荐看<ython核心编程>.
ideasky:此帖甚好.
无聊空间:哎...有什么好争,能赚大钱才是皇道.
insulted:up
文章分类
收藏
    相册
    编程语言
    TIOBE 流行度
    性能排名
    我创建的开源项目
    VisualPyTune
    我的其它博客
    程序员的小厨房
    过去的博客
    非技术博客
    友情链接
    CP(图形学)
    DarkSpy
    一刀
    丫头
    沈公,仙人也!
    超哥之奋起版
    存档
    订阅我的博客
    XML聚合  FeedSky
    订阅到鲜果
    订阅到Google
    订阅到抓虾
    订阅到BlogLines
    订阅到Yahoo
    订阅到GouGou
    订阅到飞鸽
    订阅到Rojo
    订阅到newsgator
    订阅到netvibes

    原创 较高人工智能的人机博弈程序实现(多个算法结合)含C++源码收藏

    新一篇: 分享我写的CPU测试程序,看看你的CPU强劲吗? | 旧一篇: 基本A*算法python实现

    较高人工智能的人机博弈程序实现(多个算法结合)含C++源码

     本文由恋花蝶最初发表于http://blog.csdn.net/lanphaday上,您可以转载、引用、打印和分发等,但必须保留本文完整和包含本声明,否则必究责任。

     到昨天晚上,Topcoder Marathon Match 6结束了,我取得了第18名的成绩,已经是自己参加Marathon四次以来的最好名次啦,高兴ing,这次终于中国人的成绩超过日本人了。因为这次的题目比较偏:写一个人工智能程序和服务器端的程序进行博弈。人机博弈是一门比较专的学科,我们因为英文劣势,大部分中国高手都不能快速的在比赛中学习和实现一些复杂的算法,以致成绩不太如意;我挟之前对这方面的了解,做得还算行,所以把代码公开出来,可以多一点中文方面的资料和源码给大家参考,我也感到非常荣幸。
     比赛的题目请看这里:http://www.topcoder.com/longcontest/?module=ViewProblemStatement&rd=10118&pm=6759 主要的游戏规则也是在这里的,我就不在这里重复啦,主要讲讲我的代码用到了什么算法。麻将虽小,五脏俱全,主要应用的算法有主要变量搜索(PVS)、历史启发(HH)、杀手启发(KH)、Null Move和迭代深化(ID),可惜后来不够时间实现置换表(TT),不然可以多一个算法了。代码里还实现了时间控制策略,可以几乎用尽20秒的测试时间,为争取更好的着法提供了保证。还有值得一提的是棋盘表示,我使用了棋盘表、棋子位置表结合的方式来表示,后来发现加上空位表的话,可以加快不少走法生成和估值的速度。反正棋盘表示是一切的基础,一种好的表示方法可以带来很大的性能提升。对于代码,大家注意class SE里的search_move和pvs两个函数,上述的算法和策略都在那里。class MG是关于棋盘表示、走法生成和估值的,class KH和class HH分别是杀手启发和历史启发。Null Move是简单有效的算法,不过我的实现里是比较简单的那种,如果有兴趣,可以查询其它资料。
     
     讲了这么多,应该说一下这份代码的计算能力:以6*6的棋盘为例,这份代码在VC6的release模式下编译运行可以在1秒内搜索并评估83万个叶子节点,计算层次在8-9层;如果用MiniMax算法不进行剪枝,只能搜索到3-4层(测试机器皆为超线程P4 3.0G+1G内存)。这就是算法的力量吧。另声明一下,本代码未作优化,不代表我不懂,只是没有时间,看的朋友请海涵了。 

    下面是代码,在VC和G++上皆可编译、执行

    因为比赛期间写的,代码比较乱,但整体的风格还是可以的,复制到IDE上看可能会更好看些

    #include <iostream>
    #include 
    <cstdlib>
    #include 
    <ctime>
    #include 
    <cassert>
    #include 
    <vector>
    #include 
    <algorithm>

    using namespace std;

    typedef unsigned 
    int UINT;
    typedef UINT MOVE;

    const int INFINITY = 100000000;
    const int MAX_DEPTH = 16;

    const UINT max_board_size = 256;
    const UINT max_stones_cnt = 256;

    const UINT empty = 0;
    const UINT my_color = 1;
    const UINT svr_color = 2;

    #ifdef WIN32
    const clock_t all_time = 19200;
    #else
    const clock_t all_time = 19200000;
    #endif

    const UINT check_time_cnt = 0x00001fff;

    #define is_empty(x) (x==empty)

    #define opp_color(x) (x==my_color?svr_color:my_color)

    int leaf_cnt = 0;

    class MG
    {
    private:
        UINT N_;
        UINT board_[max_board_size];
        UINT stones_[max_stones_cnt];
    private:
        
    void extend(UINT pos, unsigned char* eht, unsigned char* est, UINT& area, UINT& round);

    public:
        MOVE move_table[MAX_DEPTH][max_board_size];
        UINT curr_stones_cnt;
        UINT curr_board_size;
        
    void set_N(int n){
            N_ 
    = n;
            curr_board_size 
    = n*n;
            curr_stones_cnt 
    = 0;
            memset(board_, 
    0sizeof(UINT)*max_board_size);
            memset(stones_, 
    0sizeof(UINT)*max_stones_cnt);
        }

        
    void make_move(int idx, int color){
            board_[idx]
    =color;
            stones_[curr_stones_cnt
    ++= idx;
        }

        
    void unmake_move(int idx){
            board_[idx] 
    = empty;
            
    --curr_stones_cnt;
        }

        inline 
    bool is_game_over(){return curr_stones_cnt == curr_board_size;}
        UINT gen_move(
    int depth);
        
    int evaluatoin(int color);
        
    int evaluatoin_4_end(int color);
        
    void print_board()
        
    {
            
    int cnt = 0;
            
    for(UINT i = 0; i < curr_board_size; ++i)
            
    {
                
    if(is_empty(board_[i]))
                    cout 
    << "";
                
    else
                    cout 
    << ((board_[i]==my_color)?"":"");
                
    ++cnt;
                
    if(cnt == N_)
                
    {
                    cnt 
    = 0;
                    cout 
    << endl;
                }

            }

        }

        
    bool can_move(MOVE move){return is_empty(board_[move]);}
        
    void remove_killers(int depth, int move_cnt, MOVE* killers, int killers_cnt)
        
    {
            
    for(int i = 0; i < killers_cnt; ++i)
            
    {
                MOVE m 
    = killers[i];
                
    for(int j = 0; j < move_cnt; ++j)
                
    {
                    
    if(move_table[depth][j] != m)
                        
    continue;
                    
    for(int k = j+1; k < move_cnt; ++k)
                    
    {
                        move_table[depth][k
    -1= move_table[depth][k];
                    }

                    
    break;
                }

            }

        }

    }
    ;

    UINT MG::gen_move(
    int depth)
    {
        
    int cnt = 0;
        
    for(UINT i = 0; i < curr_board_size; ++i)
        
    {
            
    if(is_empty(board_[i]))
                move_table[depth][cnt
    ++= i;
        }

        
    return cnt;
    }


    int MG::evaluatoin(int color)
    {
        
    if(curr_stones_cnt+1 == curr_board_size)
        
    {
            
    for(int i = 0; i < curr_board_size; ++i)
            
    {
                
    if(is_empty(board_[i]))
                
    {
                    board_[i] 
    = color;
                    
    int value = -evaluatoin_4_end(opp_color(color));
                    board_[i] 
    = empty;
                    
    return value;
                }

            }

        }

        
    ++leaf_cnt;
        unsigned 
    char extended_hash_table[max_board_size] = {0};
        
        
    int my_score = 0, svr_score = 0;
        
    for(UINT i = 0; i < curr_stones_cnt; ++i)
        
    {
            UINT pos 
    = stones_[i];
            
    if(extended_hash_table[pos])
                
    continue;
            UINT area 
    = 0, round = 0;
            unsigned 
    char extended_space_table[max_board_size] = {0};
            extend(pos, extended_hash_table, extended_space_table, area, round);
            
    if(board_[pos] == my_color)
            
    {
                my_score 
    += area*area*round;
            }

            
    else
            
    {
                svr_score 
    += area*area*round;
            }

        }

        
    if(color == my_color)
            
    return my_score - svr_score;
        
    return svr_score - my_score;
    }


    int MG::evaluatoin_4_end(int color)
    {
        
    ++leaf_cnt;
        unsigned 
    char extended_hash_table[max_board_size] = {0};
        
        
    int my_score = 0, svr_score = 0;
        
    for(UINT i = 0; i < curr_stones_cnt; ++i)
        
    {
            UINT pos 
    = stones_[i];
            
    if(extended_hash_table[pos])
                
    continue;
            UINT area 
    = 0, round = 0;
            unsigned 
    char extended_space_table[max_board_size] = {0};
            extend(pos, extended_hash_table, extended_space_table, area, round);
            
    if(board_[pos] == my_color)
            
    {
                my_score 
    += area*area;
            }

            
    else
            
    {
                svr_score 
    += area*area;
            }

        }

        
    if(color == my_color)
            
    return my_score - svr_score;
        
    return svr_score - my_score;
    }


    void MG::extend(UINT pos, unsigned char* eht, unsigned char* est, UINT& area, UINT& round)
    {
        
    const UINT round_cnt = 4;
        
    int is[round_cnt] = {-N_, -11, N_};

        
    ++area;
        eht[pos] 
    = 1;

        
    for(UINT i = 0; i < round_cnt; ++i)
        
    {
            
    int new_idx = pos + is[i];
            
    if(new_idx < 0 || new_idx >= curr_board_size)
                
    continue;
            
    if(i == 1 && pos % N_ == 0)
                
    continue;
            
    if(i == 2 && new_idx % N_ == 0)
                
    continue;
            
    if(is_empty(board_[new_idx]) && (!est[new_idx]))
            
    {
                
    ++round;
                est[new_idx] 
    = 1;
                
    continue;
            }

            
    if(eht[new_idx])
                
    continue;
            
    if(board_[new_idx] == board_[pos])
                extend(new_idx, eht, est, area, round);
        }

    }


    class HH
    {
    private:
        UINT board_[
    2][max_board_size];
    public:
        
    void reset(){memset(board_, 0sizeof(UINT)*max_board_size);}
        
    void update_value(int depth, int color, MOVE move);
        MOVE get_best(MOVE
    * move_list, int color, int cnt);
    }
    ;

    void HH::update_value(int depth, int color, MOVE move)
    {
        board_[color
    -1][move] += (1 << depth);
    }


    MOVE HH::get_best(MOVE
    * move_list, int color, int cnt)
    {
        
    int real_color = color-1;
        MOVE
    * p = move_list;
        
    int best = board_[real_color][*move_list];
        
    int best_idx = 0;
        
    for(int i = 1; i < cnt; ++i)
        
    {
            
    ++move_list;