java - 六边形生命游戏Swing实现

标签 java multithreading swing

我正在尝试基于六边形网格实现康威生命游戏。除了线程处理不正确之外,一切都正常:每次运行应用程序时,我都会获得不同的结果,所以我认为这是与不同步线程有关的问题。我是java新手,所以收到任何建议都会很有帮助。谢谢。

package hexautamata;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Arrays;
import java.util.Scanner;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.*;

/**
 *
 * @author Walter
 */
public class LifeEngine extends JPanel implements MouseListener, ComponentListener, Runnable {
    private final int BOARD_WIDTH = 29;
    private final int BOARD_HEIGHT = 24;
    private final int L_ON = 1;
    private final int L_OFF = 2;
    private final int NUM_HEX_CORNERS = 6;
    private final int CELL_RADIUS = 16;

    private int[][] mCells = new int[BOARD_HEIGHT][BOARD_WIDTH];

    private int[] mCornersX = new int[NUM_HEX_CORNERS];
    private int[] mCornersY = new int[NUM_HEX_CORNERS];
    HexGridCell mCellMetrics = new HexGridCell(CELL_RADIUS);

    public LifeEngine(){
        //setBounds(0, 0, BOARD_WIDTH * mCellMetrics.WIDTH, BOARD_HEIGHT * mCellMetrics.HEIGHT + CELL_RADIUS);
        addMouseListener(this); 
        fillCells();
        setBackground(Color.WHITE); 
    }

    private void fillCells(){
        try {
            BufferedReader reader = new BufferedReader(new FileReader(new File("C:\\Users\\user\\Documents\\NetBeansProjects\\HexAutamata\\src\\hexautamata\\HEXAGON")));
            String line;
            int i = 0, j = 0; 
            while((line = reader.readLine()) != null){
                Scanner scanner = new Scanner(line);
                while(scanner.hasNext()){
                    mCells[i][j] = scanner.nextInt();
                    ++j;
                }
                i++;
                j = 0;
            }
        } catch (FileNotFoundException ex) {
            Logger.getLogger(LifeEngine.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(LifeEngine.class.getName()).log(Level.SEVERE, null, ex);
        }
    }


    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g); 
        for (int j = 0; j < BOARD_HEIGHT; j++) {
            for (int i = 0; i < BOARD_WIDTH; i++) {
                mCellMetrics.setCellIndex(i, j);
                if (mCells[j][i] != 0) {
                    mCellMetrics.computeCorners(mCornersX, mCornersY);
                    g.setColor((mCells[j][i] == L_ON) ? Color.ORANGE : Color.GRAY);
                    g.fillPolygon(mCornersX, mCornersY, NUM_HEX_CORNERS);
                    g.setColor(Color.BLACK);
                    g.drawPolygon(mCornersX, mCornersY, NUM_HEX_CORNERS);
                }
            }
        }
        Graphics2D g2 = (Graphics2D) g;
        g2.setPaint(Color.RED); 
        g2.setStroke(new BasicStroke(0x8)); 
        g2.drawOval(55, 30, 642, 642);
    }

    private boolean isInsideBoard(int i, int j) {
        return i >= 0 && i < BOARD_HEIGHT && j >= 0 && j < BOARD_WIDTH
                && mCells[i][j] != 0;
    }

    private void toggleCell(int i, int j) {
        mCells[j][i] = (mCells[j][i] == L_ON) ? L_OFF : L_ON;
    }

    public void resetGame() {
        for (int j = 0; j < BOARD_HEIGHT; j++) {
            for (int i = 0; i < BOARD_WIDTH; i++) {
                if (mCells[j][i] == L_OFF) {
                    mCells[j][i] = L_ON;
                }
            }
        }
        repaint();
    }

    @Override
    public void mouseReleased(MouseEvent arg0) {
        mCellMetrics.setCellByPoint(arg0.getX(), arg0.getY());
        int clickI = mCellMetrics.getIndexI();
        int clickJ = mCellMetrics.getIndexJ();
        if (isInsideBoard(clickJ, clickI)) {
            toggleCell(clickI, clickJ);
        }
        this.repaint();
    }

    @Override
    public void mouseClicked(MouseEvent arg0) {
        mCellMetrics.setCellByPoint(arg0.getX(), arg0.getY());
        int clickI = mCellMetrics.getIndexI();
        int clickJ = mCellMetrics.getIndexJ();
        //System.out.println("i = " + clickI + " j = " + clickJ);
    }

    @Override
    public void mouseEntered(MouseEvent arg0) {
    }

    @Override
    public void mouseExited(MouseEvent arg0) {
    }

    @Override
    public void mousePressed(MouseEvent arg0) {
    }

    @Override
    public void componentResized(ComponentEvent e) {
    }

    @Override
    public void componentMoved(ComponentEvent e) {
    }

    @Override
    public void componentShown(ComponentEvent e) {
    }

    @Override
    public void componentHidden(ComponentEvent e) {
    }

    private synchronized void setCellIndex(int j, int i){
        mCellMetrics.setCellIndex(j, i);
    }

    private synchronized int getIndexI(){
        return mCellMetrics.getIndexI();
    }

    private synchronized int getIndexJ(){
        return mCellMetrics.getIndexJ();
    }


    @Override
    public void run() {
        int[][] buffer = new int[mCells.length][mCells[0].length];
        for(int i = 0; i < mCells.length; ++i){
            buffer[i] = Arrays.copyOf(mCells[i], mCells[i].length);
        }

        for(int i = 0; i < BOARD_HEIGHT; ++i){ 
            for(int j = 0; j < BOARD_WIDTH; ++j){
        synchronized(mCellMetrics){
                int neigbourCounter = 0;
                mCellMetrics.setCellIndex(j, i);
                int clickI = mCellMetrics.getIndexI();
                int clickJ = mCellMetrics.getIndexJ();
                System.out.println(Thread.currentThread().getName() + " i = " + clickI + " j = " + clickJ);
                if(isInsideBoard(clickJ, clickI)) {
                    for(int k = 0; k < 6; k++){
                        //System.out.println(Thread.currentThread().getName() + " i = " + mCellMetrics.getIndexI() + " j = " +mCellMetrics.getIndexJ());
                        int nI = mCellMetrics.getNeighborI(k);
                        int nJ = mCellMetrics.getNeighborJ(k); 
                        if(isInsideBoard(nJ, nI) && mCells[nJ][nI] == 2) {
                            System.out.println("And its neigbours : i = " + nI + " j = " + nJ);
                            neigbourCounter++;
                        }
                    }
                    if(mCells[i][j] == L_ON){
                        if(neigbourCounter == 3){
                            buffer[i][j] = L_OFF;
                        }
                    } else if(mCells[i][j] == L_OFF){
                        if(neigbourCounter > 3 || neigbourCounter < 2){
                            buffer[i][j] = L_ON;
                        }
                    }
                }
            }
        }
        }
        for(int i = 0; i < buffer.length; ++i){
            mCells[i] = Arrays.copyOf(buffer[i], buffer[i].length);
        }
        repaint();
        try {
            Thread.sleep(5000);
            run(); 
        } catch(InterruptedException e){}
    }
}

