当前位置: 代码迷 >> J2ME >> j2me五子棋源码算法详细诠释
  详细解决方案

j2me五子棋源码算法详细诠释

热度:2620   发布时间:2013-02-25 21:30:30.0
j2me五子棋源码算法详细注释
五子棋算法注释非常详细,供大家开发时候参考。此源码是收集的网上最流行也是免费的五子棋源码。
   private Dot to4B(int player)
    {//活四 棋型的实现
        if(playerCounter[player] < 3)//如果该方下的棋子小于3,则不进行检测
            return null;
        Dot dot = null;
        int maxGain = 0;//对某位置进行评分
        for(int r = 1; r < boardSize - 1; r++)
        {
            for(int c = 1; c < boardSize - 1; c++)
                if(table[r][c] == 0)//如果该位置为空白,则检测是否实现“活四”
                {
                    int cd[] = connectedIn8D(player, r, c);//检测同类棋子在8个方向上的连续数目
                    int ed[] = expandedIn8D(player, r, c);//检测同类棋子和空白在8个方向上的连续数目
                    for(int i = 0; i < 4; i++)
                        if(ed[i] > cd[i] && ed[i + 4] > cd[i + 4] && cd[i] + cd[i + 4] + 1 >= 4)
                        {//如果活四
                            int gain = gainAt(r, c);//计算在该位置的得分,及对己方的有利程度
                            if(gain > maxGain || gain > 0 && gain == maxGain && randomTrue())
                            {
                                maxGain = gain;//保留最大分值的点,如果得分相同则随机选着
                                dot = new Dot(r, c);
                            }
                        }

                }

        }

        return dot;
    }

    private Dot toSingle4S_3B_2N1B(int player)
    {
        if(playerCounter[player] < 2)
            return null;
        Dot dot = null;
        for(int r = 0; r < boardSize; r++)
        {
            for(int c = 0; c < boardSize; c++)
            {
                if(table[r][c] != 0 || find4S_3B_2N1BAt(r, c, player, -1) == -1)
                    continue;
                dot = new Dot(r, c);
                break;
            }

            if(dot != null)
                break;
        }

        return dot;
    }

    private Dot toDouble4S_3B_2N1B(int player, boolean only4S)
    {
        if(playerCounter[player] < 4)
            return null;
        Dot dot = null;
        for(int rTest = 0; rTest < boardSize; rTest++)
        {
            for(int cTest = 0; cTest < boardSize; cTest++)
            {
                if(table[rTest][cTest] != 0)
                    continue;
                int cd[] = connectedIn8D(player, rTest, cTest);
                if(cd[0] + cd[1] + cd[2] + cd[3] + cd[4] + cd[5] + cd[6] + cd[7] <= 0)
                    continue;
                triedDot.setRowCol(rTest, cTest);
                table[rTest][cTest] = player;
                boolean found = false;
                int dFirst = find4S_3B_2N1B(player, -1, rTest, cTest, only4S);
                if(dFirst != -1 && find4S_3B_2N1B(player, dFirst, rTest, cTest, false) != -1)
                    found = true;
                table[rTest][cTest] = 0;
                triedDot.setRowCol(-1, -1);
                if(!found)
                    continue;
                dot = new Dot(rTest, cTest);
                break;
            }

            if(dot != null)
                break;
        }

        return dot;
    }

    private int find4SAt(int row, int col, int player, int exceptDirection)
    {
        int dFond = -1;
        int cd[] = connectedIn8D(player, row, col);
        int ed[] = expandedIn8D(player, row, col);
        for(int d = 0; d < 4; d++)
        {
            if(d == exceptDirection || table[row][col] != player)
                continue;
            int nConnect = cd[d] + cd[d + 4] + 1;
            int nFree1 = ed[d] - cd[d];
            int nFree2 = ed[d + 4] - cd[d + 4];
            boolean b4S = nConnect >= 4 && (nFree1 >= 1 || nFree2 >= 1);
            if(!b4S)
                continue;
            dFond = d;
            break;
        }

        return dFond;
    }

    private int find4S_3B_2N1BAt(int row, int col, int player, int exceptDirection)
    {
        int dFond = -1;
        int cd[] = connectedIn8D(player, row, col);
        int ed[] = expandedIn8D(player, row, col);
        for(int d = 0; d < 4; d++)
        {
            if(d == exceptDirection)
                continue;
            if(table[row][col] == player)
            {
                int nConnect = cd[d] + cd[d + 4] + 1;
                int nFree1 = ed[d] - cd[d];
                int nFree2 = ed[d + 4] - cd[d + 4];
                boolean b4S = nConnect >= 4 && (nFree1 >= 1 || nFree2 >= 1);
                boolean b3B = nConnect >= 3 && nFree1 >= 1 && nFree2 >= 1;
                if(b4S || b3B)
                {
                    dFond = d;
                    break;
                }
            }
            if(table[row][col] != 0)
                continue;
            int nFree1 = ed[d] - cd[d];
            int nFree2 = ed[d + 4] - cd[d + 4];
            boolean b2N1 = cd[d] >= 2 && cd[d + 4] >= 1 || cd[d] >= 1 && cd[d + 4] >= 2;
            boolean bSFree = nFree1 >= 1 && nFree2 >= 1;
            if(!b2N1 || !bSFree)
                continue;
            dFond = d;
            break;
        }

        return dFond;
    }

    private int find4S_3B_2N1B(int player, int exceptDirection, int rTest, int cTest, boolean only4S)
    {
        int dFond = -1;
        int rMin = rTest - 3;
        if(rMin < 0)
            rMin = 0;
        int rMax = rTest + 3;
        if(rMax > boardSize)
            rMax = boardSize;
        int cMin = cTest - 3;
        if(cMin < 0)
            cMin = 0;
        int cMax = cTest + 3;
        if(cMax > boardSize)
            cMax = boardSize;
        for(int r = rMin; r < rMax; r++)
        {
            for(int c = cMin; c < cMax; c++)
            {
                if(table[r][c] != player && table[r][c] != 0)
                    continue;
                if(only4S)
                    dFond = find4SAt(r, c, player, exceptDirection);
                else
                    dFond = find4S_3B_2N1BAt(r, c, player, exceptDirection);
                if(dFond != -1)
                    break;
            }

            if(dFond != -1)
               break;
        }

        return dFond;
    }

    private Dot to5L(int player)
    {/*
    *to5L方法将根据指定一方,在棋盘上搜索能实现连五得点,如果该方下了4步以下的棋,则不进行检测
    *,在检测过程中,如果检测到多个点能实现连五则比较to5LAt()返回的整数值(该数值有两部分组成,
    *每个连五得32分,可能实现连五则得2分,各个方向上得分相加),如果两点的分相同,则随机选择
    *
    */
        if(playerCounter[player] < 4)//如果该方只下了4步以下的棋,则不进行检测
            return null;
        int maxGain = 0;//得分
        Dot dot = null;
        for(int r = 0; r < boardSize; r++)//遍历行
        {
            for(int c = 0; c < boardSize; c++)//遍历列
            {
                int gain = to5LAt(player, r, c);//对某点的连五情况进行评分
                if(gain > maxGain || gain > 0 && gain == maxGain && randomTrue())//按照评分选择落子
                {
                    maxGain = gain;
                    dot = new Dot(r, c);
                }
            }

        }

        return dot;//返回落子
    }
