/*
Source file: GameOfLifeFrame.java
Author: Markus Bautsch
Licence: public domain
Date: 13 October 2024
Version: 1.0
Programming language: Java
*/
/*
Frame for the graphical representation of instances of GameOfLifeModel
*/
public final class GameOfLifeFrame extends javax.swing.JFrame implements java.awt.event.ActionListener
{
// serialVersionUID for serialisation, implemented in classe javax.swing.JFrame
private final static long serialVersionUID = 1L;
// Class constants
/**
* All game board frames will be saved to PNG files
*/
public static final boolean Save = true;
/**
* No game board frame will be saved to a PNG file
*/
public static final boolean DontSave = false;
final private static int [] DeadColour = {0, 0, 0};
final private static int [] LiveColour = {255, 255, 255};
final private static java.lang.String title = "Conway's Game of Life";
final private static int offset = 10;
final private static int offsetTop = 30;
private static long fileCounter = 100000;
// Instance variables
private int sizeX = 200;
private int sizeY = 200;
private long currentStepNumber = 0;
private long numberOfIterations = 1;
private boolean save; /* is true, if simulation shall be saved to PNG-files */
private GameOfLifeModel model;
private java.awt.image.BufferedImage bufferedImage;
private java.awt.image.WritableRaster imageRaster;
private javax.swing.Timer timer = new javax.swing.Timer (1, this); // for the delay between two subsequent JFrames
/**
* Constructor for the initialisation of instances of the class GameOfLifeFrame
* @param width: horizontal board size in pixel
* @param height: vertical board size in pixel
* @param mode: border handling, either GameOfLifeModel.ALWAYS_DEAD or GameOfLifeModel.TORODIAL
* @param count: number of steps for the game board iterations
* @param saveBoard: save all iterated boards to PNG files, either Save oder DontSave
*/
public GameOfLifeFrame (int width, int height, boolean mode, long count, boolean saveBoard)
{
super (title);
this.model = new GameOfLifeModel (width, height, mode);
this.sizeX = width;
this.sizeY = height;
this.numberOfIterations = count;
this.save = saveBoard;
// frame size, background and layout
this.setSize (this.sizeX + 2 * offset, this.sizeY + 2 * offset + offsetTop);
this.setBackground (java.awt.Color.WHITE);
this.bufferedImage = new java.awt.image.BufferedImage (this.sizeX, this.sizeY, java.awt.image.BufferedImage.TYPE_INT_RGB);
this.imageRaster = bufferedImage.getWritableTile (0, 0);
this.setDefaultCloseOperation (javax.swing.WindowConstants.EXIT_ON_CLOSE);
}
/**
* Returns the model of the instance GameOfLifeModel of GameOfLifeFrame
* @return model of the instance GameOfLifeModel
*/
public GameOfLifeModel getModel ()
{
return model;
}
/**
* Standard method for painting a rectangular board of the Game of Life
* @param graphics: instance of java.awt.Graphics
*/
public void paint (java.awt.Graphics graphics)
{
for (int y = 0; y < sizeY; y++)
{
for (int x = 0; x < sizeX; x++)
{
boolean cellState = model.getCellState (x, y);
if (cellState == GameOfLifeModel.DEAD)
{
imageRaster.setPixel (x, y, DeadColour);
}
else
{
imageRaster.setPixel (x, y, LiveColour);
}
}
}
graphics.drawImage (bufferedImage, offset, offset + offsetTop, null);
java.awt.Toolkit.getDefaultToolkit ().sync (); // This method call ensures that the display is up-to-date
}
/**
* Method for the initialisation of an instance of GameOfLifeFrame
* Starts timer instance of the class javax.swing.Timer for updating frames
*/
public void init ()
{
java.awt.Graphics2D graphics2D = bufferedImage.createGraphics ();
this.paint (graphics2D);
this.setVisible (true);
this.setAlwaysOnTop (true);
this.timer.start ();
}
/**
* Method save for saving the current board frame to a graphical PNG file
* @param bufferedImage: instance of java.awt.image.BufferedImage
*/
private void saveFile (java.awt.image.BufferedImage bufferedImage)
{
final java.lang.String fileExtension = "png";
java.util.Iterator <javax.imageio.ImageWriter> iter = javax.imageio.ImageIO.getImageWritersByFormatName (fileExtension);
javax.imageio.ImageWriter imageWriter = (javax.imageio.ImageWriter) iter.next ();
javax.imageio.ImageWriteParam imageWriteParam = imageWriter.getDefaultWriteParam ();
imageWriteParam.setCompressionMode (javax.imageio.ImageWriteParam.MODE_DISABLED);
try
{
java.lang.String fileNameCounter = java.lang.Long.toString (fileCounter);
java.lang.String fileName = "GameOfLife" + fileNameCounter + "." + fileExtension;
javax.imageio.stream.FileImageOutputStream fileImageOutputStream = new javax.imageio.stream.FileImageOutputStream (new java.io.File (fileName));
imageWriter.setOutput (fileImageOutputStream);
javax.imageio.IIOImage iIOimg = new javax.imageio.IIOImage ((java.awt.image.RenderedImage) bufferedImage, null, null);
java.lang.System.out.println ("Save " + fileName);
imageWriter.write (null, iIOimg, imageWriteParam);
fileCounter++;
}
catch (java.lang.Exception exception)
{
exception.printStackTrace ();
}
}
/**
* Overwritten standard method actionPerformed for the synchronised update of an instance GameOfLifeFrame
*/
public void actionPerformed (java.awt.event.ActionEvent event)
{
java.lang.String currentTitel = title + " / step " + currentStepNumber;
if (currentStepNumber < numberOfIterations)
{
currentStepNumber++;
model.computeNextBoard ();
if (this.save)
{
this.saveFile (bufferedImage);
}
repaint ();
}
else
{
currentTitel = currentTitel + " / game over";
timer.stop ();
}
setTitle (currentTitel);
}
}