package hexautamata;

/**
 *
 * @author Walter
 */
public class HexGridCell {
    private final int[] NEIGHBORS_DI = { 0, 1, 1, 0, -1, -1 };
    private final int[][] NEIGHBORS_DJ = { 
            { -1, -1, 0, 1, 0, -1 }, { -1, 0, 1, 1, 1, 0 } };

    private final int[] CORNERS_DX; // array of horizontal offsets of the cell's corners
    private final int[] CORNERS_DY; // array of vertical offsets of the cell's corners
    private final int SIDE;

    private int mX = 0; // cell's left coordinate
    private int mY = 0; // cell's top coordinate

    private int mI = 0; // cell's horizontal grid coordinate
    private int mJ = 0; // cell's vertical grid coordinate

    /**
     * Cell radius (distance from center to one of the corners)
     */
    public final int RADIUS;
    public final int HEIGHT;
    public final int WIDTH;
    public final int NUM_NEIGHBORS = 6;

    /**
     * @param radius Cell radius (distance from the center to one of the corners)
     */
    public HexGridCell(int radius) {
        RADIUS = radius;
        WIDTH = radius * 2;
        HEIGHT = (int) (((float) radius) * Math.sqrt(3));
        SIDE = radius * 3 / 2;

        int cdx[] = { RADIUS / 2, SIDE, WIDTH, SIDE, RADIUS / 2, 0 };
        CORNERS_DX = cdx;
        int cdy[] = { 0, 0, HEIGHT / 2, HEIGHT, HEIGHT, HEIGHT / 2 };
        CORNERS_DY = cdy;
    }

    public int getLeft() {
        return mX;
    }

    public int getTop() {
        return mY;
    }

    public int getCenterX() {
        return mX + RADIUS;
    }

    public int getCenterY() {
        return mY + HEIGHT / 2;
    }

    public int getIndexI() {
        return mI;
    }

    public int getIndexJ() {
        return mJ;
    }

    public int getNeighborI(int neighborIdx) {
        return mI + NEIGHBORS_DI[neighborIdx];
    }

