<file_sep>package a8;

import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.ParseException;
import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.Color;
import java.util.ArrayList;

import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

public class LifeView extends JPanel implements ActionListener, ChangeListener, LifeSpotListener {

    private ArrayList<LifeViewListener> _listeners;

    private PaintPanel _board;
    private JPanel _bottom_panel, _game_control, _rate_control;
    private JLabel _dimensionlabel, _ratelabel;
    private JTextField _dimensionbox, _ratebox;
    private JSlider _sleeptime_slider;
    private JButton _advance, _reset, _clear, _setdimension, _setrate, _toggletorus,_togglerunning;

    public LifeView(int dimension) {
        _board = new PaintPanel(dimension,this);

        _board.addListener(this);
        setLayout(new BorderLayout());
        add(_board, "Center");

        _bottom_panel = new JPanel();
        _bottom_panel.setLayout(new GridLayout(2,1));

        // game control panel

        _game_control = new JPanel();
        _togglerunning = new JButton("\u25B6");
        _togglerunning.addActionListener(this);
        _advance = new JButton("Adv.");
        _advance.addActionListener(this);
        _clear = new JButton("Clear");
        _clear.addActionListener(this);
        _reset = new JButton("Reset");
        _reset.addActionListener(this);
        _sleeptime_slider = new JSlider(10,1000);
        _sleeptime_slider.setValue(50);
        _sleeptime_slider.setSize(new Dimension(40,20));
        _sleeptime_slider.addChangeListener(this);

        _game_control.add(_sleeptime_slider);
        _game_control.add(_togglerunning);
        _game_control.add(_advance);
        _game_control.add(_reset);
        _game_control.add(_clear);

        // rate control panel

        _rate_control = new JPanel();

        _setdimension = new JButton("Set Dim.");
        _dimensionlabel = new JLabel();
        _dimensionlabel.setText(Integer.toString(dimension));
        _setdimension.addActionListener(this);
        _dimensionbox = new JTextField(3);
        _setrate = new JButton("Set Rates");
        _setrate.addActionListener(this);
        _ratebox = new JTextField(5);
        _ratelabel = new JLabel("3,3,2,3");
        _toggletorus = new JButton("Torus off");
        _toggletorus.addActionListener(this);
        
        _rate_control.add(_dimensionlabel);
        _rate_control.add(_dimensionbox);
        _rate_control.add(_setdimension);
        // _rate_control.add(new JLabel("(lb,hb,ls,hs)"));
        _rate_control.add(_ratelabel);
        _rate_control.add(_ratebox);
        _rate_control.add(_setrate);
        _rate_control.add(_toggletorus);

        _bottom_panel.add(_game_control);
        _bottom_panel.add(_rate_control);

        add(_board,BorderLayout.CENTER);
        add(_bottom_panel, BorderLayout.SOUTH);

        resetView();

        _listeners = new ArrayList<LifeViewListener>();
    }

    public void updatePanel(boolean[][] board) {
        _board.update(board);
    }

    public void resetView() {
        remove(_board);
        add(_board, "Center");
        
        revalidate();
        repaint();
    }

    public void color(int x, int y, boolean alive) {
        if (alive) {
            _board.setAlive(x, y);
        } else {
            _board.setDead(x, y);
        }
    }

    public PaintPanel getBoard() {
        return _board;
    }

    // button actionPerformed
    @Override
    public void actionPerformed(ActionEvent e) {
        if (e.getActionCommand().equals(_advance.getText())) {
            for (LifeViewListener l : _listeners) {
                l.advancePressed();
            }
        }

        if (e.getActionCommand().equals(_clear.getText())) {
            for (LifeViewListener l : _listeners) {
                l.clear();
            }
        }

        if (e.getActionCommand().equals(_reset.getText())) {
            for (LifeViewListener l : _listeners) {
                l.reset();
            }
        }

        if (e.getActionCommand().equals(_togglerunning.getText())) {
            for (LifeViewListener l : _listeners) {
                l.toggleThread();
                if (_togglerunning.getText().equals("\u25B6")) {
                    _togglerunning.setText("\u23F8");
                } else {
                    _togglerunning.setText("\u25B6");
                }
            }
        }

        if (e.getActionCommand().equals(_toggletorus.getText())) {
            for (LifeViewListener l : _listeners) {
                l.toggleTorusMode();
                if (_toggletorus.getText().equals("Torus off")) {
                    _toggletorus.setText("Torus on");
                } else {
                    _toggletorus.setText("Torus off");
                }
            }
        }

        if (e.getActionCommand().equals(_setdimension.getText())) {
            for (LifeViewListener l : _listeners) {
                int dim = 0;
                try {
                    dim = Integer.parseInt(_dimensionbox.getText());
                } catch (NumberFormatException f) {
                    _dimensionlabel.setText("Invalid input");
                }
                if (dim < 10 || dim > 500) {
                    _dimensionlabel.setText("Illegal dimension");
                    return;
                }
                l.changeDimension(dim);
                _dimensionlabel.setText(_dimensionbox.getText());
                _dimensionbox.setText("");
            }
        }

        if (e.getActionCommand().equals(_setrate.getText())) {
            for (LifeViewListener l : _listeners) {
                String rates = _ratebox.getText();
                int lb = 0;
                int hb = 0;
                int ls = 0;
                int hs = 0;
                try {
                    lb = rates.charAt(0)-48;
                    hb = rates.charAt(2)-48;
                    ls = rates.charAt(4)-48;
                    hs = rates.charAt(6)-48;
                } catch (StringIndexOutOfBoundsException f) {
                    _ratelabel.setText("invalid input");
                    return;
                }

                if ((lb+hb+ls+hs) > 32 || (lb+hb+ls+hs) < 0 || (!(lb <= hb)) || (!(ls <= hs))) {
                    _ratelabel.setText("invalid rates");
                    return;
                }
                
                l.changeRates(lb,hb,ls,hs);
                _ratelabel.setText(lb+","+hb+","+ls+","+hs);
                _ratebox.setText("");
            }
        }
    }

