首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Java生命游戏

Java生命游戏
EN

Code Review用户
提问于 2017-04-24 11:34:57
回答 1查看 590关注 0票数 2

我的Java需要一点更新,所以我将生命游戏作为一个小片段来实现。这场比赛正如期而至,但我相信还有一些改进的余地。

下面是代码(跳过接口、包和导入)

代码语言:javascript
复制
/**
 * Controller Implementation responsible for starting the game and keeping it
 * running.
 */
public class Controller implements IController {
    private IModel model;
    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

    /**
     * Initialize a new controller and implicitly a model and view as well.
     * Start a loop updating the game.
     */
    public Controller() {
        this.model = new Model(50, 50, this);
        this.model.initializeModel();
    }

    @Override
    public void nextTick() {
        this.model.updateModel();
    }

    @Override
    public void randomSeed() {
        this.model.initializeModel();
    }

    @Override
    public void start() {
        final Runnable tickRunner = new Runnable() {
            @Override
            public void run() {
                nextTick();
            }
        };
        this.scheduler.scheduleAtFixedRate(tickRunner, 0, 50, TimeUnit.MILLISECONDS);
    }
}



/**
 * Model implementation with a simple array holding the game data. Responsible
 * for updating the view.
 */
public class Model implements IModel {
    public static int NUM_COLS;
    public static int NUM_ROWS;
    private final boolean[] currData;
    private final boolean[] nextData;
    private final Cursor cursor;
    private IView view;

    /**
     * Instantiate a new model
     * 
     * @param x
     *            number of rows of the game field.
     * @param y
     *            number of columns of the game field.
     * @param controller
     *            the controller to pass to the view.
     */
    public Model(final int x, final int y, final IController controller) {
        this.view = new SwingView(controller);
        NUM_COLS = x;
        NUM_ROWS = y;
        this.currData = new boolean[NUM_COLS * NUM_ROWS];
        this.nextData = new boolean[NUM_COLS * NUM_ROWS];
        this.cursor = new Cursor();
    }

    @Override
    public void initializeModel() {
        for (int i = 0; i < this.currData.length; i++) {
            this.currData[i] = Math.random() >= 0.5;
        }
        this.view.updateView(this.currData);
    }

    @Override
    public void updateModel() {
        getNewData();

        System.arraycopy(this.nextData, 0, this.currData, 0, this.currData.length);

        this.view.updateView(this.currData);
    }

    private void getNewData() {
        for (int i = 0; i < this.currData.length; i++) {
            updateCell(i, getNumberOfAliveNeighbours(i));
        }
    }

    private void updateCell(final int i, final int numberOfNeighbours) {
        final boolean isCellAlive = this.currData[i];

        if (isCellAlive) {
            if (numberOfNeighbours < 2 || numberOfNeighbours > 3) {
                this.nextData[i] = false;
            } else {
                this.nextData[i] = true;
            }
        } else {
            if (numberOfNeighbours == 3) {
                this.nextData[i] = true;
            }
        }
    }

    private int getNumberOfAliveNeighbours(final int i) {
        int aliveNeighbours = 0;

        this.cursor.setPosition(i);

        if (this.currData[this.cursor.moveRight()]) {
            ++aliveNeighbours;
        }

        if (this.currData[this.cursor.moveDown()]) {
            ++aliveNeighbours;
        }

        if (this.currData[this.cursor.moveLeft()]) {
            ++aliveNeighbours;
        }

        if (this.currData[this.cursor.moveLeft()]) {
            ++aliveNeighbours;
        }

        if (this.currData[this.cursor.moveUp()]) {
            ++aliveNeighbours;
        }

        if (this.currData[this.cursor.moveUp()]) {
            ++aliveNeighbours;
        }

        if (this.currData[this.cursor.moveRight()]) {
            ++aliveNeighbours;
        }

        if (this.currData[this.cursor.moveRight()]) {
            ++aliveNeighbours;
        }

        return aliveNeighbours;
    }

    private class Cursor {
        private int pos;
        private final int MAX_POS = (Model.NUM_ROWS * Model.NUM_COLS);

        public Cursor() {
            super();
        }

        public void setPosition(final int pos) {
            this.pos = pos;
        }

        public int moveRight() {
            if (this.pos >= this.MAX_POS - 1) {
                this.pos = -1;
            }
            return ++this.pos;
        }

        public int moveLeft() {
            if (this.pos <= 1) {
                this.pos = this.MAX_POS;
            }
            return --this.pos;
        }

        public int moveDown() {
            this.pos += Model.NUM_ROWS;
            if (this.pos > this.MAX_POS - 1) {
                this.pos -= this.MAX_POS;
            }

            return this.pos;
        }

        public int moveUp() {
            this.pos -= Model.NUM_ROWS;
            if (this.pos < 0) {
                this.pos += this.MAX_POS;
            }
            return this.pos;
        }
    }
}


/**
 * View implementation using Swing for the GUI.
 */
