⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 blackwhite.java

📁 实现基本的人机对弈 算法比较简单 有悔棋功能
💻 JAVA
字号:
package com;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.LineBorder;

public class BlackWhite extends JFrame
{
	public BlackWhite()
	{
		setTitle ( "黑白棋" );
		getContentPane().add ( new Chessboard() );
	}

	public static void main ( String[] args )
	{
		BlackWhite frame = new BlackWhite();
		frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
		frame.setSize (600,600);
		
		
		//框架居中
		Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
		int screenWidth = screenSize.width;
		int screenHeight = screenSize.height;
		int x = ( screenWidth - frame.getWidth() ) / 2;
		int y = ( screenHeight - frame.getHeight() ) /2;
		frame.setLocation ( x, y );
		frame.setVisible ( true );
	}
}

class Chessboard extends JPanel implements MouseListener
{
	private int edge;
	//chess[x][y]为O代表空,为B代表有黑子,为W代表有白子,为E代表有空子即当前可以在此处下子
	private char[][] chess;
	//mark记录电脑刚刚在哪里下了棋子
	private int[] mark;
	private Graphics g;
	private char whoseTurn;
	private int steps;

	public Chessboard()
	{	
		edge = 60;
		chess = new char[8][8];
		for ( int i = 0; i < 8; i++ )
			for ( int j = 0; j < 8; j++ )
				chess[i][j] = 'O';
		mark = new int[2];
		mark[0] = -1;
		mark[1] = -1;
		whoseTurn = 'B';	
		setBorder ( new LineBorder ( Color.black, 1 ) );

		//在棋盘正中摆好四枚棋子,黑白各2枚,交叉放置
		chess[3][3] = 'B';
		chess[4][4] = 'B';
		chess[3][4] = 'W';
		chess[4][3] = 'W';
		chess[3][5] = 'E';
		chess[5][3] = 'E';
		chess[4][2] = 'E';
		chess[2][4] = 'E';
		
		steps = 4;
		addMouseListener ( this );
	}

	protected void paintComponent ( Graphics g )
	{
		super.paintComponent ( g );
		g.setColor ( Color.cyan );
		g.fillRect ( 0, 0, getWidth(), getHeight() );
		//画出棋盘
		g.setColor ( Color.black );
		for ( int i = 1; i <= 8; i++ )
			g.drawLine ( edge * i, 0, edge * i, edge * 8 );
		for ( int j = 1; j <= 8; j++ )
			g.drawLine ( 0, edge * j, edge * 8, edge * j );

		//根据chess画出棋子以及空子
		for ( int i = 0; i < 8; i++ )
			for ( int j = 0; j < 8; j++ )
			{
				if ( chess[i][j] == 'B' )
				{
					g.setColor ( Color.black );
					g.fillOval ( i*edge + 2, j*edge + 2, edge - 4, edge - 4 );
				}
				else if ( chess[i][j] == 'W' )
				{
					g.setColor ( Color.white );
					g.fillOval ( i*edge + 2, j*edge + 2, edge - 4, edge - 4 );
				}
				else if ( chess[i][j] == 'E' )
				{
					g.setColor ( Color.red );
					g.drawOval ( i*edge + 2, j*edge + 2, edge - 4, edge - 4 );
				}
			}

	}

	//在该格子处落子
	public void drawTurn ( int x, int y )
	{
		g = getGraphics();
		if ( whoseTurn == 'B' )
			g.setColor ( Color.black );
		else
			g.setColor ( Color.white );
		g.fillOval ( x*edge + 2, y*edge + 2, edge - 4, edge - 4 );
		g.dispose();
		chess[x][y] = whoseTurn;
	}

	//标记该格子刚刚落子,在棋子外画一个黄颜色圈
	public void drawMark ( int x, int y )
	{
		g = getGraphics();
		g.setColor ( Color.yellow );
		g.drawOval ( x*edge + 1, y*edge + 1, edge - 2, edge - 2 );
		g.dispose();
		mark[0] = x;
		mark[1] = y;
	}

	//清除该格子的标记
	public void throwMark ()
	{
		int x = mark[0];
		int y = mark[1];
		g = getGraphics();
		g.setColor ( Color.cyan );
		g.drawOval ( x*edge + 1, y*edge + 1, edge - 2, edge - 2 );
		g.dispose();
	}

