Программа Game of Life на Java

Я пытался сделать программу Game of Life довольно простым (но, возможно, неправильным) способом. Технически это работает, но когда я попытался запустить несколько тестов, результаты не соответствовали примерам в Интернете.

Вот основной класс:

  package gameOfLife;import java.applet.*;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import gameOfLife.Cell.State;

public class CreateGame extends Applet implements Runnable, MouseListener, KeyListener {


private enum GameState {
    SETTING, START
}

private GameState state = GameState.SETTING;
private Image image;
private Graphics second;
private Cell[][] cells = new Cell[200][120];
private int indexI = 1;
private int indexJ = 1;

@Override
public void init() {
    setSize(1000, 600);
    setFocusable(true);
    Frame frame = (Frame) this.getParent().getParent();
    frame.setResizable(false);
    frame.setTitle("Game of Life - Settings");

    for (int i = 0; i < cells.length; i++) {
        for (int j = 0; j < cells[0].length; j++) {
            cells[i][j] = new Cell(i * 5, j * 5);
        }
    }
    addMouseListener(this);
    addKeyListener(this);
    super.init();
}

@Override
public void start() {
    Thread thread = new Thread(this);
    thread.start();
    super.start();
}

@Override
public void run() {
    while (true) {
        if (state == GameState.START) {
            for(int i=1; i<cells.length-1; i++){
                for(int j=1; j<cells[0].length-1; j++){
                    update(i, j);
                }
            }
        }
        repaint();
        try {
            Thread.sleep(125);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public void update(int i, int j) { //updating the cells into ALIVE/DEAD state
    int[] neighbors = new int[8];
    int sum = 0;
    for (int index = 0; index < 8; index++)
        neighbors[index] = 0;
    if (cells[i - 1][j - 1].state == State.ALIVE)
        neighbors[0] = 1;
    if (cells[i][j - 1].state == State.ALIVE)
        neighbors[1] = 1;
    if (cells[i + 1][j - 1].state == State.ALIVE)
        neighbors[2] = 1;
    if (cells[i - 1][j].state == State.ALIVE)
        neighbors[3] = 1;
    if (cells[i + 1][j].state == State.ALIVE)
        neighbors[4] = 1;
    if (cells[i - 1][j + 1].state == State.ALIVE)
        neighbors[5] = 1;
    if (cells[i][j + 1].state == State.ALIVE)
        neighbors[6] = 1;
    if (cells[i + 1][j + 1].state == State.ALIVE)
        neighbors[7] = 1;
    for (int index = 0; index < 8; index++)
        sum += neighbors[index];
    if ((sum < 2 || sum > 3) && cells[i][j].state == State.ALIVE)
        cells[i][j].state = State.DEAD;
    else {
        if (sum == 3 && cells[i][j].state == State.DEAD)
            cells[i][j].state = State.ALIVE;
    }
}

@Override
public void paint(Graphics g) {
    Graphics2D g2d = (Graphics2D) g;
    Rectangle r;
    for (int i = 0; i < cells.length; i++) {
        for (int j = 0; j < cells[0].length; j++) {
            if (cells[i][j].state == State.ALIVE) {
                r = cells[i][j].getCell();
                g.fillRect((int) r.getX(), (int) r.getY(), (int) r.getWidth(), (int) r.getHeight());
            }
        }
    }
    super.paint(g);
}

@Override
public void update(Graphics g) {
    if (image == null) {
        image = createImage(this.getWidth(), this.getHeight());
        second = image.getGraphics();
    }

    second.setColor(getBackground());
    second.fillRect(0, 0, getWidth(), getHeight());
    second.setColor(getForeground());
    paint(second);

    g.drawImage(image, 0, 0, this);
}

@Override
public void mousePressed(MouseEvent e) {
    if (state == GameState.SETTING) {
        int x = e.getX() - e.getX() % 5;
        int y = e.getY() - e.getY() % 5;

        cells[x / 5][y / 5].born();
    }
}

@Override
public void mouseEntered(MouseEvent e) {
    // TODO Auto-generated method stub

}

@Override
public void mouseExited(MouseEvent e) {
    // TODO Auto-generated method stub

}

@Override
public void mouseClicked(MouseEvent e) {
    // TODO Auto-generated method stub

}

@Override
public void mouseReleased(MouseEvent e) {
    // TODO Auto-generated method stub

}

@Override
public void keyPressed(KeyEvent e) {
    if (e.getKeyCode() == KeyEvent.VK_SPACE) {
        if(state == GameState.START)
            state = GameState.SETTING;
        else 
            state = GameState.START;
    }

}

@Override
public void keyReleased(KeyEvent arg0) {
    // TODO Auto-generated method stub

}

@Override
public void keyTyped(KeyEvent arg0) {
    // TODO Auto-generated method stub

}

}

Я сделал программу таким образом, что нажатием клавиши «Пробел» вы меняете состояние игры с «НАСТРОЙКА» (нажатие на экран для создания ячеек, которое приостанавливает игру) и «СТАРТ», при котором игра запускается сама по себе. Ячейка - это созданный мной класс, который содержит прямоугольник и состояние:

package gameOfLife;

import java.awt.Rectangle;

public class Cell {

final int Measure = 5;
public static enum State{
    DEAD, ALIVE
}

private Rectangle cell;
private int x, y;

public State state;

public void born(){
    state = State.ALIVE;
}

public Cell(int x, int y){
    state = State.DEAD;
    cell = new Rectangle(x, y, Measure, Measure);
    this.x = x;
    this.y = y;
}

public Rectangle getCell(){
    return this.cell;
}

}

Если кто-нибудь сможет протестировать это и сказать мне, что не так с логикой программы, это будет здорово, потому что в целом она работает, но, вероятно, есть какая-то незначительная проблема, которую я не могу указать, которая ее портит.


person user3917631    schedule 09.09.2016    source источник
comment
Это может быть более уместно на codereview.stackexchange.com.   -  person Andrew Mortimer    schedule 09.09.2016
comment
Это может быть уместно здесь, если вы делаете воспроизводимый пример с образцом ввода, описанием ожидаемого результата и, возможно, образцом неправильного результата. Вот код, найдите ошибку, это не то, на что люди обычно отвечают здесь.   -  person Martin Nyolt    schedule 09.09.2016
comment
Что ж, извините, я действительно не знаю, как это место работает. Я подумал, что могу опубликовать его, как я это сделал, и если есть какие-либо пояснения или примеры необходимых результатов, я опубликую их в комментариях.   -  person user3917631    schedule 09.09.2016


Ответы (1)


Я понял ошибку - вы обновляли состояние каждой ячейки при повторении по сетке, но правильный способ - создать сетку новых состояний ячеек во время итерации, а затем установить существующие состояния ячеек в новую ячейку. состояний за один раз, если это имеет смысл.

Измененный код, который, кажется, работает:

public class CreateGame
    extends Applet
    implements Runnable, MouseListener, KeyListener
{

  private enum GameState
  {
    SETTING, START
  }

  private GameState state = GameState.SETTING;

  private Image image;

  private Graphics second;

  private Cell[][] cells = new Cell[200][120];

  private int indexI = 1;

  private int indexJ = 1;

  @Override
  public void init()
  {
    setSize(1000, 600);
    setFocusable(true);
    Frame frame = (Frame) this.getParent().getParent();
    frame.setResizable(false);
    frame.setTitle("Game of Life - Settings");

    for (int i = 0; i < cells.length; i++)
    {
      for (int j = 0; j < cells[0].length; j++)
      {
        cells[i][j] = new Cell(i * 5, j * 5);
      }
    }
    addMouseListener(this);
    addKeyListener(this);
    super.init();
  }

  @Override
  public void start()
  {
    Thread thread = new Thread(this);
    thread.start();
    super.start();
  }

  @Override
  public void run()
  {
    while (true)
    {
      if (state == GameState.START)
      {
        Cell[][] newCells = new Cell[200][120];

        for (int i = 0; i < newCells.length; i++)
        {
          for (int j = 0; j < newCells[0].length; j++)
          {
            newCells[i][j] = new Cell(i * 5, j * 5);
          }
        }

        for (int i = 1; i < cells.length - 1; i++)
        {
          for (int j = 1; j < cells[0].length - 1; j++)
          {
            update(newCells, i, j);
          }
        }

        cells = newCells; // update all cell states in one go
      }
      repaint();
      try
      {
        Thread.sleep(125);
      }
      catch (InterruptedException e)
      {
        e.printStackTrace();
      }
    }
  }

  public void update(Cell[][] newCells, int i, int j)
  { // updating the cells into ALIVE/DEAD state

    int[] neighbors = new int[8];
    int sum = 0;
    for (int index = 0; index < 8; index++)
      neighbors[index] = 0;
    if (cells[i - 1][j - 1].state == State.ALIVE)
      neighbors[0] = 1;
    if (cells[i][j - 1].state == State.ALIVE)
      neighbors[1] = 1;
    if (cells[i + 1][j - 1].state == State.ALIVE)
      neighbors[2] = 1;
    if (cells[i - 1][j].state == State.ALIVE)
      neighbors[3] = 1;
    if (cells[i + 1][j].state == State.ALIVE)
      neighbors[4] = 1;
    if (cells[i - 1][j + 1].state == State.ALIVE)
      neighbors[5] = 1;
    if (cells[i][j + 1].state == State.ALIVE)
      neighbors[6] = 1;
    if (cells[i + 1][j + 1].state == State.ALIVE)
      neighbors[7] = 1;
    for (int index = 0; index < 8; index++)
      sum += neighbors[index];

    if (cells[i][j].state == State.ALIVE)
    {
      if ((sum < 2 || sum > 3))
      {
        newCells[i][j].state = State.DEAD;
      }
      else // sum == 2 or 3
      {
        newCells[i][j].state = State.ALIVE;
      }
    }
    else if (cells[i][j].state == State.DEAD)
    {
      if (sum == 3)
      {
        newCells[i][j].state = State.ALIVE;
      }
    }  
  }

  @Override
  public void paint(Graphics g)
  {
    Graphics2D g2d = (Graphics2D) g;
    Rectangle r;
    for (int i = 0; i < cells.length; i++)
    {
      for (int j = 0; j < cells[0].length; j++)
      {
        if (cells[i][j].state == State.ALIVE)
        {
          r = cells[i][j].getCell();
          g.fillRect((int) r.getX(), (int) r.getY(), (int) r.getWidth(), (int) r.getHeight());
        }
      }
    }
    super.paint(g);
  }

  @Override
  public void update(Graphics g)
  {
    if (image == null)
    {
      image = createImage(this.getWidth(), this.getHeight());
      second = image.getGraphics();
    }

    second.setColor(getBackground());
    second.fillRect(0, 0, getWidth(), getHeight());
    second.setColor(getForeground());
    paint(second);

    g.drawImage(image, 0, 0, this);
  }

  @Override
  public void mousePressed(MouseEvent e)
  {
    if (state == GameState.SETTING)
    {
      int x = e.getX() - e.getX() % 5;
      int y = e.getY() - e.getY() % 5;

      cells[x / 5][y / 5].born();
    }
  }

  @Override
  public void mouseEntered(MouseEvent e)
  {
    // TODO Auto-generated method stub

  }

  @Override
  public void mouseExited(MouseEvent e)
  {
    // TODO Auto-generated method stub

  }

  @Override
  public void mouseClicked(MouseEvent e)
  {
    // TODO Auto-generated method stub

  }

  @Override
  public void mouseReleased(MouseEvent e)
  {
    // TODO Auto-generated method stub

  }

  @Override
  public void keyPressed(KeyEvent e)
  {
    if (e.getKeyCode() == KeyEvent.VK_SPACE)
    {
      if (state == GameState.START)
        state = GameState.SETTING;
      else
        state = GameState.START;
    }

  }

  @Override
  public void keyReleased(KeyEvent arg0)
  {
    // TODO Auto-generated method stub

  }

  @Override
  public void keyTyped(KeyEvent arg0)
  {
    // TODO Auto-generated method stub

  }

}
person phil76    schedule 09.09.2016
comment
Большое спасибо за Вашу помощь! Пытался проголосовать за ваш комментарий, но, похоже, мне нужно больше репутации для этого. Еще одна вещь, теперь программа работает нормально, но я попытался запустить этот тестовый пример (который должен работать в бесконечном цикле): en.wikipedia.org/wiki/Gun_(cellular_automaton)#/media/ и это не сработало, что-то неправильная логика кода? - person user3917631; 09.09.2016
comment
Нет проблем, рад, что смог помочь. Возможно все же логическая ошибка, остальной код не проверял. Как вы настраиваете тестовый шаблон, который не работает, вы создаете шаблон в коде или вручную? Спрашиваю, потому что одна другая ячейка в начальной конфигурации приведет к совершенно другому поведению. Кроме того, как это терпит неудачу? - person phil76; 09.09.2016
comment
Установил паттерн хан, как раз сейчас работаю над настройкой кода. Хотя ставил точно (делал несколько раз и не раз проверял). Картина разваливается после стрельбы по космическим кораблям. Вот изображение того, как это выглядит в конце: siz.co. ил/my.php?i=nd5jq5jzmktu.png - person user3917631; 09.09.2016
comment
Хм, я бы определенно попытался исключить возможную ошибку в начальной конфигурации шаблона, написав некоторый код для предварительного заполнения сетки. Может быть, создать функцию, которая принимает массив 2D int с координатами x, y, чтобы установить его изначально живым? - person phil76; 09.09.2016
comment
Это то, над чем я уже начал работать, только вместо массива координат функция будет принимать массив 0 и 1 для живых и мертвых клеток. В любом случае, спасибо за помощь, я думаю, что теперь я попытаюсь справиться с этим самостоятельно :) - person user3917631; 10.09.2016