    public void spotClicked(int x, int y) {
        for (LifeViewListener l : _listeners) {
            l.spotChanged(x,y);
        }
    }


    public void setDimension(int dimension) {
        _board.setDimension(dimension);
        resetView();
    }

    // convenience for adding and removing listeners

    public void addListener(LifeViewListener listener) {
        _listeners.add(listener);
    }

    public void removeListener(LifeViewListener listener) {
        _listeners.remove(listener);
    }

    @Override
    public void stateChanged(ChangeEvent e) {
        JSlider src = (JSlider) e.getSource();
        for (LifeViewListener l : _listeners) {
            l.changeSleepTime(src.getValue());
        }
    }
    
}
<file_sep>package a8;

public interface LifeSpotListener {
    public void spotClicked(int x, int y);
}<file_sep>package a8;

import java.awt.Color;

import struct.Spot;

public class LifeController implements LifeViewListener, LifeModelObserver {
    private LifeView _view;
    private LifeModel _model;
    private ModelRunner _runner;

    public LifeController(LifeView view, LifeModel model) {
        _view = view;
        _model = model;
        _runner = new ModelRunner(_model);
        _runner.start();
        _view.addListener(this);
        _model.addObserver(this);
        _view.updatePanel(_model.getBoard());
    }

    @Override
    public void updateSpot(int x, int y, boolean alive) {
        _view.color(x, y, alive);
    }

    @Override
    public void spotChanged(int x, int y) {
        if (_model.isAlive(x, y)) {
            _model.setDead(x, y);
        } else {
            _model.setAlive(x,y);
        }
        pushModel();
    }

    public void pushModel() {
        _view.updatePanel(_model.getBoard());
    }

    @Override
    public void clear() {
        _model.clear();
    }

    @Override
    public void reset() {
        _model.reset();
    }

    @Override
    public void advancePressed() {
        _model.advance();

    }

    @Override
    public void changeDimension(int dimension) {
        _model.setDimension(dimension);
        _view.setDimension(dimension);
        pushModel();
    }

    @Override
    public void changeRates(int lowbirth, int highbirth, int lowsurvival, int highsurvival) {
        _model.setRates(lowbirth, highbirth, lowsurvival, highsurvival);

    }

    @Override
    public void toggleTorusMode() {
        _model.toggleTorusMode();
        if (_model.getTorusMode()) {
            
        }
    }

    @Override
    public void toggleThread() {
        System.out.println("toggle thread");
        if (_runner.isPaused()) {
            _runner.resum();
            System.out.println("resume");
        } else {
            _runner.pause();
        }
    }

    @Override
    public void changeSleepTime(int sleeptime) {
        _runner.setSleepTime(sleeptime);

    }


}<file_sep>package a8;

public class ModelRunner extends Thread {

	private LifeModel _model;

	private volatile boolean _running;
	private volatile boolean _paused;

	private int _sleeptime;

	private Object lock;

	public ModelRunner(LifeModel model) {
		_model = model;
		_running = true;
		_paused = true;
		_sleeptime = 50;
		lock = new Object();
	}

	@Override
	public void run() {    
	
		while (_running) {
			synchronized (lock) {
				if (!_running) { // may have changed while waiting to
					// synchronize on pauseLock
					break;
				}
				if (_paused) {
					try {
						synchronized (lock) {
							lock.wait(); // will cause this Thread to block until 
							// another thread calls pauseLock.notifyAll()
							// Note that calling wait() will 
							// relinquish the synchronized lock that this 
							// thread holds on pauseLock so another thread
							// can acquire the lock to call notifyAll()
							// (link with explanation below this code)
						}
					} catch (InterruptedException ex) {
						break;
					}
					if (!_running) { // running might have changed since we paused
						break;
					}
				}
			}
			_model.advance();
			try {
				sleep(_sleeptime); 
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		
	}

	public void pause() {
		_paused = true;
	}


	public void resum() {
		synchronized (lock) {
			_paused = false;
			lock.notifyAll();
		}
	}

	public boolean isPaused() {
		return _paused;
	}

	public void setSleepTime(int time) {
		if (time < 10) {throw new IllegalArgumentException("sleep length is too short");}
		if (time > 1000) {throw new IllegalArgumentException("sleep length is too long");}
		_sleeptime = time;
	}
}
<file_sep>.PHONY: game run build clean

game: build
	java -cp bin -Djava.awt.headless=true main.ExampleGame

run: build
	java -cp bin main.Main

build: bin/
	javac -d ./bin ./src/main/*

bin/:
	mkdir -p ./bin