    public int getNeighborJ(int neighborIdx) {
        return mJ + NEIGHBORS_DJ[mI % 2][neighborIdx];
    }

    public void computeCorners(int[] cornersX, int[] cornersY) {
        for (int k = 0; k < NUM_NEIGHBORS; k++) {
            cornersX[k] = mX + CORNERS_DX[k];
            cornersY[k] = mY + CORNERS_DY[k];
        }
    }

    public void setCellIndex(int i, int j) {
        mI = i;
        mJ = j;
        mX = i * SIDE;
        mY = HEIGHT * (2 * j + (i % 2)) / 2;
    }

    public void setCellByPoint(int x, int y) {
        int ci = (int)Math.floor((float)x/(float)SIDE);
        int cx = x - SIDE*ci;

        int ty = y - (ci % 2) * HEIGHT / 2;
        int cj = (int)Math.floor((float)ty/(float)HEIGHT);
        int cy = ty - HEIGHT*cj;

        if (cx > Math.abs(RADIUS / 2 - RADIUS * cy / HEIGHT)) {
            setCellIndex(ci, cj);
        } else {
            setCellIndex(ci - 1, cj + (ci % 2) - ((cy < HEIGHT / 2) ? 1 : 0));
        }
    }
}

package hexautamata;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;

/**
 *
 * @author Walter
 */
public class MainToolbar extends JFrame implements ActionListener {

    private static final Dimension DEFAULT_WINDOW_SIZE = new Dimension(800, 750);
    private static final Dimension MINIMUM_WINDOW_SIZE = new Dimension(400, 400);
    private final LifeEngine lifeengine;
    private JMenuBar my_menu;
    private JMenu m_file, m_game;
    private JMenuItem m_start, m_stop, m_reset;
    private Thread game;

    public MainToolbar(){
        super("Tool bar");
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setSize(DEFAULT_WINDOW_SIZE);
        setMinimumSize(MINIMUM_WINDOW_SIZE);
        setLocation((Toolkit.getDefaultToolkit().getScreenSize().width - this.getWidth())/2,
                    (Toolkit.getDefaultToolkit().getScreenSize().height - this.getHeight())/2);
        my_menu = new JMenuBar();
        m_file = new JMenu("File");
        my_menu.add(m_file);
        m_game = new JMenu("Game");
        my_menu.add(m_game);
        m_start = new JMenuItem("Start");
        m_start.addActionListener(this); 
        m_game.add(m_start);
        m_game.add(new JSeparator());
        m_stop = new JMenuItem("Stop");
        m_stop.addActionListener(this);
        m_game.add(m_stop);
        m_game.add(new JSeparator());
        m_reset = new JMenuItem("Reset");
        m_reset.addActionListener(this);
        m_game.add(m_reset);

        setJMenuBar(my_menu);

        lifeengine = new LifeEngine(); 
        add(lifeengine); 

        setVisible(true);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        if(e.getSource().equals(m_reset)){
            lifeengine.resetGame();
        } else if(e.getSource().equals(m_start)){
            setGameBeingPlayed(true); 
        } else if(e.getSource().equals(m_stop)){
            setGameBeingPlayed(false); 
        }
    }

    public void setGameBeingPlayed(boolean isBeingPlayed){
        if(isBeingPlayed){
            m_start.setEnabled(false);
            m_stop.setEnabled(true);
            game = new Thread(lifeengine);
            game.start();
        } else {
            m_start.setEnabled(true);
            m_stop.setEnabled(false);
            game.interrupt();
        }
    }
}

package hexautamata;

import java.awt.Dimension;

/**
 *
 * @author Walter
 */
public class HexAutomata {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        MainToolbar mtb = new MainToolbar();
        mtb.setResizable(false);
    }

}

最佳答案

而不是用这个阻塞EventDispatcherThread

    try {
        Thread.sleep(5000);
        run(); 
    } catch(InterruptedException e){}

使用javax.swing.Timer并将代码添加到Timer调用的Action的actionPerformed()

关于java - 六边形生命游戏Swing实现,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23118747/

相关文章:

java - 如何组织Web服务,以便开发人员可以轻松找到它们?

java - 了解死锁

java - 使用 JGraphMenu 的菜单项的 ActionListener

java - JApplet显示

java - WebSocket握手时出错: Unexpected response code: 404 WrappedWebSocket @ VM222:161

java - 访问一个类中填充的ArrayList,另一个类中的填充

java - 如何在任务栏中创建简单的动画?

java - 我们可以在 apache Camel 中将 jmsxgroupid 与线程一起使用来解决线程 dsl 标记中的排序问题吗

Java Spring bean 创建另一个 bean 的更多实例

java - 标签错误中的文本对齐?