五子棋程序实现了判赢,禁手,复盘,低级AI(判断还存在许多问题,欢迎指正),继续游戏,夜间模式,悔棋等基本功能
代码可研究不可复制粘贴。
禁手的定义:是为了限制黑方的先手优势而设定的一种比赛规则
禁手的分类:三三禁手(黑棋一子落下同时形成两个或两个以上的活三,此子必须为两个活三共同的构成子)、四四禁手(黑棋一子落下同时形成两个或两个以上的冲四或活四)、长连禁手(黑棋一子落下形成一个或一个以上的长连)。
构成禁手的基本子力要素:活三(本方再走一着可以形成活四的三)、活四(有两个点可以成五的四)、冲四(只有一个点可以成五的四)、长连(在棋盘上的阳线和阴线任意一条线上,形成的5个以上同色棋子不间隔的相连)
关于禁手的规定:黑方五连与禁手同时形成,禁手失效,黑方胜
具体规则参见附件
程序说明:
0表示空位,1表示黑子,-1表示白子,chesses[15][15]保存的棋盘的当前棋局
本程序的长连禁手是和判赢一起判断的,判赢算法中会返回当前下子的位置四周有几颗同颜色子相连,如果是黑子并且长度大于5则为长连禁手。
代码举例:
/** * 计算横向是否 5颗相同颜色的子相连 * @param r 当前下子的横坐标 * @param c 当前下子的纵坐标 * @return */ public static int checkRow(int r, int c) { int count = 1; // 向右找 for (int j = c + 1; j < COLS; j++) { if (chesses[r][c] == chesses[r][j]) { count++; } else { break; } } // 往左走 for (int j = c - 1; j >= 0; j--) { if (chesses[r][c] == chesses[r][j]) { count++; } else { break; } } return count; } public static boolean longlink(int r, int c) { if (chesses[r][c] == 1) { return (SuanFa.checkRow(r, c) > 5 || SuanFa.checkxiezuo(r, c) > 5 || SuanFa.checkCol(r, c) > 5 || SuanFa.checkxieyou(r, c) > 5); } else { return false; }
三三禁手和四四禁手类似,但是四四禁手有一些特殊情况。这里一44禁手为例进行说明
首先,判断禁手,并不需要扫描整个棋盘,只需要扫描当前落子的周围关键位置,比如放了棋子在chesses[r][c]的位置,横向只需要扫描chesses[r-4][c-4]到[r+4][c+4]的位置。选择好范围之后,从[r-4][c-4]开始,每次扫描连续的5个位置看是否构成活四或者重四(三三禁手只有活三),由于采用1,-1来计数,可以直接这样判断:
if (chesses[r][j] + chesses[r][j + 1] + chesses[r][j + 2]+ chesses[r][j + 3] +chesses[r][j + 4] == 4)
如果这5个连续位置相加为4,必然是4个黑子和一个空位。(用1,2标记棋盘黑白子处理相对来说麻烦一点,但原理是一样的,多加几个判断即可。)
依次从各个方向找完之后,我们可以用count来记录找到了几个满足条件的活四或者冲四或者活三。
如果找到两个,已经构成禁手,立即结束查找。
但是,如果你理解了以上过程,你就会发现,现在的情况只能判断特定的一些禁手情况。这里我用图片进行说明:
图片1中这类型禁手已经可以进行判断了,(4,5)为最后一颗黑色落子,他会从(4,5)位置为焦点进行扫描,横向竖向都已经扫到了活三。但是2中的这种情况,以(7,10)进行扫描的话无法扫描出(6,X)横向的活三。
所以在扫描过程中,还需要对(7,10)周围的(6,10)进行扫描,如果以(6,10)为焦点也扫描到了活三,这个时候也是三三禁手。
这里用一个层次来标记一下,方便递归来查找。int cenci=1;如果是SetChesses设置的棋子,那么他的cenci=1;如果是SetChesses设置的棋子为焦点扫描到的活三,活四,冲四,那么该方向其余元素(6,10),(8,10)的层次cenci=0;这样,就不会一直扫描下去。
在这里又出现一个问题,即便用cenci来记录了要找周围元素的活四等等,以(6,10)为例,(6,10)会找到横着的活三,也会找到竖着的活三,这样,竖着的就算重复了,这是非常不合理的,所以得用一个变量标记一下方向,对于层次为0的我们只要找其余三个方向。
活四中还有几种特例:比如:
此处(5,6)构成了四四禁手(三三禁手没有这样的特例),由于这种类型的禁手很少,所以在程序中单独列举出来进行判断。
活四判断代码参考如下:
public static int Four(int r, int c, int cenci, char direction) { if (chesses[r][c] == 1) { int i, j, count4 = 0; if (cenci == 1) { // 判断横向四 for (j = 0; j < 11; j++) { if (chesses[r][j] + chesses[r][j + 1] + chesses[r][j + 2] + chesses[r][j + 3] + chesses[r][j + 4] == 4) { count4++; for (int k = 0; k < 5; k++) { if (chesses[r][j + k] == 1) { count4 += Four(r, j + k, 0, 'a'); if (count4 > 1) { return 2; } } } break; } } // 判断纵向四 for (i = 0; i < 11; i++) { if (chesses[i][c] + chesses[i + 1][c] + chesses[i + 2][c] + chesses[i + 3][c] + chesses[i + 4][c] == 4) { count4++; for (int k = 0; k < 5; k++) { if (chesses[i + k][c] == 1) { count4 += Four(i + k, c, 0, 'b'); if (count4 > 1) { return 2; } } } break; } } // 判断“\”向四 if (c > r) { for (i = 0, j = c - r; i < (11 - c + r); i++, j++) { if (chesses[i][j] + chesses[i + 1][j + 1] + chesses[i + 2][j + 2] + chesses[i + 3][j + 3] + chesses[i + 4][j + 4] == 4) { count4++; for (int k = 0; k < 5; k++) { if (chesses[i + k][j + k] == 1) { count4 += Four(i + k, j + k, 0, 'c'); if (count4 > 1) { return 2; } } } break; } } } else { for (i = r - c, j = 0; i < 11; i++, j++) { // 判断“\”向“活三” if (chesses[i][j] + chesses[i + 1][j + 1] + chesses[i + 2][j + 2] + chesses[i + 3][j + 3] + chesses[i + 4][j + 4] == 4) { count4++; for (int k = 0; k < 5; k++) { if (chesses[i + k][j + k] == 1) { count4 += Four(i + k, j + k, 0, 'c'); if (count4 > 1) { return 2; } } } break; } } } if (r + c < 15) { for (i = r + c, j = 0; i >= 4; i--, j++) { // 判断“/”向“活三” if (chesses[i][j] + chesses[i - 1][j + 1] + chesses[i - 2][j + 2] + chesses[i - 3][j + 3] + chesses[i - 4][j + 4] == 4) { count4++; for (int k = 0; k < 5; k++) { if (chesses[i - k][j + k] == 1) { count4 += Four(i - k, j + k, 0, 'd'); if (count4 > 1) { return 2; } } } break; } } } else { for (i = 14, j = r + c - 14; j < 11; i--, j++) { // 判断“/”向“活三” if (chesses[i][j] + chesses[i - 1][j + 1] + chesses[i - 2][j + 2] + chesses[i - 3][j + 3] + chesses[i - 4][j + 4] == 4) { count4++; for (int k = 0; k < 5; k++) { if (chesses[i - k][j + k] == 1) { count4 += Four(i - k, j + k, 0, 'd'); if (count4 > 1) { return 2; } } } break; } } } if (count4 > 1) { return 2; } } else { if (direction != 'a') { // 判断横向四 for (j = 0; j < 11; j++) { if (chesses[r][j] + chesses[r][j + 1] + chesses[r][j + 2] + chesses[r][j + 3] + chesses[r][j + 4] == 4) { return 1; } } } if (direction != 'b') { // 判断纵向四 for (i = 0; i < 11; i++) { if (chesses[i][c] + chesses[i + 1][c] + chesses[i + 2][c] + chesses[i + 3][c] + chesses[i + 4][c] == 4) { return 1; } } } if (direction != 'c') { // 判断“\”向四 if (c > r) { for (i = 0, j = c - r; i < (11 - c + r); i++, j++) { if (chesses[i][j] + chesses[i + 1][j + 1] + chesses[i + 2][j + 2] + chesses[i + 3][j + 3] + chesses[i + 4][j + 4] == 4) { return 1; } } } else { for (i = r - c, j = 0; i < 11; i++, j++) { // 判断“\”向“活三” if (chesses[i][j] + chesses[i + 1][j + 1] + chesses[i + 2][j + 2] + chesses[i + 3][j + 3] + chesses[i + 4][j + 4] == 4) { return 1; } } } } if (direction != 'd') { if (r + c < 15) { for (i = r + c, j = 0; i >= 4; i--, j++) { // 判断“/”向“活三” if (chesses[i][j] + chesses[i - 1][j + 1] + chesses[i - 2][j + 2] + chesses[i - 3][j + 3] + chesses[i - 4][j + 4] == 4) { return 1; } } } else { for (i = 14, j = r + c - 14; j < 11; i--, j++) { // 判断“/”向“活三” if (chesses[i][j] + chesses[i - 1][j + 1] + chesses[i - 2][j + 2] + chesses[i - 3][j + 3] + chesses[i - 4][j + 4] == 4) { return 1; } } } } } // 横向特殊四四 if (c > 2 && c < 12 && chesses[r][c - 3] == 1 && chesses[r][c - 2] == 0 && chesses[r][c - 1] == 1 && chesses[r][c + 3] == 1 && chesses[r][c + 2] == 0 && chesses[r][c + 1] == 1) { return 2; } else if (c > 2 && c < 11 && chesses[r][c - 3] == 1 && chesses[r][c - 2] == 1 && chesses[r][c - 1] == 0 && chesses[r][c + 4] == 1 && chesses[r][c + 3] == 1 && chesses[r][c + 2] == 0 && chesses[r][c + 1] == 1) { return 2; } else if (c > 3 && c < 11 && chesses[r][c - 4] == 1 && chesses[r][c - 3] == 1 && chesses[r][c - 2] == 1 && chesses[r][c - 1] == 0 && chesses[r][c + 4] == 1 && chesses[r][c + 3] == 1 && chesses[r][c + 2] == 1 && chesses[r][c + 1] == 0) { return 2; } else if (c > 3 && c < 11 && chesses[r][c - 4] == 1 && chesses[r][c - 3] == 1 && chesses[r][c - 2] == 0 && chesses[r][c - 1] == 1 && chesses[r][c + 4] == 1 && chesses[r][c + 3] == 1 && chesses[r][c + 2] == 0 && chesses[r][c + 1] == 1) { return 2; } // 纵向特殊四四 if (r > 2 && r < 12 && chesses[r - 3][c] == 1 && chesses[r - 2][c] == 0 && chesses[r - 1][c] == 1 && chesses[r + 3][c] == 1 && chesses[r + 2][c] == 0 && chesses[r + 1][c] == 1) { return 2; } else if (r > 2 && r < 11 && chesses[r - 3][c] == 1 && chesses[r - 2][c] == 1 && chesses[r - 1][c] == 0 && chesses[r + 4][c] == 1 && chesses[r + 3][c] == 1 && chesses[r + 2][c] == 0 && chesses[r + 1][c] == 1) { return 2; } else if (r > 3 && r < 11 && chesses[r - 4][c] == 1 && chesses[r - 3][c] == 1 && chesses[r - 2][c] == 1 && chesses[r - 1][c] == 0 && chesses[r + 4][c] == 1 && chesses[r + 3][c] == 1 && chesses[r + 2][c] == 1 && chesses[r + 1][c] == 0) { return 2; } else if (r > 3 && r < 11 && chesses[r - 4][c] == 1 && chesses[r - 3][c] == 1 && chesses[r - 2][c] == 0 && chesses[r - 1][c] == 1 && chesses[r + 4][c] == 1 && chesses[r + 3][c] == 1 && chesses[r + 2][c] == 0 && chesses[r + 1][c] == 1) { return 2; } // "\"向特殊四四 if (c > 2 && c < 12 && r > 2 && r < 12 && chesses[r - 3][c - 3] == 1 && chesses[r - 2][c - 2] == 0 && chesses[r - 1][c - 1] == 1 && chesses[r + 3][c + 3] == 1 && chesses[r + 2][c + 2] == 0 && chesses[r + 1][c + 1] == 1) { return 2; } else if (c > 2 && c < 11 && r > 2 && r < 11 && chesses[r - 3][c - 3] == 1 && chesses[r - 2][c - 2] == 1 && chesses[r - 1][c - 1] == 0 && chesses[r + 4][c + 4] == 1 && chesses[r + 3][c + 3] == 1 && chesses[r + 2][c + 2] == 0 && chesses[r + 1][c + 1] == 1) { return 2; } else if (c > 3 && c < 11 && r > 3 && r < 11 && chesses[r - 4][c - 4] == 1 && chesses[r - 3][c - 3] == 1 && chesses[r - 2][c - 2] == 1 && chesses[r - 1][c - 1] == 0 && chesses[r + 4][c + 4] == 1 && chesses[r + 3][c + 3] == 1 && chesses[r + 2][c + 2] == 1 && chesses[r + 1][c + 1] == 0) { return 2; } else if (c > 3 && c < 11 && r > 3 && r < 11 && chesses[r - 4][c - 4] == 1 && chesses[r - 3][c - 3] == 1 && chesses[r - 2][c - 2] == 0 && chesses[r - 1][c - 1] == 1 && chesses[r + 4][c + 4] == 1 && chesses[r + 3][c + 3] == 1 && chesses[r + 2][c + 2] == 0 && chesses[r + 1][c + 1] == 1) { return 2; } // "/"向特殊四四 // 101 1 101 if (c > 2 && c < 12 && r > 2 && r < 12 && chesses[r - 3][c + 3] == 1 && chesses[r - 2][c + 2] == 0 && chesses[r - 1][c + 1] == 1 && chesses[r + 3][c - 3] == 1 && chesses[r + 2][c - 2] == 0 && chesses[r + 1][c - 1] == 1) { return 2; } // 110 1 1011 else if (c > 3 && c < 12 && r > 3 && r < 11 && chesses[r - 3][c + 3] == 1 && chesses[r - 2][c + 2] == 1 && chesses[r - 1][c + 1] == 0 && chesses[r + 4][c - 4] == 1 && chesses[r + 3][c - 3] == 1 && chesses[r + 2][c - 2] == 0 && chesses[r + 1][c - 1] == 1) { return 2; } // 1110 1 0111 else if (c > 3 && c < 11 && r > 3 && r < 11 && chesses[r - 4][c + 4] == 1 && chesses[r - 3][c + 3] == 1 && chesses[r - 2][c + 2] == 1 && chesses[r - 1][c + 1] == 0 && chesses[r + 4][c - 4] == 1 && chesses[r + 3][c - 3] == 1 && chesses[r + 2][c - 2] == 1 && chesses[r + 1][c - 1] == 0) { return 2; } // 1101 1 011 else if (c > 3 && c < 11 && r > 3 && r < 11) { if (chesses[r - 4][c + 4] == 1 && chesses[r - 3][c + 3] == 1 && chesses[r - 2][c + 2] == 0 && chesses[r - 1][c + 1] == 1 && chesses[r + 4][c - 4] == 1 && chesses[r + 3][c - 3] == 1 && chesses[r + 2][c - 2] == 0 && chesses[r + 1][c - 1] == 1) { return 2; } } } return 0; }
函数调用如下:
/** * 判赢有禁手 * @param r 最后一次下子的行数 * @param c 最后一次下子的列数 */ public boolean JudgeWinWithJinshou(int r, int c) { if (Main.count == -1) {// 白子 if (SuanFa.beFive(r, c)) { javax.swing.JOptionPane.showMessageDialog(mu, "白子赢了!!"); ReDisplayChesseBoard(); return true; } } else if (Main.count == 1) {// 黑子 if (SuanFa.Five(r, c)) {// 五子 javax.swing.JOptionPane.showMessageDialog(mu, "黑子赢了!!"); ReDisplayChesseBoard(); return true; } else if (SuanFa.LiveThree(r, c, 1, 'e') > 1) { javax.swing.JOptionPane.showMessageDialog(mu, "三三禁手!黑子输了!!"); ReDisplayChesseBoard(); return true; } else if (SuanFa.Four(r, c, 1, 'e') > 1) { javax.swing.JOptionPane.showMessageDialog(mu, "四四禁手!黑子输了!!"); ReDisplayChesseBoard(); return true; } else if (SuanFa.longlink(r, c)) {// 长连 javax.swing.JOptionPane.showMessageDialog(mu, "长连禁手!黑子输了!!"); ReDisplayChesseBoard(); return true; } } return false; }