在Java中实现计算机围棋棋盘的简单方法

3

我想制作一个简单的围棋棋盘来设计一个计算机围棋游戏。

在围棋游戏中,您需要将“石头”(白色或黑色)放置在水平和垂直线交叉的位置。

有什么简单的方法可以限制用户将其石头放置在其他位置吗?
也许我只是没有看到简单的解决方案。

编辑
我想更好地重新表达我的问题: 我想知道如何制作围棋棋盘的背景图像,以便我可以在水平和垂直线的交点上放置石头。我考虑获取一个普通的围棋棋盘图像,然后在实际渲染石头时,找到正确的像素位置来放置石头。但是,这个解决方案似乎并不是最好的解决方案,因为我需要担心石头图像的大小,并且在扩展或缩小棋盘窗口时考虑比例。


@skaffman:你真的认为这是一道作业题吗?我很失望。也许是因为这个游戏是围棋。 - CPerkins
Skaffman显然删除了他的评论,但这是我的个人项目。我正在尝试实现一个Go游戏引擎,同时应用和学习一些概率AI技术。 - codingbear
很高兴听到这个消息,codingbear。skaffman编辑了你的问题,我假设他重新标记为“作业”。既然它不是作业,我将删除该标签:它往往会让人们感到不适。请随时向我们更新你的进展。 - CPerkins
有了这个做法,针对您的编辑,为了处理图像大小和调整棋盘窗口大小,您可能会问自己以下问题:您想支持任意大小吗?您是否关心纵横比?你看,这些问题的答案将帮助您发现如何处理调整大小。 - CPerkins
5个回答

2

我会在这里使用枚举:

enum Stone {BLAC, WHITE, NONE}

class Board {
    static final int dimension = 19;
    private Stone[][] board = new Stone[dimension][dimension];
    // ...
}

编辑:

这是一个小的演示(没有调整棋盘大小和图片,只有经典的图形!):

import java.awt.*;
import java.awt.event.*;
import java.util.Random;
import javax.swing.*;

public class GoBoardDemo {
    public static void main(String[] args) {
        JFrame frame = new JFrame();
        frame.setLayout(new BorderLayout());
        frame.add(new GoPanel(19), BorderLayout.CENTER);
        frame.setSize(600, 625);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setResizable(false);
        frame.setVisible(true);
    }
}


@SuppressWarnings("serial")
class GoPanel extends JPanel {

    Square[][] board;
    boolean whiteToMove;

    GoPanel(int dimension) {
        board = new Square[dimension][dimension];
        whiteToMove = true;
        initBoard(dimension);
    }

    private void initBoard(int dimension) {
        super.setLayout(new GridLayout(dimension, dimension));
        for(int row = 0; row < dimension; row++) {
            for(int col = 0; col < dimension; col++) {
                board[row][col] = new Square(row, col);
                super.add(board[row][col]);
            }
        }
        repaint();
    }

    private class Square extends JPanel {

        Stone stone;
        final int row;
        final int col;

        Square(int r, int c) {
            stone = Stone.NONE;
            row = r;
            col = c;
            super.addMouseListener(new MouseAdapter(){
                @Override
                public void mouseClicked(MouseEvent me) {
                    if(stone != Stone.NONE) return;
                    stone = whiteToMove ? Stone.WHITE : Stone.BLACK;
                    whiteToMove = !whiteToMove;
                    repaint();
                }
            });
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            int w = super.getWidth();
            int h = super.getHeight();
            g.setColor(new Color(0xB78600));
            g.fillRect(0, 0, w, h);
            g.setColor(Color.BLACK);   
            if(row == 0 || row == board.length-1 || col == 0 || col == board.length-1) {
                if(col == 0) {
                    g.drawLine(w/2, h/2, w, h/2);
                    if(row == 0) g.drawLine(w/2, h/2, w/2, h);
                    else if(row == 18) g.drawLine(w/2, h/2, w/2, 0);
                    else g.drawLine(w/2, 0, w/2, h);
                }
                else if(col == 18) {
                    g.drawLine(0, h/2, w/2, h/2);
                    if(row == 0) g.drawLine(w/2, h/2, w/2, h);
                    else if(row == 18) g.drawLine(w/2, h/2, w/2, 0);
                    else g.drawLine(w/2, 0, w/2, h);
                }
                else if(row == 0) {
                    g.drawLine(0, h/2, w, h/2);
                    g.drawLine(w/2, h/2, w/2, h);
                }
                else {
                    g.drawLine(0, h/2, w, h/2);
                    g.drawLine(w/2, h/2, w/2, 0);
                }
            } else {
                g.drawLine(0, h/2, w, h/2);
                g.drawLine(w/2, 0, w/2, h);
            }
            stone.paint(g, w);
        }
    }
}