	//恢复该格子为背景颜色
	public void returnBackGround ( int x, int y )
	{
		g = getGraphics();
		g.setColor ( Color.cyan );
		g.fillRect ( x*edge + 1, y*edge + 1, edge - 2, edge - 2 );
		g.dispose();
		chess[x][y] = 'O';
	}

	//根据chess画出所有空子
	public void drawAllEmpty ()
	{
		g = getGraphics ();
		g.setColor ( Color.red );
		for ( int i = 0; i < 8; i++ )
			for ( int j = 0; j < 8; j++ )
				if ( chess[i][j] == 'E' )
					g.drawOval ( i*edge + 2, j*edge + 2, edge - 4, edge - 4 );
		g.dispose ();
	}

	//查找当前可以下子的位置并记录,返回这种位置的个数
	public int countAableToClick ()
	{
		int count = 0;
		for ( int i = 0; i < 8; i++ )
			for ( int j = 0; j < 8; j++ )
			{
				if ( chess[i][j] == 'O' )	//只有该格为空时才能继续判断是否ableToClick
					if ( ableToClick ( i, j ) )
						count++;
			}
		return count;
	}

	//判断该格子是否可以落子,如果可以就记录,已经判断过该格子一定为空
	public boolean ableToClick ( int x, int y )
	{
		//eightDirection储存"上,右上,右,右下,下,左下,左,左上"八个方向
		int[][] eightDirection = { {0, -1} , {1, -1} , {1, 0} , {1, 1} , {0, 1} , {-1, 1} , {-1, 0} , {-1, -1} };

		for ( int i = 0; i < 8; i++ )
			if ( ableThisDirection ( x, y, eightDirection[i] ) )
			{
				chess[x][y] = 'E';
				return true;
			}

		return false;		
	}

	//根据whoseTurn判断该格子在方向direction上是否可以吃子,已经判断过该格子一定为空
	public boolean ableThisDirection ( int x, int y, int[] direction )
	{
		//nextX, nextY是x, y在方向direction上的下一个格子的坐标
		int nextX = x + direction[0];
		int nextY = y + direction[1];
		
		//nextX, nextY超出棋盘
		if ( nextX < 0 || nextX > 7 || nextY < 0 || nextY > 7 )	
			return false;

		//坐标nextX, nextY的格子是对方棋子时才有可能为true,即落棋时要求必须放在与对方棋子相邻的空位上
		else if ( chess[nextX][nextY] == whoseTurn || chess[nextX][nextY] == 'O' || chess[nextX][nextY] == 'E' )
			return false;

		//此时坐标nextX, nextY的格子是对方棋子,用enemy记录对方棋子
		else
		{
			char enemy = (whoseTurn == 'B') ? 'W' : 'B';
			while ( chess[nextX][nextY] == enemy )
			{
				nextX = nextX + direction[0];
				nextY = nextY + direction[1];
				//nextX, nextY超出棋盘
				if ( nextX < 0 || nextX > 7 || nextY < 0 || nextY > 7 )	
					return false;
			}
			if ( chess[nextX][nextY] == whoseTurn )
				return true;
			else
				return false; 
		}
	}

	//根据whoseTurn以及坐标重画棋盘,即完成“吃子”的功能
	public void repaintChess ( int x, int y )
	{
		int[][] eightDirection = { {0, -1} , {1, -1} , {1, 0} , {1, 1} , {0, 1} , {-1, 1} , {-1, 0} , {-1, -1} };

		for ( int i = 0; i < 8; i++ )
			if ( ableThisDirection ( x, y, eightDirection[i] ) )
			{
				int nextX = x + eightDirection[i][0];
				int nextY = y + eightDirection[i][1];
				char enemy = (whoseTurn == 'B') ? 'W' : 'B';
				while ( chess[nextX][nextY] == enemy )
				{
					drawTurn ( nextX, nextY );
					nextX = nextX + eightDirection[i][0];
					nextY = nextY + eightDirection[i][1];
				}
			}
	}