public class SwingView extends JFrame implements IView {
    /**
     * Auto generated by Eclipse.
     */
    private static final long serialVersionUID = -1538005133913482652L;
    private final JButton btnRnd = new JButton("(Re)Start");
    private Rectangles rectangles;
    final IController controller;

    /**
     * Initialize the Swing view and create controls.
     * 
     * @param controller
     *            the controller which is triggered by controls.
     */
    public SwingView(final IController controller) {
        super();
        this.setTitle("Game of Life");
        this.getContentPane().setLayout(null);
        this.setBounds(100, 100, 625, 575);
        addControls();
        this.setVisible(true);
        this.setDefaultCloseOperation(EXIT_ON_CLOSE);
        this.controller = controller;
    }

    private void addControls() {
        this.btnRnd.setBounds(500, 0, 100, 30);
        this.btnRnd.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                SwingView.this.controller.randomSeed();
            }
        });
        this.add(this.btnRnd);
    }

    @Override
    public void updateView(final boolean[] data) {
        if (this.rectangles == null) {
            this.add(new Rectangles(data));
        } else {
            this.rectangles.setNewData(data);
        }
        this.revalidate();
        this.repaint();
    }

    private class Rectangles extends JPanel {
        /**
         * Auto generated by Eclipse.
         */
        private static final long serialVersionUID = -8352502831091810753L;
        private boolean[] data;
        private static final int RECT_SIZE = 10;

        public Rectangles(final boolean[] data) {
            this.data = data;
            this.setBounds(0, 0, data.length * RECT_SIZE, data.length * RECT_SIZE);
        }

        public void setNewData(final boolean[] data) {
            this.data = data;
        }

        @Override
        protected void paintComponent(final Graphics g) {
            super.paintComponent(g);

            for (int i = 0; i < this.data.length; i++) {
                if (this.data[i]) {
                    g.setColor(Color.BLACK);
                } else {
                    g.setColor(Color.WHITE);
                }
                g.fillRect((i % Model.NUM_ROWS) * RECT_SIZE, (i / Model.NUM_COLS) * RECT_SIZE, RECT_SIZE, RECT_SIZE);
            }
        }
    }
}

代码也可以在这里获得:https://github.com/totoMauz/gameOfLife

我唯一的想法和评论是:

我从多维数组开始,但是过了一段时间,它看起来有点迟钝,所以我切换到了一个正常的数组,这帮助了一点。

MVC模式似乎有点强制,但我是有意这样做的,因为它最大的优点是理论上可以切换实现。如果还没有看到在实践中,我也不知道如何正确实例化组件,以使控制器到视图。

场地是随机的,我不打算扩大它的方式,球员可以标记开始活的位置。

内存消耗相当高(~90 MB),性能似乎随着时间的推移而退化,但我没有看到任何明显的(还没有分析)

谢谢:)

EN

回答 1

Code Review用户

回答已采纳

发布于 2017-04-24 12:33:30

代码语言:javascript
复制
    if (isCellAlive) {
        if (numberOfNeighbours < 2 || numberOfNeighbours > 3) {
            this.nextData[i] = false;
        } else {
            this.nextData[i] = true;
        }
    } else {
        if (numberOfNeighbours == 3) {
            this.nextData[i] = true;
        }
    }

你应该养成这样的习惯:

如果If语句将某项设置为布尔值,而另一组设置为该布尔值的负值(例如if (x) { y = true; } else { y = false; }),则不必费心使用if语句并直接分配值。

如果其他语句只包含If或if- else ( if -else)链,则将内部链拉到作用域的一个级别上。

具体地说:

代码语言:javascript
复制
    if (isCellAlive) {
        this.nextData[i] = !(numberOfNeighbours < 2 || numberOfNeighbours > 3);
    } else {
        if (numberOfNeighbours == 3) {
            this.nextData[i] = true;
        }
    }

在您应用第一条规则之后,代码就是这样的.

代码语言:javascript
复制
    if (isCellAlive) {
        this.nextData[i] = !(numberOfNeighbours < 2 || numberOfNeighbours > 3);
    } else if (numberOfNeighbours == 3) {
        this.nextData[i] = true;
    }

这就是应用第二条规则后的样子。

你会得到更短的代码。

不过我觉得还没那么好..。我不喜欢这里的否定:

代码语言:javascript
复制
this.nextData[i] = !(numberOfNeighbours < 2 || numberOfNeighbours > 3);

我们可以倒转条件来消除否定:

代码语言:javascript
复制
this.nextData[i] = numberOfNeighbours >= 2 && numberOfNeighbours <= 3;

但是,对于本质上“有两个或三个邻居”的事物来说,这似乎是愚蠢的。如果我们把它说得更清楚..。

代码语言:javascript
复制
this.nextData[i] = numberOfNeighbours == 2 || numberOfNeighbours == 3;

对我来说,这似乎更好一些,因为它更清楚地写出了这样的要求:“如果一个单元有两个或三个邻居,它就会保持活力。”

票数 5
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/161648

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档