QQ中国象棋Java棋盘识别与自动下棋程序

QQ中国象棋(传统版)的棋盘识别程序Java源码(Eclipse工程)目前维护在Github

项目地址:https://github.com/qinjiannet/screen-chess-qq/

程序的入口main方法位于execute包下的Executor类中

自动下棋时默认采用随机行走,开发者可以通过修改ai包下的Thinker类中的getBestMove方法实现更加智能的AI

运行时,需要在QQ游戏大厅中手工开始一局象棋,然后运行Executor

由于程序采用的是截图分析获取棋盘状态的方式,因此运行过程中需要保证游戏窗口不被其他窗口遮挡

QQ中国象棋界面

部分Java源码摘录如下:

RecognitionHelper.java 用来识别QQ中国象棋的棋盘

package recognition;

import java.awt.Point;
import java.awt.image.BufferedImage;
import java.util.HashMap;

import color.ColorHelper;

import entity.Board;
import entity.Coordinate;
import entity.chess.Chess;
import entity.chess.ChessFactory;

//Author:在线疯狂
//Homepage: http://bookshadow.com
public class RecognitionHelper {

	public static final int gridSize = 48;

	public static final int chessSize = 32; // 42

	public static final int orderSize = 42;

	public static final char CHESS_NAME[] = { '车', '马', '炮', '砲', '象', '相',
			'士', '仕', '将', '帅', '兵', '卒' };
	public static final int CHESS_VAL[] = { 96, 110, 101, 81, 112, 124, 55, 70,
			97, 122, 88, 82 };

	public static final HashMap<Integer, Character> CHESS_NAME_MAP = new HashMap<Integer, Character>();

	static {
		for (int i = 0; i < CHESS_VAL.length; i++) {
			CHESS_NAME_MAP.put(CHESS_VAL[i], CHESS_NAME[i]);
		}
	}

	// Screen Coordinate
	public static int[] getStartXY(BufferedImage bi) {
		int width = (int) bi.getWidth();
		int height = (int) bi.getHeight();
		int startX = -1, startY = -1;
		for (int i = 0; i < height; i++) {
			int cnt = 0;
			for (int j = 0; j < width; j++) {
				int rgb = bi.getRGB(j, i);
				if (ColorHelper.PATTERN_SET.contains(rgb)) {
					cnt++;
				}
			}
			if (cnt > 100) {
				startX = i;
				break;
			}
		}

		for (int i = 0; i < width; i++) {
			int cnt = 0;
			for (int j = 0; j < height; j++) {
				int rgb = bi.getRGB(i, j);
				if (ColorHelper.PATTERN_SET.contains(rgb)) {
					cnt++;
				}
			}
			if (cnt > 400) {
				startY = i - 2;
				break;
			}
		}

		return new int[] { startX, startY };
	}

	public static HashMap<Integer, Integer> getChess(BufferedImage bi) {
		HashMap<Integer, Integer> hmap = new HashMap<Integer, Integer>();
		int width = bi.getWidth();
		int height = bi.getHeight();
		for (int ii = 0; ii < width; ii++) {
			for (int jj = 0; jj < height; jj++) {
				int rgb = bi.getRGB(ii, jj);
				Integer val = hmap.get(rgb);
				
				if (val == null) {
					val = 0;
				}
				hmap.put(rgb, val + 1);
			}
		}
		return hmap;
	}

	public static char getSelfColor(BufferedImage bi) {
		int startXY[] = getStartXY(bi);
		if (startXY[0] < 0 || startXY[1] < 0)
			return 0;
		int startLX = startXY[0] + 338;
		int startY = 0;
		BufferedImage lowerImage = bi.getSubimage(startY, startLX, startXY[1],
				orderSize);
		return getColor(lowerImage);
	}

	/**
	 * getOrder
	 * 
	 * @param bi
	 * @return null:error,"sr":self red,"or":opponent red, "sb":self black,
	 *         "ob":opponent black
	 */
	public static String getOrder(BufferedImage bi) {
		int startXY[] = getStartXY(bi);
		if (startXY[0] < 0 || startXY[1] < 0)
			return null;

		int startUX = startXY[0] + 90;
		int startLX = startXY[0] + 338;
		int startY = 0;
		BufferedImage upperImage = bi.getSubimage(startY, startUX, startXY[1],
				orderSize);
		BufferedImage lowerImage = bi.getSubimage(startY, startLX, startXY[1],
				orderSize);
		
		char uc = getColor(upperImage);
		char lc = getColor(lowerImage);
		String ret = null;
		if (uc != Chess.WHITE) {
			ret = "" + Board.OPPONENT + uc;
		} else if (lc != Chess.WHITE) {
			ret = "" + Board.SELF + lc;
		}
		return ret;
	}

