Skip to content

Feat/java fx boilerplate #40

Merged
merged 4 commits into from
Apr 22, 2026
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
<artifactId>javafx-maven-plugin</artifactId>
<version>0.0.8</version>
<configuration>
<mainClass>temppackage.Main</mainClass>
<mainClass>millions.App</mainClass>
</configuration>
</plugin>
<plugin>
Expand Down
25 changes: 25 additions & 0 deletions src/main/java/millions/App.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package millions;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
import millions.controller.GameController;
import millions.view.StartView;

public class App extends Application {

@Override
public void start(Stage stage) {
GameController controller = new GameController();
StartView startView = new StartView(stage);

Scene scene = new Scene(startView, 400, 350);
stage.setTitle("Millions");
stage.setScene(scene);
stage.show();
}

public static void main(String[] args) {
launch(args);
}
}
33 changes: 33 additions & 0 deletions src/main/java/millions/controller/GameController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package millions.controller;

import java.math.BigDecimal;
import java.nio.file.Path;
import java.util.List;
import millions.controller.fileIO.CSVStockFileParser;
import millions.controller.fileIO.StockFileReader;
import millions.model.Exchange;
import millions.model.Player;
import millions.model.Stock;

public class GameController {
private Player player;
private Exchange exchange;

public void startGame(String name, BigDecimal startingMoney, Path stockFilePath) {
StockFileReader reader = new StockFileReader(stockFilePath);
List<String> lines = reader.readFile();
CSVStockFileParser parser = new CSVStockFileParser(lines);
List<Stock> stocks = parser.parse();

player = new Player(name, startingMoney);
exchange = new Exchange("Exchange", stocks);
}

public Player getPlayer() {
return player;
}

public Exchange getExchange() {
return exchange;
}
}
25 changes: 25 additions & 0 deletions src/main/java/millions/model/Exchange.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
Expand All @@ -20,6 +21,7 @@ public class Exchange {
private Random random = new Random();
private final TransactionFactory purchaseFactory = new PurchaseFactory();
private final TransactionFactory saleFactory = new SaleFactory();
private final List<ExchangeListener> listeners = new ArrayList<>();

public Exchange(String name, List<Stock> stockList) {
this.name = name;
Expand Down Expand Up @@ -47,6 +49,7 @@ public Transaction buy(String symbol, Player player, BigDecimal quantity) {

Transaction purchase = purchaseFactory.createTransaction(shareToBuy, weekNumber);
purchase.commit(player);
notifyTransactionCompleted(purchase);

return purchase;
}
Expand All @@ -59,6 +62,7 @@ public Transaction sell(Share share, Player player) {
Transaction sale = saleFactory.createTransaction(share, weekNumber);

sale.commit(player);
notifyTransactionCompleted(sale);
return sale;
}

Expand Down Expand Up @@ -107,5 +111,26 @@ public void advance() {
.setScale(2, RoundingMode.HALF_UP));
// RoundingMode from AI suggestion
}
notifyWeekAdvanced();
}

public void addListener(ExchangeListener listener) {
listeners.add(listener);
}

public void removeListener(ExchangeListener listener) {
listeners.remove(listener);
}

private void notifyWeekAdvanced() {
for (ExchangeListener listener : listeners) {
listener.onWeekAdvanced(weekNumber);
}
}

private void notifyTransactionCompleted(Transaction transaction) {
for (ExchangeListener listener : listeners) {
listener.onTransactionCompleted(transaction);
}
}
}
8 changes: 8 additions & 0 deletions src/main/java/millions/model/ExchangeListener.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package millions.model;

public interface ExchangeListener {

void onWeekAdvanced(int newWeek);

void onTransactionCompleted(Transaction transaction);
}
43 changes: 43 additions & 0 deletions src/main/java/millions/model/Player.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;