	//只在chess上吃子而不改变棋盘
	public void eatAround ( int x, int y )
	{
		int[][] eightDirection = { {0, -1} , {1, -1} , {1, 0} , {1, 1} , {0, 1} , {-1, 1} , {-1, 0} , {-1, -1} };

		for ( int i = 0; i < 8; i++ )
			if ( ableThisDirection ( x, y, eightDirection[i] ) )
			{
				int nextX = x + eightDirection[i][0];
				int nextY = y + eightDirection[i][1];
				char enemy = (whoseTurn == 'B') ? 'W' : 'B';
				while ( chess[nextX][nextY] == enemy )
				{
					chess[nextX][nextY] = whoseTurn;
					nextX = nextX + eightDirection[i][0];
					nextY = nextY + eightDirection[i][1];
				}
			}
	}

	//游戏结束,判断胜利者
	public void gameOver()
	{
		//统计黑白棋数
		int numberBlack = 0;
		int numberWhite = 0;
		for ( int i = 0; i < 8; i++ )
			for ( int j = 0; j < 8; j++ )
			{
				if ( chess[i][j] == 'B' )
					numberBlack++;
				else if ( chess[i][j] == 'W' )
					numberWhite++;
			}
		if ( numberBlack == numberWhite )
		{
			JOptionPane.showMessageDialog(null,"黑棋数:" + numberBlack + ", 白棋数:" + numberWhite + "\n平局",
				"提示信息",JOptionPane.INFORMATION_MESSAGE);
		}
		else if ( numberBlack > numberWhite )
		{
			JOptionPane.showMessageDialog(null,"黑棋数:" + numberBlack + ", 白棋数:" + numberWhite + "\n黑棋胜",
				"提示信息",JOptionPane.INFORMATION_MESSAGE);
		}
		else
		{
			JOptionPane.showMessageDialog(null,"黑棋数:" + numberBlack + ", 白棋数:" + numberWhite + "\n白棋胜",
				"提示信息",JOptionPane.INFORMATION_MESSAGE);
		}
		
		JOptionPane.showMessageDialog(null,"玩的开心吗?是否再来一局吧!",
			"提示信息",JOptionPane.INFORMATION_MESSAGE);
		
		//清空棋盘,重新开局
		for ( int i = 0; i < 8; i++ )
			for ( int j = 0; j < 8; j++ )
				chess[i][j] = 'O';
		whoseTurn = 'B';	
		chess[3][3] = 'B';
		chess[4][4] = 'B';
		chess[3][4] = 'W';
		chess[4][3] = 'W';
		chess[3][5] = 'E';
		chess[5][3] = 'E';
		chess[4][2] = 'E';
		chess[2][4] = 'E';		
		steps = 4;
		repaint();
	}

	//电脑下一步棋(白字)
	public void computerPlay ()
	{
		steps++;
		int[] placeToClick = new int[2];
		placeToClick = findClickPlace ();
		//恢复所有空子为背景颜色
		for ( int i = 0; i < 8; i++ )
			for ( int j = 0; j < 8; j++ )
				if ( chess[i][j] == 'E' )
					returnBackGround ( i, j );
		//在该位置落子
		drawTurn ( placeToClick[0], placeToClick[1] );
		//标记该位置
		drawMark ( placeToClick[0], placeToClick[1] );
		//吃子
		repaintChess ( placeToClick[0], placeToClick[1] );
		if ( steps == 64 )
		{
			JOptionPane.showMessageDialog(null,"棋盘已满,游戏结束",
					"提示信息",JOptionPane.INFORMATION_MESSAGE);
			gameOver();
		}
		else
		{
			//更改whoseTurn
			whoseTurn = 'B';
			//查找当前可以下子的位置
			int numberAbleToClick = countAableToClick ();
			//如果黑棋有可以下的位置,则画出可下的位置并等待mouseClick事件,如果黑棋无处可下,则电脑连续落子
			if ( numberAbleToClick != 0 )
				drawAllEmpty ();
			else
			{
				JOptionPane.showMessageDialog(null,"黑棋无子可下,白棋可连续落子",
					"提示信息",JOptionPane.INFORMATION_MESSAGE);
				//更改whoseTurn
				whoseTurn = 'W';
				//查找当前可以下子的位置
				numberAbleToClick = countAableToClick ();
				//如果白棋有棋可下,则递归调用computerPlay
				if ( numberAbleToClick != 0 )
					computerPlay ();

				else	//白棋也无子可下
				{
					JOptionPane.showMessageDialog(null,"双方均无子可下,游戏结束",
						"提示信息",JOptionPane.INFORMATION_MESSAGE);
					gameOver();
				}
			}
		}
	}