	/**
	 * getColor
	 * 
	 * @param bi
	 * @return 'w':white,'r':red,'b':black
	 */
	public static char getColor(BufferedImage bi) {
		int blackCnt = 0;
		int redCnt = 0;
		int width = bi.getWidth();
		int height = bi.getHeight();
		for (int i = 0; i < width; i++) {
			for (int j = 0; j < height; j++) {
				int rgb = bi.getRGB(i, j);
				if (ColorHelper.BLACK == rgb) {
					blackCnt++;
				} else if (ColorHelper.RED == rgb) {
					redCnt++;
				}
			}
		}
		
		if (blackCnt > 80) {
			return 'b';
		}
		if (redCnt > 110) {
			return 'r';
		}
		return 'w';
	}

	/**
	 * GetChessStatus By BufferedImage
	 * 
	 * @param bi
	 *            Screenshot
	 * @return
	 */
	public static Board getChessBoard(BufferedImage bi) {
		if (bi == null)
			return null;

		int startXY[] = getStartXY(bi);
		if (startXY[0] < 0 || startXY[1] < 0)
			return null;

		int chessStartX = startXY[0] + 30;
		int chessStartY = startXY[1] + 30;
		
		Chess[][] ans = new Chess[10][9];
		for (int i = 0; i < 10; i++) {
			for (int j = 0; j < 9; j++) {
				int x = chessStartX + i * RecognitionHelper.gridSize;
				int y = chessStartY + j * RecognitionHelper.gridSize;
				int sx = x - RecognitionHelper.chessSize / 2;
				int sy = y - RecognitionHelper.chessSize / 2;
				BufferedImage subImage = bi.getSubimage(sy, sx,
						RecognitionHelper.chessSize,
						RecognitionHelper.chessSize);
				HashMap<Integer, Integer> chessMap = RecognitionHelper
						.getChess(subImage);
				
				Character color = null;
				Character name = null;
				Character redName = null;
				Character blackName = null;
				Integer redNum = chessMap.get(ColorHelper.RED);
				Integer blackNum = chessMap.get(ColorHelper.BLACK);
				if (redNum != null) {
					redName = RecognitionHelper.CHESS_NAME_MAP.get(redNum);
				} else if (chessMap.get(ColorHelper.BLACK) != null) {
					blackName = RecognitionHelper.CHESS_NAME_MAP.get(blackNum);
				}
				Point point = new Point(x, y);
				Chess chess = null;
				if (redName != null || blackName != null) {
					if (redName != null) {
						color = '红';
						name = redName;
					} else {
						color = '黑';
						name = blackName;
					}
					char code = Chess.name2Code(name);
					chess = ChessFactory.newChess(code);
					
					chess.setLocation(point);
					chess.setColor(Chess.color2Alphabet(color));
					chess.setCoordinate(new Coordinate(i, j));
					
					chess.setName(color + "" + name);
				}
				ans[i][j] = chess;
				
			}
			
		}
		// Identify upper generals's color
		char upColor = Chess.WHITE;
		outer: for (int i = 0; i <= 2; i++) {
			for (int j = 3; j <= 5; j++) {
				if (ans[i][j] != null && ans[i][j].getCode() == Chess.GENERAL) {
					upColor = ans[i][j].getColor();
					break outer;
				}
			}
		}
		return new Board(ans, upColor);
	}
}

ScreenshotGetter.java 用来获取屏幕截图

package recognition;

import java.awt.AWTException;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
//Author:在线疯狂
//Homepage: http://bookshadow.com
public class ScreenshotGetter {
	public BufferedImage getScreenshot() throws AWTException {
		final Robot robot = new Robot();
		Dimension dimension = Toolkit.getDefaultToolkit().getScreenSize();
		int width = (int) dimension.getWidth();
		int height = (int) dimension.getHeight();
		BufferedImage screenshot = robot.createScreenCapture(new Rectangle(0,
				0, width, height));
		return screenshot;
	}
}

ColorHelper.java 用来获取RGB格式的颜色代码

package color;

import java.util.HashSet;
//Author:在线疯狂
//Homepage: http://bookshadow.com
public class ColorHelper {

	public static final int PATTERN_ARR[] = { -2569056, -2571120, -2571112,
			-3099528, -1516360, -2573176, -2044752, -2046816, -2571104,
			-1516352, -2044760, -2573168, -3099512, -2042696, -3101576,
			-1516344, -1518408, -2573160, -3099520, -2044768, -2042704 };
	public static final HashSet<Integer> PATTERN_SET = new HashSet<Integer>();

	static {
		for (int rgb : PATTERN_ARR) {
			PATTERN_SET.add(rgb);
		}
	}

	public static final int RED_RGB[] = { 200, 8, 0 };
	public static final int RED = -3667968;
	public static final int BLACK_RGB[] = { 56, 56, 56 };
	public static final int BLACK = -13092808;

	public static int[] getRGB(int rgb) {
		int R = (rgb & 0xff0000) >> 16;
		int G = (rgb & 0xff00) >> 8;
		int B = (rgb & 0xff);
		return new int[] { R, G, B };
	}

}

 

本文链接:http://bookshadow.com/weblog/2014/10/25/qq-chess-recognition-ai-java-program/
请尊重作者的劳动成果,转载请注明出处!书影博客保留对文章的所有权利。

如果您喜欢这篇博文,欢迎您捐赠书影博客: ,查看支付宝二维码

Pingbacks已关闭。

暂无评论

张贴您的评论