/*
* to5LAt方法在指定的位置检测是否能够完成“连五”,检测方法为统计8个方向的同类棋子数目,如果在直线上同类棋子能够超过5颗,
* 既能实现“连五”
*/
    private int to5LAt(int player, int row, int col)
    {
        int lines = 0;
        int otherGain = 0;//走该步棋的增益,衡量该步棋对己方的有利程度
        if(table[row][col] == 0)
        {
            int cd[] = connectedIn8D(player, row, col);//检测同类棋子在8个方向上的连续数目
            int ed[] = expandedIn8D(player, row, col);
            for(int i = 0; i < 4; i++)
                if(ed[i] + ed[i + 4] + 1 >= 5)//在某一直线上同类棋子和空白数量超过5
                {
                    int l = cd[i] + cd[i + 4] + 1;
                    if(l >= 5)//判断是否出现“连五”
                        lines++;
                    else
                        otherGain += 2 ^ l;//该方向利于己方,增加评分
                }

        }
        return lines > 0 ? lines * 32 + otherGain : 0;//如果存在连五则返回真
    }

    private int[] expandedIn8D(int player, int row, int col)
    {
        int ed[] = new int[8];
        for(int d = 0; d < 8; d++)
            ed[d] = expandedIn1D(player, row, col, d);//对指定方向进行检测

        return ed;//返回检测结果
    }

    private int expandedIn1D(int player, int row, int col, int direction)
    {/*
    *对指定方向进行检测,检测的内容是同类棋子或空白区域,其中空白区域不超过4,如果棋子超出棋盘或者
    *遇见对手棋子则终止检测
    */
        int n = 0;//检测计数器
        int cn = 0;//循环计数器
        Dot d = new Dot(row, col);//根据指定位置创建棋子
        while(cn < 4)
        {
            d.copyFrom(moveOneStep(d, direction));//将棋子朝指定的方向移动
            if(!d.isInBoard(boardSize))//如果棋子超出棋盘则终止检测
                break;
            int p = table[d.row][d.col];//获取棋盘在该位置的状态
            if(p == 0)//如果棋盘在该位置是空白
                cn++;//循环计数器计数
            if(p != player && p != 0)//如果遇见对手棋子则终止检测
                break;
            n++;//计数
        }
        return n;//返回检测结果
    }

    private Dot maxGainedDot()
    {
        Dot dotWithMaxGain = null;
        int maxGain = 0;
        for(int r = 0; r < boardSize; r++)
        {
            for(int c = 0; c < boardSize; c++)
            {
                int gain = gainAt(r, c);
                if(gain > maxGain || gain > 0 && gain == maxGain && randomTrue())
                {
                    maxGain = gain;
                    dotWithMaxGain = new Dot(r, c);
                }
            }

        }

        return dotWithMaxGain;
    }

    private int gainAt(int row, int col)
    {
        if(table[row][col] == 0)
        {
            int gain = 0;
            for(int d = 0; d < 8; d++)
            {
                int gd = gainAtDirection(row, col, d);
                if(gd == 0)
                    gain >>= 2;
                else
                    gain += gd;
            }

            if(gain < 1)
                gain = 1;
            return gain;
        } else
        {
            return 0;
        }
    }

    private int gainAtDirection(int row, int col, int direction)
    {/*
    *计算指定位置在某个方向上的得分,该评分是针对计算机方有效的。
    *从某个位置向某个方向逐步移动,如果遇见玩家棋子则终止检测循环
    *,在移动过程中发现计算机棋子得分权重为5,遇见空白区域得分权重为1,
    *该位置的得分还与移动步长有关,离出发位置越远其影响力越小,因此步长的计算公式
    *为gain=(boardSize-step)/2,其中boardsize为棋盘大小,step为移动步长,计算某个方向上
    *得分代码如下
    */
        int gain = 0;
        Dot d = new Dot(row, col);
        int step = 0;
        do
        {
            d.copyFrom(moveOneStep(d, direction));//朝指定方向上移动一个位置
            step++;//步长加一
            if(!d.isInBoard(boardSize))//判断该位置是否是在棋盘中,即是否合法
                break;
            int player = table[d.row][d.col];//获得该位置的落子状态
            if(player == 2)//如果该位置玩家已经落子,则终止循环
                break;
            int gainByStone = player == 1 ? 5 : 1;//设置权重,机方落子权重为5,空白区域落子权重为1
            gain += gainByStep(step) * gainByStone;//计算机评分
        } while(true);
        return gain;//返回在指定位置落子朝指定方向上得分
    }

    private int gainByStep(int step)
    {
        int gain = (boardSize - step) / 2;//计算步长得分
        if(gain < 1)
            gain = 1;//得分最小为1
        return gain;//返回移动步长得出的分数
    }

    private int exist2N1(int row, int col, int player, int exceptDirection)
    {
        int cd[] = connectedIn8D(player, row, col);
        int ed[] = expandedIn8D(player, row, col);
        int existDirection = -1;
        for(int i = 0; i < 4; i++)
        {
            if(i == exceptDirection || (cd[i] < 2 || cd[i + 4] < 1) && (cd[i] < 1 || cd[i + 4] < 2) || (ed[i] - cd[i]) + (ed[i + 4] - cd[i + 4]) <= 0)
                continue;
            existDirection = i;
            break;
        }

        return existDirection;
    }

    private int checkFiveInRow(int row, int col, int n, int exceptDirection)
    {
        int player = table[row][col];//获取该位置的落子状态
        int cd[] = connectedIn8D(player, row, col);//检测8个方向上同类棋子的落子数目
        int ed[] = expandedIn8D(player, row, col);//检测8个方向上同类棋子及空白的落子个数
        int existDirection = -1;//检测出的连珠方向
        for(int i = 0; i < 4; i++)
        {               
            if(i == exceptDirection || cd[i] + cd[i + 4] + 1 < n || (ed[i] - cd[i]) + (ed[i + 4] - cd[i + 4]) < 0)
                continue;
             

            existDirection = i;//存在五子连珠情况
            break;//终止循环
        }

        return existDirection;//返回检测出的方向
    }

    private int[] connectedIn8D(int player, int row, int col)
    {
    /*
    *connectedIn8D对8个方向上都进行检测并用整形数组记录
    *
    */
        int cd[] = new int[8];//用来记录8个方向同类棋子情况的数组
        for(int d = 0; d < 8; d++)
            cd[d] = connectedIn1D(player, row, col, d);//对指定方向进行检测

        return cd;
    }

    private int connectedIn1D(int player, int row, int col, int direction)
    {
        /*
         * connectedIn1D方法用于检测棋盘上任意一点在指定的方向上的同类棋子相连的个数,传入落子
         * 方标志(区分计算机还是玩家),检测位置的行列坐标和检测方向,返回在该方向上相连棋子的个数
         * (若检测到对方棋子或者空白棋面则停止检测)。
         */
    int n = 0;
        Dot d = new Dot(row, col);//在当前位置创建棋子
        do
        {
            d.copyFrom(moveOneStep(d, direction));//返回在指定方向上移动后的棋子
            if(d.isInBoard(boardSize) && table[d.row][d.col] == player)
            //移动后依然在棋盘上并有同类棋子
                n++;//计数器加一
            else
                return n;//当棋子超出棋盘或者检测到对方棋子或空白区域时停止检测
        } while(true);
    }

    private Dot moveOneStep(Dot d, int direction)//朝8个方向移动一步的moveOneStep方法,传入当前棋子和移动方向,返回移动之后的棋子
    {
        int r = d.row;
        int c = d.col;
        switch(direction)
        {
        case 0: //右方
            c++;
            break;

        case 1: //右上方
            r--;
            c++;
            break;

        case 2: //上方
            r--;
            break;

        case 3: //左上方
            r--;
            c--;
            break;

        case 4: //左方
            c--;
            break;

        case 5: //左下方
            r++;
            c--;
            break;

        case 6: //下方
            r++;
            break;

        case 7: //右下方
            r++;
            c++;
            break;
        }
        return new Dot(r, c);//返回移动后的棋子
    }

    private boolean randomTrue()
    {
        return rndNum.nextInt() % 2 == 0;//返回随机的布尔值
    }

电脑每次下棋,按照顺序(能连成五子,能连成四子、三子)检测落子位置,
如果那些方法没找到合适的落子位置,根据点的分数落子,然后判断是否赢了,
然后该人下了
选择性参考一下

//构造函数(画布,棋盘大小,谁先落子,难度)
public FIRLogic(FIRCanvas canvas, int boardSize, boolean isComputerFirst, int degree)

//落子(行,列,哪方落)
private void goAt(int row, int col, int player)

//悔棋
public boolean undo()

//某一点在特定方向的同类棋子的个数(哪一方,行,列,方向)
connectedIn1D(int player, int row, int col, int direction)

//某一点在八个方向上的同类棋子的个数的数组
private int[] connectedIn8D(int player, int row, int col)

//某一点在特定方向的同类棋子或空位的个数(哪一方,行,列,方向)
expandedIn1D(int player, int row, int col, int direction)

//某一点在特定方向的同类棋子或空位的个数的数组(哪一方,行,列,方向)
private int[] expandedIn8D(int player, int row, int col)

//判断胜利的方法,是否五子相连
private int checkFiveInRow(int row, int col, int n, int exceptDirection)

//某一方某个点(row,col)能否实现连五
private int to5LAt(int player, int row, int col)

//根据to5LAt()返回其中一个可以实现连五的点
private Dot to5L(int player)

//在direction方向上对点进行评分(当条件相同时,以分数来决定落子)
gainAtDirection(int row, int col, int direction)

//对某点的分数进行汇总(gainAtDirection()遍历)
gainAt(int row, int col)

//寻找活四的点(活四:有两个点可以形成连五的状态)
to4B(int player)

//寻找能冲四、活三的点
toSingle4S_3B_2N1B(int player)

//#%*$^*^*&(&&---------  ^.^
toDouble4S_3B_2N1B(int player, boolean only4S)

//只找冲四的点。能形成五子相連的四子叫 活四或冲四(活四:有两个点可形成五子相连 。冲四:只有一个点)
find4SAt(int row, int col, int player, int exceptDirection)

//某点某方向是否存在冲四、活三的可能
find4S_3B_2N1BAt(int row, int col, int player,int exceptDirection)

//寻找一个点,满足find4S_3B_2N1BAt()方法
find4S_3B_2N1B(int player, int exceptDirection, int rTest,
int cTest, boolean only4S)

//最佳落子点(有得分计算出来)
maxGainedDot()