	//根据某种智能方法找到应该落子的位置,这里让敌人下一步可以走的棋的位置的数目尽量少
	public int[] findClickPlace ()
	{
		//tempPlace用来存放找到的坐标
		int[] tempPlace = new int[2];
		//tempChess储存当前chess状态,便于随时恢复chess
		char[][] tempChess = new char[8][8];
		for ( int i = 0; i < 8; i++ )
			for ( int j = 0; j < 8; j++ )
				tempChess[i][j] = chess[i][j];
		//leastNumberToClick记录下一步走tempPlace的话,留给对手下棋位置的最小数目
		int leastNumberToClick = 100;

		for ( int row = 0; row < 8; row++ )
			for ( int col = 0; col < 8; col++ )
			{
				if ( chess[row][col] == 'E' )
				{
					//在该位置落子
					chess[row][col] = 'W';
					for ( int m = 0; m < 8; m++ )
						for ( int n = 0; n < 8; n++ )
							if ( chess[m][n] == 'E' )
								chess[m][n] = 'O';
					//吃子
					eatAround ( row, col );
					//更改whoseTurn
					whoseTurn = 'B';
					//查找敌人可以下子的位置的数目
					int numberAbleToClick = countAableToClick ();
					//更新leastNumberToClick
					if ( leastNumberToClick > numberAbleToClick )
					{
						leastNumberToClick = numberAbleToClick;
						tempPlace[0] = row;
						tempPlace[1] = col;
					}
					//恢复whoseTurn
					whoseTurn = 'W';
					//恢复chess
					for ( int m = 0; m < 8; m++ )
						for ( int n = 0; n < 8; n++ )
							chess[m][n] = tempChess[m][n];
				}
			}
		return tempPlace;
	}

	//人下一步棋(黑子)
	public void humanPlay ( int x, int y )
	{
		steps++;
		//恢复所有空子为背景颜色
		for ( int i = 0; i < 8; i++ )
			for ( int j = 0; j < 8; j++ )
				if ( chess[i][j] == 'E' )
					returnBackGround ( i, j );
		//清除之前的标记
		throwMark ();
		//在该位置落子
		drawTurn ( x, y );
		//吃子
		repaintChess ( x, y );
		if ( steps == 64 )
		{
			JOptionPane.showMessageDialog(null,"棋盘已满,游戏结束",
					"提示信息",JOptionPane.INFORMATION_MESSAGE);
			gameOver();
		}
		else
		{
			//更改whoseTurn
			whoseTurn = 'W';
			//查找当前可以下子的位置
			int numberAbleToClick = countAableToClick ();

			//如果电脑有可以下的位置,则让电脑下,如果电脑无处可下,则黑子连续落子
			if ( numberAbleToClick == 0 )
			{
				JOptionPane.showMessageDialog(null,"白棋无子可下,黑棋可连续落子",
					"提示信息",JOptionPane.INFORMATION_MESSAGE);
				//更改whoseTurn
				whoseTurn = 'B';
				//查找当前可以下子的位置
				numberAbleToClick = countAableToClick ();
				//如果黑棋有棋可下,则画出可下的位置并等待下一个mouseClick事件
				if ( numberAbleToClick != 0 )
					drawAllEmpty ();
				
				else	//黑棋也无子可下
				{
					JOptionPane.showMessageDialog(null,"双方均无子可下,游戏结束",
						"提示信息",JOptionPane.INFORMATION_MESSAGE);
					gameOver();
				}
			}
		}
	}

	public void mouseClicked ( MouseEvent e )
	{
		//获得鼠标位置
		int xco = (int)e.getX() / edge;
		int yco = (int)e.getY() / edge;
		if ( chess[xco][yco] == 'E' )	//该位置可以落子
		{
			humanPlay ( xco, yco );
			if ( whoseTurn == 'W' )
				computerPlay ();
		}	
	}

	public void mousePressed ( MouseEvent e ) {}

	public void mouseReleased ( MouseEvent e ) {}

	public void mouseEntered ( MouseEvent e ) {}

	public void mouseExited ( MouseEvent e ) {}
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -