From f07a5e5309c50d65853d1fb19e269b3b87f4a894 Mon Sep 17 00:00:00 2001 From: pawelsa Date: Sat, 23 May 2026 12:27:06 +0200 Subject: [PATCH 01/11] feat: Add week in toolbar --- .../ntnu/idi/idatt/session/UserSession.java | 13 ++++++++++ .../idatt/view/components/ui/UIFactory.java | 26 ++++++++++++------- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/src/main/java/edu/ntnu/idi/idatt/session/UserSession.java b/src/main/java/edu/ntnu/idi/idatt/session/UserSession.java index 2fef3e5..8531f80 100644 --- a/src/main/java/edu/ntnu/idi/idatt/session/UserSession.java +++ b/src/main/java/edu/ntnu/idi/idatt/session/UserSession.java @@ -5,6 +5,7 @@ import edu.ntnu.idi.idatt.model.Exchange; import edu.ntnu.idi.idatt.model.player.Player; import edu.ntnu.idi.idatt.storage.SessionManager; +import javafx.beans.property.SimpleIntegerProperty; import javafx.beans.property.SimpleObjectProperty; public class UserSession { @@ -41,10 +42,12 @@ public Exchange getExchange() { public void setExchange(Exchange exchange) { this.exchange = exchange; + updateGameState(); // Startup hook } private final SimpleObjectProperty moneyProperty = new SimpleObjectProperty<>(BigDecimal.ZERO); private final SimpleObjectProperty netWorthProperty = new SimpleObjectProperty<>(BigDecimal.ZERO); + private final SimpleIntegerProperty weekProperty = new SimpleIntegerProperty(); public SimpleObjectProperty moneyProperty() { return moneyProperty; @@ -54,9 +57,19 @@ public SimpleObjectProperty netWorthProperty() { return netWorthProperty; } + public SimpleIntegerProperty weekProperty() { + return weekProperty; + } + public void updateGameState() { + + if (player == null || exchange == null) { + return; + } + moneyProperty.set(player.getMoney()); netWorthProperty.set(player.getNetWorth()); + weekProperty.set(exchange.getWeek()); SessionManager.saveSession(); } diff --git a/src/main/java/edu/ntnu/idi/idatt/view/components/ui/UIFactory.java b/src/main/java/edu/ntnu/idi/idatt/view/components/ui/UIFactory.java index 2be519b..365580f 100644 --- a/src/main/java/edu/ntnu/idi/idatt/view/components/ui/UIFactory.java +++ b/src/main/java/edu/ntnu/idi/idatt/view/components/ui/UIFactory.java @@ -160,32 +160,38 @@ public static Parent createToolbar(ActionEventHandler menuHandle, ActionEventHan Label balance = new Label(); balance.textProperty().bind( UserSession.getInstance().moneyProperty().asString("Balance: %.2f $")); + Label week = new Label(); + week.textProperty().bind( + UserSession.getInstance().weekProperty().asString("Week: %d")); Label playerStatus = new Label("Status:" + UserSession.getInstance().getPlayer().getStatus()); Label playerNetWorth = new Label(); playerNetWorth.textProperty().bind( UserSession.getInstance().netWorthProperty().asString("Net Worth: %.2f $")); CssUtils.apply(balance, CssUtils.MED_TEXT_16); + CssUtils.apply(week, CssUtils.MED_TEXT_16); CssUtils.apply(playerStatus, CssUtils.MED_TEXT_16); CssUtils.apply(playerNetWorth, CssUtils.MED_TEXT_16); - VBox infoWrapper = new VBox(); - infoWrapper.setAlignment(Pos.CENTER); - infoWrapper.getChildren().addAll(playerStatus, playerNetWorth); - IconComponent userIcon = new IconComponent(ResourceUtils.MENU_ICON, null, 44); IconComponent quitIcon = new IconComponent(ResourceUtils.QUIT_ICON, null, 44); - HBox iconWrapper = new HBox(); - iconWrapper.setAlignment(Pos.CENTER); - iconWrapper.getChildren().addAll(userIcon, quitIcon); - UICompositor toolbar = new UICompositor.Builder() .parent(new HBox()) .growWithAlignment(Pos.CENTER) - .addContent(balance) + .wrap(new VBox()) + .properties(wrapper -> ((VBox) wrapper).setAlignment(Pos.CENTER)) + .addAllContent(balance, week) + .unwrap() .filler() - .addAllContent(infoWrapper, iconWrapper) + .wrap(new VBox()) + .properties(wrapper -> ((VBox) wrapper).setAlignment(Pos.CENTER)) + .addAllContent(playerStatus, playerNetWorth) + .unwrap() + .wrap(new HBox()) + .properties(wrapper -> ((HBox) wrapper).setAlignment(Pos.CENTER)) + .addAllContent(userIcon, quitIcon) + .unwrap() .build(); userIcon.onIconClick(() -> menuHandle.handle()); From 7ef974d7c9591f9681fd17222bcfc080a1eb9370 Mon Sep 17 00:00:00 2001 From: pawelsa Date: Sat, 23 May 2026 12:27:33 +0200 Subject: [PATCH 02/11] feat: Create Newspaper models for additional feature --- .../edu/ntnu/idi/idatt/model/Newspaper.java | 57 +++++++++ .../idi/idatt/model/enums/NewspaperEnum.java | 109 ++++++++++++++++++ 2 files changed, 166 insertions(+) create mode 100644 src/main/java/edu/ntnu/idi/idatt/model/Newspaper.java create mode 100644 src/main/java/edu/ntnu/idi/idatt/model/enums/NewspaperEnum.java diff --git a/src/main/java/edu/ntnu/idi/idatt/model/Newspaper.java b/src/main/java/edu/ntnu/idi/idatt/model/Newspaper.java new file mode 100644 index 0000000..da6aa1f --- /dev/null +++ b/src/main/java/edu/ntnu/idi/idatt/model/Newspaper.java @@ -0,0 +1,57 @@ +package edu.ntnu.idi.idatt.model; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import edu.ntnu.idi.idatt.model.enums.NewspaperEnum; + +public class Newspaper { + + ArrayList news = new ArrayList<>(); + private static double chance = 0.05; // In percent + + public NewspaperEnum makeNews() { + Random r = new Random(); + double roll = r.nextDouble(); + + if (roll >= chance) { + news.add(NewspaperEnum.NONE_EVENT); + return NewspaperEnum.NONE_EVENT; + } + + int message = r.nextInt(NewspaperEnum.values().length - 1); // Do not include last + NewspaperEnum event = NewspaperEnum.values()[message]; + + news.add(event); + return event; + } + + public ArrayList getNews() { + return news; + } + + public List getNewsStrings() { + + ArrayList strings = new ArrayList<>(); + + int i = 1; // First possible new happens at week 1 + for (NewspaperEnum newsEnum : news) { + + if (newsEnum == NewspaperEnum.NONE_EVENT) { + i++; + continue; + } + + strings.add(String.format("Week: %d - %s [%s]", + i, + newsEnum.getTitle(), + newsEnum.getDescription())); + i++; + + } + return strings; + + } + +} diff --git a/src/main/java/edu/ntnu/idi/idatt/model/enums/NewspaperEnum.java b/src/main/java/edu/ntnu/idi/idatt/model/enums/NewspaperEnum.java new file mode 100644 index 0000000..bc48f3e --- /dev/null +++ b/src/main/java/edu/ntnu/idi/idatt/model/enums/NewspaperEnum.java @@ -0,0 +1,109 @@ +package edu.ntnu.idi.idatt.model.enums; + +public enum NewspaperEnum { + NEW_PRODUCT( + "New product announced!", + "The company revealed plans for a major new product launch next week!", + 0.08, + 0.02), + + QUARTER( + "Quarterly earnings released!", + "The company's quarterly earnings report exceeded analyst expectations!", + 0.05, + 0.03), + + DISASTER( + "Natural disaster struck!", + "A natural disaster disrupted several of the company's factories!", + -0.08, + 0.08), + + RESEARCH_FUNDS( + "Government research funding!", + "The government approved additional funding for the company's research division!", + 0.04, + 0.04), + + FRAUD( + "Fraud investigation!", + "The company's CEO is under investigation for potential fraud!", + -0.05, + 0.12), + + NEW_PARTNERSHIP( + "Major partnership formed!", + "The company announced a strategic partnership with another corporation!", + 0.07, + 0.03), + + PRODUCT_RECALL( + "Product recall issued!", + "The company recalled one of its products over safety concerns!", + -0.08, + 0.07), + + SUPPLY_SHORTAGE( + "Supply shortage reported!", + "Global supply shortages are slowing the company's production!", + -0.06, + 0.05), + + FACTORY_EXPANSION( + "Factory expansion planned!", + "The company announced plans to expand manufacturing capacity!", + 0.06, + 0.02), + + PATENT_APPROVED( + "Patent approved!", + "The company secured a major technology patent approval!", + 0.09, + 0.03), + + PATENT_DENIED( + "Patent denied!", + "Regulators denied one of the company's patent applications!", + -0.08, + 0.05), + + MARKET_MANIPULATION_INVESTIGATION( + "Market investigation launched!", + "Regulators opened a market manipulation investigation into the company!", + -0.03, + 0.15), + NONE_EVENT( + "", + "", + 0, + 0); + + private final String title; + private final String description; + private final double trend; + private final double volatility; // How violently the price moves factor + + NewspaperEnum(String title, String description, double trend, double volatility) { + this.title = title; + this.description = description; + this.trend = trend; + this.volatility = volatility; + } + + public String getTitle() { + return title; + } + + public String getDescription() { + return description; + } + + public double getTrend() { + return trend; + } + + public double getVolatility() { + return volatility; + } + +} From 41d3f32bf52e94778c1c690d3ca2183bd79c150c Mon Sep 17 00:00:00 2001 From: pawelsa Date: Sat, 23 May 2026 13:10:06 +0200 Subject: [PATCH 03/11] feat: Implement price advance method for each stock based on Newspaper events. --- .../edu/ntnu/idi/idatt/model/Exchange.java | 8 ++- .../edu/ntnu/idi/idatt/model/Newspaper.java | 6 ++- .../ntnu/idi/idatt/model/market/Stock.java | 53 +++++++++++++++++++ 3 files changed, 61 insertions(+), 6 deletions(-) diff --git a/src/main/java/edu/ntnu/idi/idatt/model/Exchange.java b/src/main/java/edu/ntnu/idi/idatt/model/Exchange.java index e41a2c0..db005da 100644 --- a/src/main/java/edu/ntnu/idi/idatt/model/Exchange.java +++ b/src/main/java/edu/ntnu/idi/idatt/model/Exchange.java @@ -1,7 +1,6 @@ package edu.ntnu.idi.idatt.model; import java.math.BigDecimal; -import java.math.RoundingMode; import java.util.*; import edu.ntnu.idi.idatt.model.market.Stock; @@ -195,11 +194,10 @@ public Transaction sell(Share share, Player player) { * @see Stock */ public void advance() { - this.week += 1; - Random random = new Random(); stockMap.values() - .forEach(s -> s.addNewSalesPrice(s.getSalesPrice() - .multiply(BigDecimal.valueOf(random.nextDouble(0.8, 1.4))).setScale(2, RoundingMode.HALF_UP))); + .forEach(stock -> stock.advancePrice()); + + this.week += 1; } } diff --git a/src/main/java/edu/ntnu/idi/idatt/model/Newspaper.java b/src/main/java/edu/ntnu/idi/idatt/model/Newspaper.java index da6aa1f..4651b30 100644 --- a/src/main/java/edu/ntnu/idi/idatt/model/Newspaper.java +++ b/src/main/java/edu/ntnu/idi/idatt/model/Newspaper.java @@ -8,7 +8,7 @@ public class Newspaper { - ArrayList news = new ArrayList<>(); + ArrayList news = new ArrayList<>(List.of(NewspaperEnum.NONE_EVENT)); private static double chance = 0.05; // In percent public NewspaperEnum makeNews() { @@ -27,6 +27,10 @@ public NewspaperEnum makeNews() { return event; } + public boolean hasNewNews() { + return news.getLast().equals(NewspaperEnum.NONE_EVENT) ? false : true; + } + public ArrayList getNews() { return news; } diff --git a/src/main/java/edu/ntnu/idi/idatt/model/market/Stock.java b/src/main/java/edu/ntnu/idi/idatt/model/market/Stock.java index cff8ad3..fc64df5 100644 --- a/src/main/java/edu/ntnu/idi/idatt/model/market/Stock.java +++ b/src/main/java/edu/ntnu/idi/idatt/model/market/Stock.java @@ -5,6 +5,10 @@ import java.util.ArrayList; import java.util.Comparator; import java.util.List; +import java.util.Random; + +import edu.ntnu.idi.idatt.model.Newspaper; +import edu.ntnu.idi.idatt.model.enums.NewspaperEnum; /** * Stock class @@ -19,6 +23,11 @@ public class Stock { private final String symbol; private final String company; private final ArrayList prices = new ArrayList<>(); + private final Newspaper newspaper = new Newspaper(); + + private double trend; // Directional price movement + private double volatility; // How violent the price behaves + private double momentum; // Price continuation factor /** * Constructor for a Stock. @@ -33,6 +42,12 @@ public Stock(String symbol, String company, List prices) { this.symbol = symbol; this.company = company; this.prices.addAll(prices); + + // Initial values for price movement + Random r = new Random(); + trend = r.nextDouble(-0.01, 0.01); + volatility = r.nextDouble(0.02, 0.11); + momentum = 0; } /** @@ -119,6 +134,44 @@ public void addNewSalesPrice(BigDecimal price) { prices.add(price); } + // TODO: JavaDocs ETC + public Newspaper getNewspaper() { + return newspaper; + } + + private double calculatePriceFactor() { + double noice = (new Random().nextGaussian() + 0.2) * volatility; + + return 0.01 /* positive trend */ + trend + momentum + noice; + } + + private void calibrateNextPriceFactor() { + + momentum = calculatePriceFactor() * 0.35; + trend *= 0.9 + new Random().nextGaussian(); + + // Reduce accumulation if constant effects applied + trend = Math.clamp(trend, -0.06, 0.08); + + volatility *= 0.95; + + NewspaperEnum type = newspaper.makeNews(); + trend += type.getTrend(); + volatility += type.getVolatility(); + + } + + public void advancePrice() { + BigDecimal currentPrice = this.getSalesPrice(); + BigDecimal priceFactor = currentPrice.multiply(BigDecimal.valueOf(this.calculatePriceFactor())); + BigDecimal newPrice = currentPrice.add(priceFactor); + newPrice = newPrice.setScale(2, RoundingMode.HALF_UP); + + this.addNewSalesPrice(newPrice); + + this.calibrateNextPriceFactor(); + } + // TODO: JavaDocs @Override public String toString() { From 3cfb7d6c0bb4afe91041f4fcc1370bcaeb420367 Mon Sep 17 00:00:00 2001 From: pawelsa Date: Sat, 23 May 2026 13:10:35 +0200 Subject: [PATCH 04/11] feat: Implement Newspaper MVC --- .../edu/ntnu/idi/idatt/view/SceneFactory.java | 10 +- .../newspaper/NewspaperController.java | 32 ++--- .../primary/newspaper/NewspaperModel.java | 10 +- .../view/primary/newspaper/NewspaperView.java | 114 +++++++++++------- 4 files changed, 97 insertions(+), 69 deletions(-) diff --git a/src/main/java/edu/ntnu/idi/idatt/view/SceneFactory.java b/src/main/java/edu/ntnu/idi/idatt/view/SceneFactory.java index 165182a..5eb1390 100644 --- a/src/main/java/edu/ntnu/idi/idatt/view/SceneFactory.java +++ b/src/main/java/edu/ntnu/idi/idatt/view/SceneFactory.java @@ -132,13 +132,15 @@ public static Parent createTransactionView() { return view.getInstance(); } - public static Parent createNewspaperView() { - mark(() -> createNewspaperView()); + public static Parent createNewspaperView(String symbol) { + + mark(() -> createNewspaperView(symbol)); + Stock stock = UserSession.getInstance().getExchange().getStock(symbol); NewspaperModel model = new NewspaperModel(); - NewspaperView view = new NewspaperView(); - NewspaperController controller = new NewspaperController(model); + NewspaperView view = new NewspaperView(); + NewspaperController controller = new NewspaperController(model, stock); view.setModel(model); view.setController(controller); diff --git a/src/main/java/edu/ntnu/idi/idatt/view/primary/newspaper/NewspaperController.java b/src/main/java/edu/ntnu/idi/idatt/view/primary/newspaper/NewspaperController.java index 674758a..23904da 100644 --- a/src/main/java/edu/ntnu/idi/idatt/view/primary/newspaper/NewspaperController.java +++ b/src/main/java/edu/ntnu/idi/idatt/view/primary/newspaper/NewspaperController.java @@ -1,24 +1,28 @@ package edu.ntnu.idi.idatt.view.primary.newspaper; -import edu.ntnu.idi.idatt.model.portfolio.Share; -import edu.ntnu.idi.idatt.session.UserSession; +import edu.ntnu.idi.idatt.model.market.Stock; import edu.ntnu.idi.idatt.view.components.AbstractController; -import edu.ntnu.idi.idatt.view.components.elements.NewspaperComponent; +import edu.ntnu.idi.idatt.view.util.CssUtils; +import javafx.scene.control.Label; import java.util.ArrayList; public class NewspaperController extends AbstractController { - private final ArrayList loadedNewspaperComponents = new ArrayList<>(); - private UserSession session = UserSession.getInstance(); - - public NewspaperController(NewspaperModel model) { - super(model); - for (Share share : session.getPlayer().getPortfolio().getShares()) { - NewspaperComponent newspaperComponent = new NewspaperComponent(share); - loadedNewspaperComponents.add(newspaperComponent); - } - model.getNewspaperComponentsList().setAll(loadedNewspaperComponents); - } + private final ArrayList