注:除机器人算法外其余借鉴于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;imaxOpponentScore){
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)=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上运行的结果: