package cellsociety.controller; public interface SimulationStateObserver { void onSimulationUpdated(GridData gridData); } package cellsociety.controller; public record GridData( public record GridData( public int getState(int row, int col) public static GridData empty(int rows, int cols) } package cellsociety.controller; public interface ErrorHandler { void onError(String title, String message, Exception cause); public AppController() { this.engine = new SimulationEngine(); this.observers = new ArrayList<>(); engine.addStateChangeListener(new SimulationStateListener() { public void onStateChanged() { notifyObservers(); public void addObserver(SimulationStateObserver observer) { if (observer != null && !observers.contains(observer)) { observers.add(observer); public void removeObserver(SimulationStateObserver observer) { observers.remove(observer); public void setErrorHandler(ErrorHandler handler) { private void notifyObservers() { GridData data = getGridData(); for (SimulationStateObserver obs : observers) { obs.onSimulationUpdated(data); private void reportError(String title, String message, Exception e) { if (errorHandler != null) { errorHandler.onError(title, message, e); public boolean loadSimulation(Path xmlPath) { engine.loadFromFile(xmlPath); notifyObservers(); } catch (Exception e) { reportError("Load Error", "Failed to load simulation: " + e.getMessage(), e); boolean loadSimulation(SimulationConfig config) { engine.loadSimulation(config); notifyObservers(); } catch (SimulationException e) { reportError("Load Error", "Failed to load simulation: " + e.getMessage(), e); public void start() { engine.start(); public void pause() { engine.pause(); public void step() { if (!engine.isValidState()) { engine.step(); public void reset() { if (!engine.isValidState()) { engine.reset(); } catch (Exception e) { reportError("Reset Error", "Failed to reset simulation: " + e.getMessage(), e); public boolean hasSimulation() { return engine.isValidState(); public boolean isRunning() { return engine.isRunning(); public void setSpeed(double speed) { engine.setSpeed(speed); public double getSpeed() { return engine.getSpeed(); public void increaseSpeed() { engine.increaseSpeed(); public void decreaseSpeed() { engine.decreaseSpeed(); public GridData getGridData() { if (!engine.isValidState()) { return GridData.empty(0, 0); return new GridData( engine.getRows(), engine.getCols(), engine.getAllCellStates(), engine.getStepCount() public GridDimensions getGridDimensions() { if (!engine.isValidState()) { return GridDimensions.empty(); return new GridDimensions(engine.getRows(), engine.getCols()); public SimulationInfo getSimulationInfo() { if (!engine.isValidState()) { return SimulationInfo.empty(); return new SimulationInfo( engine.getSimulationType(), engine.getTitle(), engine.getAuthor(), engine.getDescription(), engine.getStateLegend(), engine.getParameters() public long getStepCount() { return engine.getStepCount(); public boolean saveSimulation(Path xmlPath) { engine.saveToFile(xmlPath); } catch (UnsupportedOperationException e) { reportError("Save Error", "Save functionality not yet implemented", e); } catch (Exception e) { reportError("Save Error", "Failed to save simulation: " + e.getMessage(), e); public boolean saveSimulation(Path xmlPath, SimulationInfo updatedInfo) { SimulationMetadata metadata = new SimulationMetadata( updatedInfo.title(), updatedInfo.author(), updatedInfo.description(), updatedInfo.stateLegend(), updatedInfo.parameters() engine.saveToFile(xmlPath, metadata); } catch (UnsupportedOperationException e) { reportError("Save Error", "Save functionality not yet implemented", e); } catch (Exception e) { reportError("Save Error", "Failed to save simulation: " + e.getMessage(), e); } package cellsociety.controller; public class AppController { public AppController() public void onStateChanged() public void addObserver(SimulationStateObserver observer) public void removeObserver(SimulationStateObserver observer) public void setErrorHandler(ErrorHandler handler) public boolean loadSimulation(Path xmlPath) public void start() public void pause() public void step() public void reset() public boolean hasSimulation() public boolean isRunning() public void setSpeed(double speed) public double getSpeed() public void increaseSpeed() public void decreaseSpeed() public GridData getGridData() public GridDimensions getGridDimensions() public SimulationInfo getSimulationInfo() public long getStepCount() public boolean saveSimulation(Path xmlPath) public boolean saveSimulation(Path xmlPath, SimulationInfo updatedInfo) } package cellsociety.controller; public class PlaybackController { public PlaybackController(AppController appController) public void play() public void pause() public void stop() public boolean isPlaying() public void setFramesPerSecond(double fps) public double getFramesPerSecond() public void increaseSpeed() public void decreaseSpeed() } package cellsociety.controller; public record GridDimensions(int rows, int cols) { public record GridDimensions(int rows, int cols) public static GridDimensions empty() } package cellsociety.controller; public record SimulationInfo( public record SimulationInfo( public static SimulationInfo empty() } package cellsociety.model.config; public record SimulationMetadata(String title, String author, String description, java.util.Map stateLegend, java.util.Map parameters) { public record SimulationMetadata(String title, String author, String description, java.util.Map stateLegend, java.util.Map parameters) } package cellsociety.model.config; public record ParameterSet(Map raw) { public record ParameterSet(Map raw) public ParameterSet(Map raw) public int getInt(String key) throws ParameterException public Optional findInt(String key) public Optional findDouble(String key) public String getString(String key) public boolean has(String key) } package cellsociety.model.config; public class XmlSaver { public void save(Path path, Simulation simulation, String type) throws IOException } package cellsociety.model.config; public record GridDimensions(int rows, int cols) { public record GridDimensions(int rows, int cols) } package cellsociety.model.config; public final class XmlConfigParser { public static SimulationConfig parse(Path path) throws ConfigParseException public static SimulationConfig parse(InputStream in) throws ConfigParseException } package cellsociety.model.config; public final class ConfigValidator { public static void validateSimulationType(String type) throws ConfigValidationException public static void validateDimensions(int rows, int cols) throws ConfigValidationException public static void validateInitialGrid( public static void validateConfiguration(SimulationConfig config) } package cellsociety.model.config; public record SimulationConfig( public record SimulationConfig( } package cellsociety.model.neighborhoods; public interface BoundaryPolicy { Optional resolve(Position p, int rows, int cols); } package cellsociety.model.neighborhoods; public interface NeighborhoodPolicy { List neighbors(Position p, int rows, int cols); } package cellsociety.model.neighborhoods; public class ToroidalBoundary implements BoundaryPolicy { public Optional resolve(Position p, int rows, int cols) } package cellsociety.model.neighborhoods; public class NeighborProvider { public NeighborProvider(NeighborhoodPolicy neighborhoodPolicy, BoundaryPolicy boundaryPolicy) public List getNeighbors(Position p, int rows, int cols) public int countState(Position p, int stateId, GridView grid) } package cellsociety.model.neighborhoods; public class MooreNeighborhood implements NeighborhoodPolicy { public List neighbors(Position p, int rows, int cols) } package cellsociety.model.neighborhoods; public class VonNeumannNeighborhood implements NeighborhoodPolicy { public List neighbors(Position p, int rows, int cols) } package cellsociety.model.neighborhoods; public class FiniteBoundary implements BoundaryPolicy { public Optional resolve(Position p, int rows, int cols) } package cellsociety.model.simulation; public interface Simulation> { void step(); SimulationSnapshot snapshot(); cellsociety.model.config.SimulationMetadata metadata(); void reset(); StateCodec codec(); long stepCount(); } package cellsociety.model.simulation; public final class SimulationSnapshot { public SimulationSnapshot(GridView gridView, long step) public GridView grid() public long step() } package cellsociety.model.simulation; public class WaTorSimulation extends AbstractSimulation { public WaTorSimulation(Grid initialGrid, } package cellsociety.model.simulation; public class GameOfLifeSimulation extends AbstractSimulation { public GameOfLifeSimulation(Grid initialGrid, } package cellsociety.model.simulation; public class SchellingSimulation extends AbstractSimulation { public SchellingSimulation(Grid initialGrid, } package cellsociety.model.simulation; public class FireSimulation extends AbstractSimulation { public FireSimulation(Grid initialGrid, } package cellsociety.model.simulation; public abstract class AbstractSimulation> implements Simulation { protected AbstractSimulation(Grid initialGrid, public void step() protected GridView currentGrid() public SimulationSnapshot snapshot() public SimulationMetadata metadata() public void reset() public StateCodec codec() public long stepCount() } package cellsociety.model.simulation; public class PercolationSimulation extends AbstractSimulation { public PercolationSimulation(Grid initialGrid, } package cellsociety.model.state; public final class FireStateCodec extends EnumStateCodec { public FireStateCodec() public static FireStateCodec withExplicitIds(Map explicitIds) } package cellsociety.model.state; public interface StateCodec> { int toId(S state); S fromId(int id) throws InvalidStateIdException; Set allStates(); default String displayName(S state) { return state.name(); default String colorKey(S state) { return state.name().toLowerCase(); } package cellsociety.model.state; public class EnumStateCodec> implements StateCodec { public EnumStateCodec(Class enumClass) protected EnumStateCodec(Class enumClass, public final int toId(S state) public final S fromId(int id) throws InvalidStateIdException public final Set allStates() public String displayName(S state) public String colorKey(S state) public Class getEnumClass() public Map idToDisplayNameMap() } package cellsociety.model.state; public final class GolStateCodec extends EnumStateCodec { public GolStateCodec() public static GolStateCodec withExplicitIds(Map explicitIds) } package cellsociety.model.exceptions; public class ConfigParseException extends SimulationException { public ConfigParseException(String message) public ConfigParseException(String message, Throwable cause) } package cellsociety.model.exceptions; public class FactoryException extends RuntimeException { public FactoryException(String message) public FactoryException(String message, Throwable cause) } package cellsociety.model.exceptions; public class SimulationException extends RuntimeException { public SimulationException(String message) public SimulationException(String message, Throwable cause) } package cellsociety.model.exceptions; public class ParameterException extends RuntimeException { public ParameterException(String message) public ParameterException(String message, Throwable cause) } package cellsociety.model.exceptions; public class ConfigValidationException extends SimulationException { public ConfigValidationException(String message) public ConfigValidationException(String message, Throwable cause) } package cellsociety.model.exceptions; public class InvalidStateIdException extends RuntimeException { public InvalidStateIdException(int id) public InvalidStateIdException(String message) public InvalidStateIdException(String message, Throwable cause) } package cellsociety.model.exceptions; public class GridBoundsException extends RuntimeException { public GridBoundsException(String message) public GridBoundsException(String message, Throwable cause) } package cellsociety.model.exceptions; public class ResetException extends RuntimeException { public ResetException(String message, Throwable cause) public ResetException(String message) } package cellsociety.model.rules; public class PercolationRule implements TransitionRule { public int computeNextState(Position p, GridView current, NeighborProvider neighbors, StateCodec codec) } package cellsociety.model.rules; public class FireRule implements TransitionRule { public FireRule(double growthProb, double lightningProb) public FireRule(ParameterSet params) public int computeNextState(Position p, GridView current, NeighborProvider neighbors, StateCodec codec) } package cellsociety.model.rules; public class SchellingRule implements TransitionRule { public SchellingRule(double threshold) public SchellingRule(ParameterSet params) public Grid applyStep(Grid current, Grid next, NeighborProvider neighbors, StateCodec codec) } package cellsociety.model.rules; public class WaTorRule implements TransitionRule { public WaTorRule(int fishBreedTime, int sharkBreedTime, int sharkStarveTime, public WaTorRule(ParameterSet params, GridView initialGrid, StateCodec codec) public void reset(GridView initialGrid, StateCodec codec) public Grid applyStep(Grid current, Grid next, NeighborProvider neighbors, StateCodec codec) } package cellsociety.model.rules; public interface TransitionRule> { default int computeNextState(Position p, GridView current, NeighborProvider neighbors, StateCodec codec) { throw new UnsupportedOperationException("Must override computeNextState or applyStep"); default Grid applyStep(Grid current, Grid next, NeighborProvider neighbors, StateCodec codec) { int rows = current.rows(); int cols = current.cols(); for (int r = 0; r < rows; r++) { for (int c = 0; c < cols; c++) { Position p = new Position(r, c); next.setState(r, c, computeNextState(p, current, neighbors, codec)); default void reset(GridView initialGrid, StateCodec codec) { } package cellsociety.model.rules; public class GameOfLifeRule implements TransitionRule { public int computeNextState(Position p, GridView current, NeighborProvider neighbors, StateCodec codec) } package cellsociety.model.engine; public interface SimulationStateListener { void onStateChanged(); } package cellsociety.model.engine; public class SimulationEngine { public SimulationEngine() public void start() public void pause() public void reset() public void step() public boolean isRunning() public void setSpeed(double speed) public double getSpeed() public void increaseSpeed() public void decreaseSpeed() public void update() public long getStepCount() public void loadSimulation(SimulationConfig config) throws SimulationException public void loadFromFile(Path xmlPath) throws SimulationException public SimulationConfig getCurrentConfig() public SimulationConfig captureCurrentState() public void saveToFile(Path xmlPath) throws UnsupportedOperationException public void saveToFile(Path xmlPath, SimulationMetadata updatedMetadata) public int getRows() public int getCols() public int getCellState(int row, int col) public List> getAllCellStates() public void addStateChangeListener(SimulationStateListener listener) public void removeStateChangeListener(SimulationStateListener listener) public String getSimulationType() public String getTitle() public String getAuthor() public String getDescription() public Map getStateLegend() public Map getParameters() public void validateConfiguration(SimulationConfig config) throws SimulationException public boolean isValidState() } package cellsociety.model.engine; public final class SimulationFactory { public static Simulation create(SimulationConfig config) throws FactoryException } package cellsociety.model.grid; public interface Grid extends GridView { void setState(int row, int col, int state); Grid emptyCopy(); void fill(int state); } package cellsociety.model.grid; public interface GridView { int rows(); int cols(); int getState(int row, int col); } package cellsociety.model.grid; public record Position(int row, int col) { public record Position(int row, int col) } package cellsociety.model.grid; public class ArrayGrid implements Grid { public ArrayGrid(int rows, int cols) public int rows() public int cols() public int getState(int row, int col) public void setState(int row, int col, int state) public Grid emptyCopy() public void fill(int state) } package cellsociety.model.grid; public class DoubleBufferGrid { public DoubleBufferGrid(Grid current, Grid next) public Grid getCurrent() public Grid getNext() public void swap() } package cellsociety.view.app; public class ViewFactory { public ViewFactory() public GridView createCanvasGridView(double width, double height) public GridView createGridPaneGridView() public RootView createRootView() public ControlPanel createControlPanel(ControlPanelListener listener) public SpeedControl createSpeedControl(Consumer onSpeedChanged) public FileControlPanel createFileControlPanel(Runnable onLoad, Runnable onSave) public ToolbarView createToolbarView(Runnable onLoad, Runnable onSave) public InfoPanel createInfoPanel() public LegendView createLegendView() public AboutDialogView createAboutDialogView() } package cellsociety.view.app; public class Main extends Application { public static final String DATA_FILE_FOLDER public void start(Stage primaryStage) public void onStart() public void onPause() public void onReset() public void onStep() } package cellsociety.view.chooser; public class SaveLocationChooser { public SaveLocationChooser() public Optional showSaveDialog(Window ownerWindow) } package cellsociety.view.chooser; public class FileSaver { public record SaveResult(Path path, SimulationInfo info) {} public FileSaver() public Optional showSaveDialog(Window ownerWindow, SimulationInfo currentInfo) public record SaveResult(Path path, SimulationInfo info) } package cellsociety.view.chooser; public class MetadataEditor { public Optional showEditDialog(Window ownerWindow, SimulationInfo currentInfo) } package cellsociety.view.chooser; public class ConfigChooser { public ConfigChooser() public Optional showOpenDialog(Window ownerWindow) } package cellsociety.view.alert; public class AlertService { public AlertService(ResourceManager resources) public void showErrorByKey(String titleKey, String messageKey) public void showError(String title, String message) } package cellsociety.view.alert; public class ExceptionTranslator { public String translate(Exception e) } package cellsociety.view.layout; public class ToolbarView { public ToolbarView(Runnable loadAction, Runnable saveAction) public ToolBar getToolBar() public FileControlPanel getFileControlPanel() } package cellsociety.view.layout; public class RootView { public RootView() public void setCenter(Node node) public void setTop(Node node) public void setBottom(Node node) public void setLeft(Node node) public void setRight(Node node) public BorderPane getRoot() } package cellsociety.view.info; public class AboutDialogView { public void show(Window ownerWindow, SimulationInfo info) } package cellsociety.view.info; public class LegendView { public void update(SimulationInfo info) public void clear() public VBox getRoot() } package cellsociety.view.info; public class InfoPanel { public InfoPanel() public void update(SimulationInfo info) public void clear() public VBox getRoot() } package cellsociety.view.controls; public interface ControlPanelListener { void onStart(); void onPause(); void onReset(); void onStep(); } package cellsociety.view.controls; public class FileControlPanel { public FileControlPanel(Runnable onLoadRequested, Runnable onSaveRequested) public HBox getRoot() } package cellsociety.view.controls; public class ControlPanel { public ControlPanel(ControlPanelListener listener) public ControlPanel(ControlPanelListener listener, Consumer onSpeedChanged) public HBox getRoot() public SpeedControl getSpeedControl() public Slider getSpeedSlider() } package cellsociety.view.controls; public class SpeedControl { public SpeedControl(Consumer onSpeedChanged) public SpeedControl() public VBox getRoot() public Slider getSlider() public double getSpeed() public void setSpeed(double speed) } package cellsociety.view.localization; public class ResourceManager { public ResourceManager(String bundleName) public String get(String key, Object... args) } package cellsociety.view.localization; public class StyleManager { public StyleManager() public StyleManager(String stylesheetPath) public void applyTo(Scene scene) } package cellsociety.view.grid; public class GridPaneGridView implements GridView { public Node getNode() public void render(GridData gridData) public void resizeTo(GridDimensions dims) public void clear() } package cellsociety.view.grid; public interface GridView { Node getNode(); void render(GridData gridData); void resizeTo(GridDimensions dims); void clear(); } package cellsociety.view.grid; public class CanvasGridView implements GridView { public Node getNode() public void render(GridData gridData) public void resizeTo(GridDimensions dims) public void clear() }