Skip to content
Snippets Groups Projects
Commit 8f3a4ef2 authored by LABOUREL Arnaud's avatar LABOUREL Arnaud
Browse files

new template for project

parent b4f3f557
No related branches found
No related tags found
1 merge request!2Simple
Showing
with 638 additions and 217 deletions
......@@ -28,5 +28,5 @@ test {
}
application {
mainClass.set("App")
mainClass.set("FirefighterApplication")
}
\ No newline at end of file
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import static java.lang.Thread.sleep;
public class App extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) throws InterruptedException {
Group root = new Group();
Button restart = new Button("Restart");
VBox buttons = new VBox();
HBox total = new HBox();
Grid grid = new Grid(1000,1000,20,20);
root.getChildren().add(total);
total.getChildren().add(buttons);
total.getChildren().add(grid);
buttons.getChildren().add(restart);
restart.setOnMouseClicked(grid::restart);
primaryStage.setScene(new Scene(root));
primaryStage.show();
grid.repaint();
new Thread(new Runnable() {
@Override
public void run() {
while(true){
try {
sleep(50);
grid.model.activation();
grid.repaint();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
}
import controller.Controller;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import model.FirefighterBoard;
import java.io.IOException;
import java.net.URL;
public class FirefighterApplication extends Application {
private static final String VIEW_RESOURCE_PATH = "/view/view.fxml";
private static final String APP_NAME = "Firefighter simulator";
private static final int ROW_COUNT = 50;
private static final int COLUMN_COUNT = 50;
private static final int SQUARE_WIDTH = 20;
private static final int SQUARE_HEIGHT = 20;
public static final int INITIAL_FIRE_COUNT = 3;
public static final int INITIAL_FIREFIGHTER_COUNT = 6;
private Stage primaryStage;
private Parent view;
private void initializePrimaryStage(Stage primaryStage) {
this.primaryStage = primaryStage;
this.primaryStage.setTitle(APP_NAME);
this.primaryStage.setOnCloseRequest(event -> Platform.exit());
this.primaryStage.setResizable(false);
this.primaryStage.sizeToScene();
}
@Override
public void start(Stage primaryStage) throws IOException {
initializePrimaryStage(primaryStage);
initializeView();
showScene();
}
private void initializeView() throws IOException {
FXMLLoader loader = new FXMLLoader();
URL location = FirefighterApplication.class.getResource(VIEW_RESOURCE_PATH);
loader.setLocation(location);
view = loader.load();
Controller controller = loader.getController();
controller.initialize(SQUARE_WIDTH, SQUARE_HEIGHT, COLUMN_COUNT, ROW_COUNT,
INITIAL_FIRE_COUNT, INITIAL_FIREFIGHTER_COUNT);
}
private void showScene() {
Scene scene = new Scene(view);
primaryStage.setScene(scene);
primaryStage.show();
}
}
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
public class Grid extends Canvas{
int width, height, colCount, rowCount;
Model model;
public Grid(int width, int height, int colCount, int rowCount) {
super(width,height);
this.width = width;
this.height = height;
this.colCount = colCount;
this.rowCount = rowCount;
setFocusTraversable(true);
setOnMousePressed(this::mousePressed);
model = new Model(this);
model.initialisation(3,8);
}
public void restart(MouseEvent mouseEvent){
model = new Model(this);
model.initialisation(3,6);
getGraphicsContext2D().clearRect(0,0,width,height);
repaint();
}
private void mousePressed(MouseEvent mouseEvent) {
model.activation();
repaint();
/*double x = mouseEvent.getX();
double y = mouseEvent.getY();
model.click((int)x*rowCount/height,(int)y*colCount/width);*/
}
void repaint(){
for(int col=0; col<colCount; col++)
getGraphicsContext2D().strokeLine(0, col*width/colCount,height, col*width/colCount);
for(int row=0; row<rowCount;row++)
getGraphicsContext2D().strokeLine(row*height/rowCount,0,row*height/rowCount,width);
}
void paint(int row, int col){
getGraphicsContext2D().setFill(Color.WHITE);
getGraphicsContext2D().fillRect(row*height/rowCount,col*width/colCount,height/rowCount,width/colCount);
}
public void paintFF(int row, int col) {
getGraphicsContext2D().setFill(Color.BLUE);
getGraphicsContext2D().fillRect(row*height/rowCount,col*width/colCount,height/rowCount,width/colCount);
}
public void paintFire(int row, int col) {
getGraphicsContext2D().setFill(Color.RED);
getGraphicsContext2D().fillRect(row*height/rowCount,col*width/colCount,height/rowCount,width/colCount);
}
}
\ No newline at end of file
import java.util.*;
public class Model {
Grid grid;
int colCount, rowCount;
List<Position> firefighters = new ArrayList<>();
Set<Position> fires = new HashSet<>();
List<Position> ffNewPositions;
int step = 0;
public Model(Grid grid) {
this.grid = grid;
colCount = grid.colCount;
rowCount = grid.rowCount;
}
public void initialisation(int fireNumber, int fireFighterNumber){
for(int index=0; index<fireNumber;index++)
fires.add(randomPosition());
for(int index=0; index<fireFighterNumber;index++)
firefighters.add(randomPosition());
}
private Position randomPosition() {
return new Position((int) (Math.random()*rowCount), (int) (Math.random()*colCount));
}
public void activation(){
ffNewPositions = new ArrayList<>();
for(Position ff : firefighters){
Position newPosition = activateFirefighter(ff);
grid.paint(ff.row,ff.col);
grid.paintFF(newPosition.row, newPosition.col);
ffNewPositions.add(newPosition);
}
firefighters = ffNewPositions;
if(step%2==0){
List<Position> newFires = new ArrayList<>();
for(Position fire : fires){
newFires.addAll(activateFire(fire));
}
for(Position newFire : newFires)
grid.paintFire(newFire.row, newFire.col);
fires.addAll(newFires);}
step++;
}
private List<Position> activateFire(Position position) {
return next(position);
}
private Position activateFirefighter(Position position) {
Position randomPosition = aStepTowardFire(position);
//next(position).get((int) (Math.random()*next(position).size()));
List<Position> nextFires = next(randomPosition).stream().filter(fires::contains).toList();
extinguish(randomPosition);
for (Position fire : nextFires)
extinguish(fire);
return randomPosition;
}
private void extinguish(Position position) {
fires.remove(position);
grid.paint(position.row, position.col);
}
private List<Position> next(Position position){
List<Position> list = new ArrayList<>();
if(position.row>0) list.add(new Position(position.row-1, position.col));
if(position.col>0) list.add(new Position(position.row, position.col-1));
if(position.row<rowCount-1) list.add(new Position(position.row+1, position.col));
if(position.col<colCount-1) list.add(new Position(position.row, position.col+1));
return list;
}
private Position aStepTowardFire(Position position){
Queue<Position> toVisit = new LinkedList<>();
Set<Position> seen = new HashSet<>();
HashMap<Position,Position> firstMove = new HashMap<>();
toVisit.addAll(next(position));
for(Position initialMove : toVisit)
firstMove.put(initialMove,initialMove);
while(!toVisit.isEmpty()){
Position current = toVisit.poll();
if(fires.contains(current))
return firstMove.get(current);
for(Position adjacent : next(current)){
if(seen.contains(adjacent)) continue;
toVisit.add(adjacent);
seen.add(adjacent);
firstMove.put(adjacent, firstMove.get(current));
}
}
return position;
}
public record Position(int row, int col){}
}
\ No newline at end of file
package controller;
import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.ToggleButton;
import javafx.scene.control.ToggleGroup;
import javafx.util.Duration;
import javafx.util.Pair;
import model.Board;
import model.ModelElement;
import model.FirefighterBoard;
import model.Position;
import view.FirefighterGrid;
import view.ViewElement;
import java.util.ArrayList;
import java.util.List;
import static java.util.Objects.requireNonNull;
public class Controller {
public static final int PERIOD_IN_MILLISECONDS = 50;
@FXML
private Button restartButton;
@FXML
private ToggleButton pauseToggleButton;
@FXML
private ToggleButton playToggleButton;
@FXML
private FirefighterGrid grid;
private Timeline timeline;
private Board<List<ModelElement>> board;
@FXML
private void initialize() {
initializePlayAndPauseToggleButtons();
initializeTimeline();
}
private void initializePlayAndPauseToggleButtons() {
ToggleGroup toggleGroup = new PersistentToggleGroup();
toggleGroup.getToggles().addAll(playToggleButton, pauseToggleButton);
pauseToggleButton.setSelected(true);
}
private void setModel(FirefighterBoard firefighterBoard) {
this.board = requireNonNull(firefighterBoard, "model is null");
}
private void updateBoard(){
List<Position> updatedPositions = board.updateToNextGeneration();
List<Pair<Position, ViewElement>> updatedSquares = new ArrayList<>();
for(Position updatedPosition : updatedPositions){
List<ModelElement> squareState = board.getState(updatedPosition);
ViewElement viewElement = getViewElement(squareState);
updatedSquares.add(new Pair<>(updatedPosition, viewElement));
}
grid.repaint(updatedSquares);
}
private void repaintBoard(){
int columnCount = board.columnCount();
int rowCount = board.rowCount();
ViewElement[][] viewElements = new ViewElement[rowCount][columnCount];
for(int column = 0; column < columnCount; column++)
for(int row = 0; row < rowCount; row++)
viewElements[row][column] = getViewElement(board.getState(new Position(row, column)));
grid.repaint(viewElements);
}
private ViewElement getViewElement(List<ModelElement> squareState) {
if(squareState.contains(ModelElement.FIREFIGHTER)){
return ViewElement.FIREFIGHTER;
}
if (squareState.contains(ModelElement.FIRE)){
return ViewElement.FIRE;
}
return ViewElement.EMPTY;
}
private void initializeTimeline() {
Duration duration = new Duration(Controller.PERIOD_IN_MILLISECONDS);
EventHandler<ActionEvent> eventHandler =
event -> updateBoard();
KeyFrame keyFrame = new KeyFrame(duration, eventHandler);
timeline = new Timeline(keyFrame);
timeline.setCycleCount(Animation.INDEFINITE);
}
public void play() {
timeline.play();
}
public void pause() {
timeline.pause();
}
public void pauseToggleButtonAction(ActionEvent actionEvent) {
this.pause();
}
public void playToggleButtonAction(ActionEvent actionEvent) {
this.play();
}
public void restartButtonAction(ActionEvent actionEvent) {
this.pause();
board.reset();
pauseToggleButton.setSelected(true);
repaintBoard();
}
public void initialize(int squareWidth, int squareHeight, int columnCount,
int rowCount, int initialFireCount, int initialFirefighterCount) {
grid.initialize(squareWidth, squareHeight, columnCount, rowCount);
this.setModel(new FirefighterBoard(columnCount, rowCount, initialFireCount, initialFirefighterCount));
repaintBoard();
}
}
\ No newline at end of file
package controller;
import javafx.collections.ListChangeListener.Change;
import javafx.scene.control.Toggle;
import javafx.scene.control.ToggleButton;
import javafx.scene.control.ToggleGroup;
import javafx.scene.input.MouseEvent;
/**
* An extension of {@link ToggleGroup} that ensures that a {@link Toggle} in a group must always be
* selected.
*
*/
class PersistentToggleGroup extends ToggleGroup {
/**
* Creates a new {@code PersistentToggleGroup}.
*/
PersistentToggleGroup() {
getToggles().addListener((Change<? extends Toggle> change) -> {
while (change.next()) {
for (Toggle toggle : change.getAddedSubList()) {
ToggleButton toggleButton = (ToggleButton) toggle;
toggleButton.addEventFilter(MouseEvent.MOUSE_RELEASED, mouseEvent -> {
if (toggleButton.equals(getSelectedToggle())) {
mouseEvent.consume();
}
});
}
}
});
}
}
package model;
import java.util.List;
public interface Board<S> {
S getState(Position position);
int rowCount();
int columnCount();
List<Position> updateToNextGeneration();
void reset();
}
package model;
import java.util.*;
public class FirefighterBoard implements Board<List<ModelElement>> {
private final int columnCount;
private final int rowCount;
private final int initialFireCount;
private final int initialFirefighterCount;
List<Position> firefighterPositions;
Set<Position> firePositions;
List<Position> firefighterNewPositions;
int step = 0;
public FirefighterBoard(int columnCount, int rowCount, int initialFireCount, int initialFirefighterCount) {
this.columnCount = columnCount;
this.rowCount = rowCount;
this.initialFireCount = initialFireCount;
this.initialFirefighterCount = initialFirefighterCount;
initializeElements();
}
public void initializeElements() {
firefighterPositions = new ArrayList<>();
firePositions = new HashSet<>();
for (int index = 0; index < initialFireCount; index++)
firePositions.add(randomPosition());
for (int index = 0; index < initialFirefighterCount; index++)
firefighterPositions.add(randomPosition());
}
private Position randomPosition() {
return new Position((int) (Math.random() * rowCount), (int) (Math.random() * columnCount));
}
@Override
public List<ModelElement> getState(Position position) {
List<ModelElement> result = new ArrayList<>();
for(Position firefighterPosition : firefighterPositions)
if (firefighterPosition.equals(position))
result.add(ModelElement.FIREFIGHTER);
if(firePositions.contains(position))
result.add(ModelElement.FIRE);
return result;
}
@Override
public int rowCount() {
return rowCount;
}
@Override
public int columnCount() {
return columnCount;
}
public List<Position> updateToNextGeneration() {
List<Position> result = activateFirefighters();
result.addAll(activateFires());
step++;
return result;
}
private List<Position> activateFires() {
List<Position> result = new ArrayList<>();
if (step % 2 == 0) {
List<Position> newFirePositions = new ArrayList<>();
for (Position fire : firePositions) {
newFirePositions.addAll(neighbors(fire));
}
firePositions.addAll(newFirePositions);
result.addAll(newFirePositions);
}
return result;
}
private List<Position> activateFirefighters() {
List<Position> result = new ArrayList<>();
firefighterNewPositions = new ArrayList<>();
for (Position firefighterPosition : firefighterPositions) {
Position newFirefighterPosition = neighborClosestToFire(firefighterPosition);
result.add(firefighterPosition);
result.add(newFirefighterPosition);
firefighterNewPositions.add(newFirefighterPosition);
extinguish(newFirefighterPosition);
List<Position> neighborFirePositions = neighbors(newFirefighterPosition).stream().filter(firePositions::contains).toList();
for(Position firePosition : neighborFirePositions)
extinguish(firePosition);
result.addAll(firePositions);
}
firefighterPositions = firefighterNewPositions;
return result;
}
@Override
public void reset() {
initializeElements();
}
private void extinguish(Position position) {
firePositions.remove(position);
}
private List<Position> neighbors(Position position) {
List<Position> list = new ArrayList<>();
if (position.row() > 0) list.add(new Position(position.row() - 1, position.column()));
if (position.column() > 0) list.add(new Position(position.row(), position.column() - 1));
if (position.row() < rowCount - 1) list.add(new Position(position.row() + 1, position.column()));
if (position.column() < columnCount - 1) list.add(new Position(position.row(), position.column() + 1));
return list;
}
private Position neighborClosestToFire(Position position) {
Set<Position> seen = new HashSet<>();
HashMap<Position, Position> firstMove = new HashMap<>();
Queue<Position> toVisit = new LinkedList<>(neighbors(position));
for (Position initialMove : toVisit)
firstMove.put(initialMove, initialMove);
while (!toVisit.isEmpty()) {
Position current = toVisit.poll();
if (firePositions.contains(current))
return firstMove.get(current);
for (Position adjacent : neighbors(current)) {
if (seen.contains(adjacent)) continue;
toVisit.add(adjacent);
seen.add(adjacent);
firstMove.put(adjacent, firstMove.get(current));
}
}
return position;
}
}
\ No newline at end of file
package model;
public enum ModelElement {
FIREFIGHTER, FIRE
}
package model;
public record Position(int row, int column) {
}
package view;
import javafx.scene.canvas.Canvas;
import javafx.scene.paint.Color;
import javafx.util.Pair;
import model.Position;
import java.util.List;
public class FirefighterGrid extends Canvas implements Grid<ViewElement>{
private void paintElementAtPosition(ViewElement element, Position position) {
paintSquare(position.row(), position.column(), element.color);
}
private int squareWidth;
private int squareHeight;
private int columnCount;
private int rowCount;
@Override
public void repaint(List<Pair<Position, ViewElement>> positionedElements) {
for(Pair<Position, ViewElement> pair : positionedElements){
paintElementAtPosition(pair.getValue(), pair.getKey());
}
paintLines();
}
@Override
public void repaint(ViewElement[][] elements) {
for(int column = 0; column < columnCount; column++)
for(int row = 0; row < rowCount; row++){
paintElementAtPosition(elements[row][column], new Position(row, column));
}
paintLines();
}
public int getColumnCount() {
return columnCount;
}
public int getRowCount() {
return rowCount;
}
public FirefighterGrid(){
}
public void initialize(int squareWidth,
int squareHeight,
int columnCount,
int rowCount) {
this.squareWidth = squareWidth;
this.squareHeight = squareHeight;
this.columnCount = columnCount;
this.rowCount = rowCount;
}
private void paintLines(){
for(int column=0; column<columnCount; column++)
getGraphicsContext2D().strokeLine(0, column*squareHeight, getWidth(), column*squareWidth);
for(int row=0; row<rowCount;row++)
getGraphicsContext2D().strokeLine(row*squareHeight, 0,row*squareHeight, getHeight());
}
private void paintSquare(int row, int column, Color color){
getGraphicsContext2D().setFill(color);
getGraphicsContext2D().fillRect(row*squareHeight,column*squareWidth,squareHeight,squareWidth);
}
}
\ No newline at end of file
package view;
import javafx.scene.paint.Color;
import javafx.util.Pair;
import model.Position;
import java.util.List;
public interface Grid<E> {
void repaint(List<Pair<Position,E>> elements);
void repaint(E[][] elements);
int getColumnCount();
int getRowCount();
}
package view;
import javafx.scene.paint.Color;
public enum ViewElement {
FIREFIGHTER(Color.BLUE), FIRE(Color.RED), EMPTY(Color.WHITE);
final Color color;
ViewElement(Color color) {
this.color = color;
}
}
.background {
-fx-background-color: #1d1d1d;
}
.label {
-fx-font-size: 11pt;
-fx-font-family: "Segoe UI Semibold";
-fx-text-fill: white;
-fx-opacity: 0.6;
}
.label-bright {
-fx-font-size: 11pt;
-fx-font-family: "Segoe UI Semibold";
-fx-text-fill: white;
-fx-opacity: 1;
}
.label-header {
-fx-font-size: 32pt;
-fx-font-family: "Segoe UI Light";
-fx-text-fill: white;
-fx-opacity: 1;
}
.table-view {
-fx-base: #1d1d1d;
-fx-control-inner-background: #1d1d1d;
-fx-background-color: #1d1d1d;
-fx-table-cell-border-color: transparent;
-fx-table-header-border-color: transparent;
-fx-padding: 5;
}
.table-view .column-header-background {
-fx-background-color: transparent;
}
.table-view .column-header, .table-view .filler {
-fx-border-width: 0 0 1 0;
-fx-background-color: transparent;
-fx-border-color:
transparent
transparent
derive(-fx-base, 80%)
transparent;
-fx-border-insets: 0 10 1 0;
}
.table-view .column-header .label {
-fx-font-size: 20pt;
-fx-font-family: "Segoe UI Light";
-fx-text-fill: white;
-fx-alignment: center-left;
-fx-opacity: 1;
}
.table-view:focused .table-row-cell:filled:focused:selected {
-fx-background-color: -fx-focus-color;
}
.split-pane:horizontal > .split-pane-divider {
-fx-border-color: transparent #1d1d1d transparent #1d1d1d;
-fx-background-color: transparent, derive(#1d1d1d,20%);
}
.split-pane {
-fx-padding: 1 0 0 0;
}
.menu-bar {
-fx-background-color: derive(#1d1d1d,20%);
}
.context-menu {
-fx-background-color: derive(#1d1d1d,50%);
}
.menu-bar .label {
-fx-font-size: 14pt;
-fx-font-family: "Segoe UI Light";
-fx-text-fill: white;
-fx-opacity: 0.9;
}
.menu .left-container {
-fx-background-color: black;
}
.text-field {
-fx-font-size: 12pt;
-fx-font-family: "Segoe UI Semibold";
}
/*
* Metro style Push Button
* Author: Pedro Duque Vieira
* http://pixelduke.wordpress.com/2012/10/23/jmetro-windows-8-controls-on-java/
*/
.button {
-fx-padding: 5 22 5 22;
-fx-border-color: #e2e2e2;
-fx-border-width: 2;
-fx-background-radius: 0;
-fx-background-color: #1d1d1d;
-fx-font-family: "Segoe UI", Helvetica, Arial, sans-serif;
-fx-font-size: 11pt;
-fx-text-fill: #d8d8d8;
-fx-background-insets: 0 0 0 0, 0, 1, 2;
}
.button:hover {
-fx-background-color: #3a3a3a;
}
.button:pressed, .button:default:hover:pressed {
-fx-background-color: white;
-fx-text-fill: #1d1d1d;
}
.button:focused {
-fx-border-color: white, white;
-fx-border-width: 1, 1;
-fx-border-style: solid;
-fx-border-radius: 0, 0;
-fx-border-insets: 1 1 1 1, 0;
}
.button:disabled, .button:default:disabled {
-fx-opacity: 0.4;
-fx-background-color: #1d1d1d;
-fx-text-fill: white;
}
.button:default {
-fx-background-color: -fx-focus-color;
-fx-text-fill: #ffffff;
}
.button:default:hover {
-fx-background-color: derive(-fx-focus-color,30%);
}
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<?import view.FirefighterGrid?>
<?import javafx.scene.control.ToggleButton?>
<HBox styleClass="background" stylesheets="@DarkTheme.css"
xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml"
fx:controller="controller.Controller">
<VBox>
<Button fx:id="restartButton" maxHeight="-Infinity" maxWidth="-Infinity"
mnemonicParsing="false" onAction="#restartButtonAction" prefHeight="24.0" prefWidth="200.0"
text="Restart"/>
<ToggleButton fx:id="playToggleButton" maxHeight="-Infinity" maxWidth="-Infinity"
mnemonicParsing="false" onAction="#playToggleButtonAction" prefHeight="24.0"
prefWidth="200.0" styleClass="button" text="Play"/>
<ToggleButton fx:id="pauseToggleButton" maxHeight="-Infinity" maxWidth="-Infinity"
mnemonicParsing="false" onAction="#pauseToggleButtonAction" prefHeight="24.0"
prefWidth="200.0" styleClass="button" text="Pause"/>
</VBox>
<FirefighterGrid fx:id="grid" width="1000.0" height="1000.0"
xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml">
</FirefighterGrid>
</HBox>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment