Java 五子棋
注:除機器人算法外其余借鑒於MLDN。
package MyFiveChess;
import robot.*;
import java.awt.*;
import javax.swing.*;
public class Gobang {
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
JFrame frame = new MyFiveChessFrame(new StupidRobot());
frame.setTitle("五子棋");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setVisible(true);
});
}
}
創建一個名為MyFiveChess的包,類名為Gobang,主方法中調用機器人類(StudipRobot)創建一個JFrame的框架,名為五子棋。
package MyFiveChess;
import robot.*;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
class MyFiveChessFrame extends JFrame
{
public MyFiveChessFrame(IRobot robot)
{
add(new ImageComponent(robot));
pack();
}
}
/**
* A component that displays a tiled image
*/
class ImageComponent extends JComponent implements MouseListener
{
private static final int DEFAULT_WIDTH = 500;//因為圖片的大小為500*500,
private static final int DEFAULT_HEIGHT = 500;//所以這里設置組件大小也為500*500
int x; //原圖是沒有棋盤的,這里我們要自己畫
int y; //棋盤,故需要全局變量來獲取棋盤坐標
int allchess[][] = new int[16][16]; //構造一個數組來存取當前局勢情況
boolean isBlack = true; //0代表沒有棋,1代表黑子,2代表白子
boolean canPlay = true;//isBlack是用來說明當前應該是黑子或者白子落棋
private Image image; //canPlay是用來設置當游戲結束后不能夠再落子
private IRobot iRobot; //用來存儲傳入構造器的iRobot
private String string = "It's Black"; //圖片上當前會顯示該黑子落棋
public ImageComponent(IRobot iRobot)
{
this.iRobot = iRobot;
addMouseListener(this); //要實現當點擊鼠標就發生響應就必須實現MouseListener接口
image = new ImageIcon("background.jpg").getImage();
} //背景圖在最下面會貼出來的
public void paint(Graphics g) //構造paint方法
{
if (image == null) return;
g.drawImage(image, 0, 0, null);
g.setFont(new Font("宋體", 0, 16));
g.setFont(new Font("Times New Roman", 0, 27));
g.drawString(string, 135, 34);
for (int i = 0; i < 16; i++) {//這里是在畫棋盤(國際棋盤大小15*15)
g.drawLine(10, 50 + i*24, 369, 50 + i*24);
g.drawLine(10 + i*24, 50, 10 + i*24, 410);
}
for (int i = 0; i < 16; i++) {
for(int j = 0; j < 16; j++)
{
if(allchess[i][j] == 1) {//為1時是黑子,這里需要調用fillOval方法畫圓
int tempx = i * 24 + 10;
int tempy = j * 24 + 50;
g.fillOval(tempx-7, tempy-7, 14, 14);
}
if (allchess[i][j] == 2) {//當白子的時候需要畫一個白色的圓再添加一個黑色的框
int tempx = i * 24 + 10;
int tempy = j * 24 + 50;
g.setColor(Color.WHITE);
g.fillOval(tempx-7, tempy-7, 14, 14);
g.setColor(Color.BLACK);
g.drawOval(tempx-7, tempy-7, 14, 14);
}
}
}
}
public Dimension getPreferredSize() { return new Dimension(DEFAULT_WIDTH, DEFAULT_HEIGHT); }
SuppressWarnings("deprecation")
@Override
public void mouseClicked(MouseEvent e) {//下面就是實現游戲界面右邊的幾個功能
// TODO Auto-generated method stub
if (x>400 && x<470 && y>50 && y<80) {
int restart = JOptionPane.showConfirmDialog(this, "是否確認重新開始游戲");
if (restart == 0) {
allchess = new int[16][16];
string = "It's Black";
isBlack = true;
this.repaint();
}
}
if (x>400 && x<470 && y>100 && y<130) {
String input = JOptionPane.showInputDialog("請輸入最大下棋時間(單位分),0代表無限制:");//這部分功能沒有實現,本來是想用線程來實現一個游戲倒計時功能的,但是因為某些原因就放棄了
}
if (x>400 && x<470 && y>150 && y<180) {
JOptionPane.showMessageDialog(this, "五子棋你都不會下么,如果不會,請參見百度,謝謝!");
}
if (x>400 && x<470 && y>250 && y<280) {
int abandoning = JOptionPane.showConfirmDialog(this, "已經決定了么");
if (abandoning == 0) {
if(isBlack) JOptionPane.showMessageDialog(this, "黑方認輸");
canPlay = false;
System.exit(0);
}
}
if (x>400 && x<470 && y>300 && y<330) {
JOptionPane.showMessageDialog(this, "本軟件由楊易大佬與曾鑫開發,若有問題,請自行解決.");
}
if (x>400 && x<470 && y>350 && y<380) {
int Continue = JOptionPane.showConfirmDialog(this, "不再玩一會么");
if(Continue == 0) {
JOptionPane.showMessageDialog(this, "大爺再見,歡迎下次再來。");
System.exit(0);
}
}
}
@Override
public void mousePressed(MouseEvent e) {
// TODO Auto-generated method stub
//System.out.println("X" + e.getX());//在划線之前要確認坐標,需要輸出坐標
//System.out.println("Y" + e.getY());
if (canPlay == true) {
x = e.getX();
y = e.getY();
if (x>10 && x<369 && y>49 && y<408) {
float xxx = (float) 24.0;
x = Math.round((x - 10)/xxx);//實現坐標的4舍5入
y = Math.round((y - 49)/xxx);
if(allchess[x][y] == 0)
{
if (isBlack == true) {
allchess[x][y] = 1;
iRobot.retrieveGameBoard(allchess);//該機器人下棋時,需要將現在棋盤的局勢傳遞過去
isBlack = false;
string = "It's White";
boolean winFlag = this.checkWin();//每下一次棋時都需要檢查是否勝出
if (winFlag == true) {
JOptionPane.showMessageDialog(this, "Game over"+(allchess[x][y]==1 ? "Black" : "White") + "winned");
canPlay = false;
}
RobotAction();//機器人下棋
this.repaint();
}
}
else {
JOptionPane.showMessageDialog(this, "Please play chess in the chessboard");
}
}
}
}
void RobotAction(){
Pair pair = iRobot.getDeterminedPos();//機器人類將返回一個pair類回來
x = pair.x;
y= pair.y;
allchess[x][y] = 2;
isBlack = true;
string = "It's Black";
boolean winFlag = this.checkWin();
if (winFlag == true) {
JOptionPane.showMessageDialog(this, "Game over"+(allchess[x][y]==1 ? "Black" : "White") + " winned");
canPlay = false;
}
}
@Override
public void mouseReleased(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
}
private boolean checkWin() {//檢測當前是否由五子連線的方法,簡述一下,這個方法其實很簡單,只要我們在每一次落子的時候檢查是否由五子連線就可以確保一旦有人勝出,我們就可以馬上發現。先檢查橫線和豎線,再檢查左右斜線。
boolean flag = false; //設置的標志,當由五子連線時就返回flag=false
int count = 1; //計數當前由幾顆棋子相連
int color = allchess[x][y];
int i = 1;
while(((x+i)<16)&&color == allchess[x+i][y]) {
count++;
i++;
}
i = 1;
while(((x-i)>=1)&&color == allchess[x-i][y]) {
count++;
i++;
}
if(count>=5)
{flag = true;}
//?????ж?
int count2 = 1;
int i2 = 1;
while(((y+i2)<16)&&color == allchess[x][y+i2]) {
count2++;
i2++;
}
i = 1;
while(((y-i2)>=1)&&color == allchess[x][y-i2]) {
count2++;
i2++;
}
if(count2>=5)
{flag = true;}
int count3 = 1;
int i3 = 1;
while(((y-i3)>=1)&&((x+i3)<16)&&color == allchess[x+i3][y-i3]) {
count3++;
i3++;
}
i = 1;
while(((x-i3)>=1)&&((y+i3)<16)&&color == allchess[x-i3][y+i3]) {
count3++;
i3++;
}
if(count3>=5)
{flag = true;}
int count4 = 1;
int i4 = 1;
while(((y-i4)>=1)&&((x-i4)>=1)&&color == allchess[x-i4][y-i4]) {
count4++;
i4++;
}
i = 1;
while(((x+i4)<16)&&((y+i4)<16)&&color == allchess[x+i4][y+i4]) {
count4++;
i4++;
}
if(count4>=5)
{flag = true;}
return flag;
}
}
這段代碼有點長,但是並不難,大家仔細看下就會明白的。
下面貼出機器人的代碼:
package robot;
import java.util.Random;
public interface IRobot {
static final Random rand = new Random();
/**
* There we provide a default implementation to simulate robot's behavior
*
* @return a {@code robot.Pair} which contains a valid (x,y) position
*/
default Pair getDeterminedPos() {
return new Pair(rand.nextInt(15) + 1, rand.nextInt(15) + 1);
}
/**
* This method is used to retrieve game board such that robot can determine its (x,y) position
* @param gameBoard the 2-dimension array to represent the game board
*/
void retrieveGameBoard(int[][] gameBoard);
}
package robot;
public class Pair {
public int x;
public int y;
public Pair(){}
public Pair(int x, int y) {
this.x = x;
this.y = y;
}
}
package robot;
public class StupidRobot implements IRobot {
private static final int BOARD_SIZE = 15;
private static final int ROLE_OPPONENT = 1;
private static final int ROLE_ROBOT = 2;
private static final int ROLE_NON = 0;
private static final int ORIENTATION_LR = 0;
private static final int ORIENTATION_UD = 1;
private static final int ORIENTATION_LT_RD = 2;
private static final int ORIENTATION_RT_LD = 3;
private int[][] boardRef = null;
/**
* There we provide a default implementation to simulate robot's behavior
*
* @return a {@code robot.Pair} which contains a valid (x,y) position
*/
@Override
public Pair getDeterminedPos() {
int[][] situationRobot = new int[boardRef.length][boardRef[0].length];
int[][] situationOpponent = new int[boardRef.length][boardRef[0].length];
int maxRobotScore = 0;
Pair maxRobotPoint = new Pair();
int maxOpponentScore = 0;
Pair maxOpponentPoint = new Pair();
for(int i=0;i<BOARD_SIZE;i++){
for(int k=0;k<BOARD_SIZE;k++){
if(boardRef[i][k]!=ROLE_NON){
situationOpponent[i][k]=situationRobot[i][k]=0;
}else{
boardRef[i][k] = ROLE_OPPONENT;
situationOpponent[i][k] = evaluateScore(ROLE_OPPONENT,i,k);
boardRef[i][k]=ROLE_NON;
if(situationOpponent[i][k]>maxOpponentScore){
maxOpponentScore = situationOpponent[i][k];
maxOpponentPoint.x = i;
maxOpponentPoint.y = k;
}
boardRef[i][k]=ROLE_ROBOT;
situationRobot[i][k]=evaluateScore(ROLE_ROBOT,i,k);
boardRef[i][k]=ROLE_NON;
if(situationRobot[i][k]>maxRobotScore){
maxRobotScore = situationRobot[i][k];
maxRobotPoint.x = i;
maxRobotPoint.y = k;
}
}
}
}
if(maxRobotScore > maxOpponentScore || maxRobotScore==Integer.MAX_VALUE){
return maxRobotPoint;
}else{
return maxOpponentPoint;
}
}
/**
* This method is used to retrieve game board such that robot can determine its (x,y) position
*
* @param gameBoard the 2-dimension array to represent the game board
*/
@Override
public void retrieveGameBoard(int[][] gameBoard) {
boardRef = gameBoard;
}
/**
* The policy of evaluating was referred to https://www.cnblogs.com/maxuewei2/p/4825520.html
* @param role the role of current player
* @param x position x
* @param y position y
* @param orientation orientation of determining line
* @return
*/
private int patternRecognition(int role, int x,int y,int orientation){
StringBuilder sb = new StringBuilder();
if(orientation==ORIENTATION_LR){
int leftBound = (x - 4)>=0?x-4:0;
int rightBound = (x +4)<BOARD_SIZE?x+4:BOARD_SIZE-1;
for(int i=leftBound;i<=rightBound;i++){
sb.append(boardRef[i][y]);
}
}else if(orientation == ORIENTATION_UD){
int bottomBound = (y+4)<BOARD_SIZE?y+4:BOARD_SIZE-1;
int topBound = (y-4)>=0?y-4:0;
for(int i=topBound;i<=bottomBound;i++){
sb.append(boardRef[x][i]);
}
}else if(orientation== ORIENTATION_LT_RD){
int leftBound = 0,rightBound = 0,bottomBound = 0,topBound = 0;
for(int i=1;i<=4;i++){
leftBound = x-i;
topBound = y-i;
if(leftBound<0||topBound<0){
leftBound++;
topBound++;
break;
}
}
for(int k=1;k<=4;k++){
rightBound = x+k;
bottomBound = y+k;
if(rightBound>BOARD_SIZE||bottomBound>BOARD_SIZE){
rightBound--;
bottomBound--;
break;
}
}
for(int i=topBound,k=leftBound;i<=bottomBound && k<=rightBound;i++,k++){
sb.append(boardRef[k][i]);
}
}else if(orientation== ORIENTATION_RT_LD){
int leftBound = 0,rightBound = 0,bottomBound = 0,topBound = 0;
for(int i=1;i<=4;i++){
rightBound = x+i;
topBound = y-i;
if(rightBound>BOARD_SIZE||topBound<0){
rightBound--;
topBound++;
break;
}
}
for(int k=1;k<=4;k++){
leftBound = x-k;
bottomBound = y+k;
if(leftBound<0||bottomBound>BOARD_SIZE){
leftBound++;
bottomBound--;
break;
}
}
for(int i=topBound,k=rightBound;i<=bottomBound && k>=leftBound;i++,k--){
sb.append(boardRef[k][i]);
}
}
String str = sb.toString();
if(str.contains(role == ROLE_ROBOT ? "22222" : "11111")){
return Integer.MAX_VALUE;
}
if(str.contains(role == ROLE_ROBOT ? "022220" : "011110")){
return 300000;
}
if(str.contains(role == ROLE_ROBOT ? "22202" : "11101") ||
str.contains(role == ROLE_ROBOT ? "20222" : "10111")){
return 3000;
}
if(str.contains(role == ROLE_ROBOT ? "0022200" : "0011100")){
return 3000;
}
if(str.contains(role == ROLE_ROBOT ? "22022" : "11011")){
return 2600;
}
if(str.contains(role == ROLE_ROBOT ? "22220" : "11110")||
str.contains(role == ROLE_ROBOT ? "02222" : "01111")){
return 2500;
}
if(str.contains(role == ROLE_ROBOT ? "020220" : "010110")||
str.contains(role == ROLE_ROBOT ? "022020" : "011010")){
return 800;
}
if(str.contains(role == ROLE_ROBOT ? "00022000" : "00011000")){
return 650;
}
if(str.contains(role == ROLE_ROBOT ? "20022" : "10011")||
str.contains(role == ROLE_ROBOT ? "22002" : "11001")){
return 600;
}
if(str.contains(role == ROLE_ROBOT ? "20202" : "10101")){
return 550;
}
if(str.contains(role == ROLE_ROBOT ? "22200" : "11100")||
str.contains(role == ROLE_ROBOT ? "00222" : "00111")){
return 500;
}
if(str.contains(role == ROLE_ROBOT ? "0020200" : "0010100")){
return 250;
}
if(str.contains(role == ROLE_ROBOT ? "020020" : "010010")){
return 200;
}
if(str.contains(role == ROLE_ROBOT ? "22000" : "11000")||
str.contains(role == ROLE_ROBOT ? "00022" : "00011")){
return 150;
}
return 0;
}
private int evaluateScore(int role,int x, int y){
int a = patternRecognition(role,x,y,ORIENTATION_RT_LD);
int b = patternRecognition(role,x,y,ORIENTATION_LT_RD);
int c = patternRecognition(role,x,y,ORIENTATION_UD);
int d = patternRecognition(role,x,y,ORIENTATION_LR);
return Math.max(Math.max(Math.max(a,b),c),d);
}
}
好吧,機器人類的代碼不是我寫的,所以我沒有發言權,如果大家想實現的是人機對棋就研究一下這段代碼,如果不是那就可以跳過了,只要修改下我最開始貼出的代碼就好了。╮(╯-╰)╭
以下是再Eclipse和打包成exe上運行的結果:



