我的Java需要一点更新,所以我将生命游戏作为一个小片段来实现。这场比赛正如期而至,但我相信还有一些改进的余地。
下面是代码(跳过接口、包和导入)
/**
* 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),性能似乎随着时间的推移而退化,但我没有看到任何明显的(还没有分析)
谢谢:)
发布于 2017-04-24 12:33:30
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)链,则将内部链拉到作用域的一个级别上。
具体地说:
if (isCellAlive) {
this.nextData[i] = !(numberOfNeighbours < 2 || numberOfNeighbours > 3);
} else {
if (numberOfNeighbours == 3) {
this.nextData[i] = true;
}
}在您应用第一条规则之后,代码就是这样的.
if (isCellAlive) {
this.nextData[i] = !(numberOfNeighbours < 2 || numberOfNeighbours > 3);
} else if (numberOfNeighbours == 3) {
this.nextData[i] = true;
}这就是应用第二条规则后的样子。
你会得到更短的代码。
不过我觉得还没那么好..。我不喜欢这里的否定:
this.nextData[i] = !(numberOfNeighbours < 2 || numberOfNeighbours > 3);我们可以倒转条件来消除否定:
this.nextData[i] = numberOfNeighbours >= 2 && numberOfNeighbours <= 3;但是,对于本质上“有两个或三个邻居”的事物来说,这似乎是愚蠢的。如果我们把它说得更清楚..。
this.nextData[i] = numberOfNeighbours == 2 || numberOfNeighbours == 3;对我来说,这似乎更好一些,因为它更清楚地写出了这样的要求:“如果一个单元有两个或三个邻居,它就会保持活力。”
https://codereview.stackexchange.com/questions/161648
复制相似问题