enum Stone { 

    BLACK(Color.BLACK), WHITE(Color.WHITE), NONE(null);

    final Color color;
    private final static Random rand = new Random();

    private Stone(Color c) {
        color = c;
    }

    public void paint(Graphics g, int dimension) {
        if(this == NONE) return;
        Graphics2D g2d = (Graphics2D)g;
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 
                RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setColor(color);
        int x = 5;
        g2d.fillOval(rand.nextInt(x), rand.nextInt(x), dimension-x, dimension-x);
    }
}

里面的随机内容是实现CPerkins所说的有机感。嗯,我尽力了。


太棒了。正是我想知道的。我应该开始学习更多关于图形方面的知识。这非常有趣。 - codingbear

2
如何实现位置已经有答案了。但你所问的是如何限制用户的放置,这是不同的事情。
可能你正在计划创建一个绘图区域,并且会检测鼠标点击。如何将棋子放置限制在交叉点上就像绘图程序实现“捕捉”(即“捕捉到网格”)功能一样。
基本上,这意味着将每个维度中落在一定像素范围内的点击缩小为落在其中一条线上的点击。在两个维度中这样做,可以将移动的点击放置在一个点上。
本质上,你要做的就是找到每个单元格的中点,任何落在一个单元格中点和下一个单元格中点之间的点击都将被视为在这些中点之间的线上。
因此,基本上这将是这样的:

    // Since traditionally, boards are not square, you'd call this twice: once with the
    //   width in X for the X click, and once again for Y.
    // Naturally, you'll want to accomodate e.g., 9x9 boards for short games.
    int snapClick (int gridWidth, int clickPos, int numCells) {
        int cellWidth = (int) (gridWidth / numCells);
        int snappedClick = Math.round ((clickPos + (cellWidth/2)) / cellWidth);
        return snappedClick;
    }

顺便提一下,在实际游戏中,棋子并不是完美地放置的,所以游戏有一种令人愉悦的有机感。您可能需要考虑一个解决方案,不仅存储网格中棋子的位置,还要在屏幕上略微不完美地对齐,以供显示。


哈,从未想过游戏的“有机感”,但这很有道理。好知道! - Bart Kiers
尽管他没有回答我所有的问题,但这真的很酷! - codingbear

1
不要想着线条,要想着交点。这些交点可以表示为一个网格,就像棋盘上的方格形成一个网格一样。
在Java中,最简单的表示方法是使用一个二维数组。类似于以下内容:
Location[MAX_Y][MAX_X];

其中Location是一个对象,表示交叉点并保存对放置在那里的棋子(如果有)的引用。


1
int[][] grid = new int[19][19];

这是你的网格。数组中的每个位置都代表着一条线的交叉点。


0
如果你的主要目标是实现AI,我建议实现 Go Text Protocol,并利用众多现有的GTP前端。 如果你想构建GUI作为学习经验,那就去尝试吧(没有双关语)。 作为一名编写商业围棋GUI的人,我警告你,它比看起来难得多(与之相比,国际象棋应用程序会更简单)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接