public class Player {
private String name;
Expand All @@ -11,6 +13,7 @@ public class Player {
private TransactionArchive transactionArchive;
// temporary attribute until a better solution is found
public int weeksTraded;
private final List<PlayerListener> listeners = new ArrayList<>();

public Player(String name, BigDecimal startingMoney) {
this.name = name;
Expand All @@ -33,13 +36,15 @@ public void addMoney(BigDecimal amount) {
throw new IllegalArgumentException("Amount cannot be null or negative");
}
this.money = this.money.add(amount);
notifyMoneyChanged();
}

public void withdrawMoney(BigDecimal amount) {
if (amount == null || amount.compareTo(BigDecimal.ZERO) < 0) {
throw new IllegalArgumentException("Amount cannot be null or negative");
}
this.money = this.money.subtract(amount);
notifyMoneyChanged();
}

public String getStatus() {
Expand Down Expand Up @@ -70,11 +75,49 @@ public Portfolio getPortfolio() {
return this.portfolio;
}

public void addShareToPortfolio(Share share) {
this.portfolio.addShare(share);
notifyPortfolioChanged();
notifyStatusChanged();
}

public void removeShareFromPortfolio(Share share) {
this.portfolio.removeShare(share);
notifyPortfolioChanged();
notifyStatusChanged();
}

public BigDecimal getNetWorth() {
return this.money.add(this.portfolio.getNetWorth());
}

public TransactionArchive getTransactionArchive() {
return this.transactionArchive;
}

public void addListener(PlayerListener listener) {
listeners.add(listener);
}

public void removeListener(PlayerListener listener) {
listeners.remove(listener);
}

private void notifyMoneyChanged() {
for (PlayerListener listener : listeners) {
listener.onMoneyChanged(money);
}
}

private void notifyPortfolioChanged() {
for (PlayerListener listener : listeners) {
listener.onPortfolioChanged();
}
}

private void notifyStatusChanged() {
for (PlayerListener listener : listeners) {
listener.onStatusChanged(getStatus());
}
}
}
12 changes: 12 additions & 0 deletions src/main/java/millions/model/PlayerListener.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package millions.model;

import java.math.BigDecimal;

public interface PlayerListener {

void onMoneyChanged(BigDecimal newBalance);

void onPortfolioChanged();

void onStatusChanged(String newStatus);
}
3 changes: 2 additions & 1 deletion src/main/java/millions/model/Purchase.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ public void commit(Player player) {
throw new IllegalStateException("Not enought money");
}
player.withdrawMoney(getCalculator().calculateTotal());
player.getPortfolio().addShare(getShare());
// Don't reach directly to the portefolio object
player.addShareToPortfolio(getShare());
player.getTransactionArchive().add(this);
setCommitted(true);
}
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/millions/model/Sale.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public void commit(Player player) {
throw new IllegalStateException("Does not own the share");
}
player.addMoney(getCalculator().calculateTotal());
player.getPortfolio().removeShare(getShare());
player.removeShareFromPortfolio(getShare());
player.getTransactionArchive().add(this);
setCommitted(true);
}
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/millions/model/factories/PurchaseFactory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package millions.model.factories;

import millions.model.Purchase;
import millions.model.Share;

public class PurchaseFactory extends TransactionFactory {
public Purchase createTransaction(Share share, int week) {
return new Purchase(share, week);
}
}
10 changes: 10 additions & 0 deletions src/main/java/millions/model/factories/SaleFactory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package millions.model.factories;

import millions.model.Sale;
import millions.model.Share;

public class SaleFactory extends TransactionFactory {
public Sale createTransaction(Share share, int week) {
return new Sale(share, week);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package millions.model.factories;

import millions.model.Share;
import millions.model.Transaction;

public abstract class TransactionFactory {
public abstract Transaction createTransaction(Share share, int week);
}
94 changes: 94 additions & 0 deletions src/main/java/millions/view/StartView.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package millions.view;

import java.io.File;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.stage.FileChooser;
import javafx.stage.Stage;

public class StartView extends VBox {

private TextField nameField;
private TextField startingAmountField;
private File selectedFile;
private Button filepickerButton;
private Button startButton;

public StartView(Stage stage) {
setAlignment(Pos.CENTER);
setSpacing(12);
setPadding(new Insets(40));

nameField = new TextField();
nameField.setPromptText("Player name:");
nameField.setMaxWidth(250);
nameField.textProperty().addListener((obs, oldVal, newVal) -> checkStartButtonValid());

startingAmountField = new TextField();
startingAmountField.setPromptText("Starting amount:");
startingAmountField.setMaxWidth(250);
startingAmountField
.textProperty()
.addListener((obs, oldVal, newVal) -> checkStartButtonValid());

filepickerButton = new Button();
filepickerButton.setText("Pick file");
filepickerButton.setMaxWidth(250);
filepickerButton.setOnAction(
e -> {
FileChooser chooser = new FileChooser();
chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("CSV files", "*.csv"));
chooser.setTitle("Select stock CSV file");
File file = chooser.showOpenDialog(stage);

if (file != null) {
selectedFile = file;
filepickerButton.setText(file.getName());
}
});

startButton = new Button("Start game");
startButton.setDisable(true);

Label title = new Label("Millions");
title.setStyle("-fx-font-size: 32px; -fx-font-weight: bold;");

getChildren().addAll(title, nameField, startingAmountField, filepickerButton, startButton);
}

private void checkStartButtonValid() {
boolean valid = true;

if (nameField.getText().isBlank()) {
valid = false;
}

try {
Integer.valueOf(startingAmountField.getText());
} catch (NumberFormatException e) {
valid = false;
}

startButton.setDisable(!valid);
}

public String getName() {
return nameField.getText();
}

public String getStartingAmount() {
return startingAmountField.getText();
}

public File getSelectedFile() {
return selectedFile;
}

public Button getStartButton() {
return startButton;
}
}
Loading