title: Hexagonal game of life Swing implementation
body: <p>I am trying to implement Conways Game of Life based on a hexagonal grid. Everything is going right except for thread processing incorrectly: every time I run an application I gain different result, so I thought that is a problem connected with unsynchronized thread. I'm new in java so it would be quite helpful to receive any suggestions. Thanks.</p>
  
  <pre><code>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 &lt; BOARD_HEIGHT; j++) {
              for (int i = 0; i &lt; 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 &gt;= 0 &amp;&amp; i &lt; BOARD_HEIGHT &amp;&amp; j &gt;= 0 &amp;&amp; j &lt; BOARD_WIDTH
                  &amp;&amp; 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 &lt; BOARD_HEIGHT; j++) {
              for (int i = 0; i &lt; 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 &lt; mCells.length; ++i){
              buffer[i] = Arrays.copyOf(mCells[i], mCells[i].length);
          }
  
          for(int i = 0; i &lt; BOARD_HEIGHT; ++i){ 
              for(int j = 0; j &lt; 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 &lt; 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) &amp;&amp; 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 &gt; 3 || neigbourCounter &lt; 2){
                              buffer[i][j] = L_ON;
                          }
                      }
                  }
              }
          }
          }
          for(int i = 0; i &lt; 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 &lt; 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 &gt; Math.abs(RADIUS / 2 - RADIUS * cy / HEIGHT)) {
              setCellIndex(ci, cj);
          } else {
              setCellIndex(ci - 1, cj + (ci % 2) - ((cy &lt; 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);
      }
  
  }
  </code></pre>