Skip to content

Game state #53

Merged
merged 10 commits into from
May 14, 2026
7 changes: 1 addition & 6 deletions src/main/java/edu/ntnu/idi/idatt/model/Exchange.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package edu.ntnu.idi.idatt.model;

import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.util.*;

Expand All @@ -11,7 +10,6 @@
import edu.ntnu.idi.idatt.model.transaction.Purchase;
import edu.ntnu.idi.idatt.model.transaction.Sale;
import edu.ntnu.idi.idatt.model.transaction.Transaction;
import edu.ntnu.idi.idatt.session.UserSession;

/**
* Exchange class
Expand Down Expand Up @@ -201,10 +199,7 @@ public void advance() {
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))));
UserSession.getInstance().netWorthProperty().set(
UserSession.getInstance().getPlayer().getNetWorth().doubleValue()); // TODO: Remove hook from here, just for
// testing
.multiply(BigDecimal.valueOf(random.nextDouble(0.8, 1.4))).setScale(2, RoundingMode.HALF_UP)));
}

}
6 changes: 6 additions & 0 deletions src/main/java/edu/ntnu/idi/idatt/model/market/Stock.java
Original file line number Diff line number Diff line change
Expand Up @@ -119,4 +119,10 @@ public void addNewSalesPrice(BigDecimal price) {
prices.add(price);
}

// TODO: JavaDocs
@Override
public String toString() {
return this.getCompany() + " (" + this.getSymbol() + ")";
}

}
13 changes: 2 additions & 11 deletions src/main/java/edu/ntnu/idi/idatt/model/player/Player.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

import edu.ntnu.idi.idatt.model.portfolio.Portfolio;
import edu.ntnu.idi.idatt.model.transaction.TransactionArchive;
import edu.ntnu.idi.idatt.session.UserSession;

import java.math.BigDecimal;
import java.math.RoundingMode;

public class Player {

Expand Down Expand Up @@ -50,19 +50,10 @@ public TransactionArchive getTransactionArchive() {

public void addMoney(BigDecimal amount) {
this.money = this.money.add(amount);
UserSession.getInstance().moneyProperty().set(this.money.doubleValue()); // Workaround instead of PropertyChange due
// to JSON saving
UserSession.getInstance().netWorthProperty().set(this.getNetWorth().doubleValue()); // Portfolio doesnt change
// without monetary change
// (either buy or sale) so thats
// the decision to hook net
// worth here and under.
}

public void withdrawMoney(BigDecimal amount) {
this.money = this.money.subtract(amount);
UserSession.getInstance().moneyProperty().set(this.money.doubleValue()); // Hooks
UserSession.getInstance().netWorthProperty().set(this.getNetWorth().doubleValue());
}

/**
Expand All @@ -86,7 +77,7 @@ public BigDecimal getNetWorth() {
*/
public String getStatus() {
int tradingWeeks = transactionArchive.countDistinctWeeks();
BigDecimal netWorth = this.getNetWorth().divide(this.startingMoney);
BigDecimal netWorth = this.getNetWorth().divide(this.startingMoney, 2, RoundingMode.HALF_UP);

if (tradingWeeks >= 20 && netWorth.compareTo(new BigDecimal("2")) >= 0) {
return "Speculator";
Expand Down
17 changes: 13 additions & 4 deletions src/main/java/edu/ntnu/idi/idatt/model/portfolio/Portfolio.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ public boolean removeShare(Share share) {
return shares.remove(share);
}

// TODO: JavaDocs, Junit
public void removeShares() {
shares.clear();
}

/**
* Getter for ArrayList shares.
*
Expand All @@ -69,19 +74,23 @@ public BigDecimal getOwnedAmount(String symbol) {
}

public BigDecimal getProfitFromStock(String symbol) {
return getShares(symbol).stream().map(s -> s.getProfit())
.reduce(BigDecimal.ZERO, BigDecimal::add);
return getShares(symbol).stream().map(s -> {
BigDecimal total = new SaleCalculator(s).calculateTotal();
BigDecimal buyPrice = s.getQuantity().multiply(s.getPurchasePrice());

return total.subtract(buyPrice);
}).reduce(BigDecimal.ZERO, BigDecimal::add);
}

public BigDecimal getChangeFromStock(String symbol) {
BigDecimal profitTotal = getProfitFromStock(symbol);
BigDecimal costTotal = getShares(symbol).stream().map(s -> s.getPurchasePrice())
BigDecimal costTotal = getShares(symbol).stream().map(s -> s.getPurchasePrice().multiply(s.getQuantity()))
.reduce(BigDecimal.ZERO, BigDecimal::add);

if (costTotal.compareTo(BigDecimal.ZERO) <= 0)
return BigDecimal.ZERO;

return profitTotal.divide(costTotal, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100));
return profitTotal.divide(costTotal, 2, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100));
}

/**
Expand Down
21 changes: 15 additions & 6 deletions src/main/java/edu/ntnu/idi/idatt/session/UserSession.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package edu.ntnu.idi.idatt.session;

import java.math.BigDecimal;

import edu.ntnu.idi.idatt.model.Exchange;
import edu.ntnu.idi.idatt.model.player.Player;
import javafx.beans.property.SimpleDoubleProperty;
import edu.ntnu.idi.idatt.storage.SessionManager;
import javafx.beans.property.SimpleObjectProperty;

public class UserSession {

Expand All @@ -29,7 +32,7 @@ public Player getPlayer() {

public void setPlayer(Player player) {
this.player = player;
moneyProperty.set(player.getMoney().doubleValue()); // Startup hook
updateGameState(); // Startup hook
}

public Exchange getExchange() {
Expand All @@ -40,17 +43,23 @@ public void setExchange(Exchange exchange) {
this.exchange = exchange;
}

private final SimpleDoubleProperty moneyProperty = new SimpleDoubleProperty();
private final SimpleDoubleProperty netWorthProperty = new SimpleDoubleProperty();
private final SimpleObjectProperty<BigDecimal> moneyProperty = new SimpleObjectProperty<>(BigDecimal.ZERO);
private final SimpleObjectProperty<BigDecimal> netWorthProperty = new SimpleObjectProperty<>(BigDecimal.ZERO);

public SimpleDoubleProperty moneyProperty() {
public SimpleObjectProperty<BigDecimal> moneyProperty() {
return moneyProperty;
}

public SimpleDoubleProperty netWorthProperty() {
public SimpleObjectProperty<BigDecimal> netWorthProperty() {
return netWorthProperty;
}

public void updateGameState() {
moneyProperty.set(player.getMoney());
netWorthProperty.set(player.getNetWorth());
SessionManager.saveSession();
}

public SessionBundle getSession() {
return new SessionBundle(player, exchange);
}
Expand Down
18 changes: 18 additions & 0 deletions src/main/java/edu/ntnu/idi/idatt/storage/SessionManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@
import com.google.gson.reflect.TypeToken;

import edu.ntnu.idi.idatt.model.Exchange;
import edu.ntnu.idi.idatt.model.market.Stock;
import edu.ntnu.idi.idatt.model.player.Player;
import edu.ntnu.idi.idatt.model.portfolio.Portfolio;
import edu.ntnu.idi.idatt.model.portfolio.Share;
import edu.ntnu.idi.idatt.model.transaction.Transaction;
import edu.ntnu.idi.idatt.service.transaction.TransactionCalculator;
import edu.ntnu.idi.idatt.session.UserSession;
Expand Down Expand Up @@ -104,6 +107,21 @@ public static boolean loadSession(String playerName) {
if (session.getPlayer().getName().equals(playerName)) {
UserSession.getInstance().setPlayer(session.getPlayer());
UserSession.getInstance().setExchange(session.getExchange());

// Reseed portfolio based on exchange's stock instances.
// This has to be done due to Gson loading via reflection.
Portfolio portfolio = UserSession.getInstance().getPlayer().getPortfolio();
ArrayList<Share> validatedShares = new ArrayList<>();

for (Share oldShare : portfolio.getShares()) { // Create new instances
Stock stock = UserSession.getInstance().getExchange().getStock(oldShare.getStock().getSymbol());
Share newShare = new Share(stock, oldShare.getQuantity(), oldShare.getPurchasePrice());
validatedShares.add(newShare);
}

portfolio.removeShares(); // Remove all old
validatedShares.forEach(share -> portfolio.addShare(share)); // Add new

return true;
}
}
Expand Down
17 changes: 9 additions & 8 deletions src/main/java/edu/ntnu/idi/idatt/view/SceneFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,17 @@
import edu.ntnu.idi.idatt.view.primary.transactions.TransactionView;
import javafx.scene.Parent;

import java.math.BigDecimal;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.List;

public class SceneFactory {

@FunctionalInterface
public interface MVCInitializer {
public interface MVCInitInterface {
Parent execute();
}

private static Deque<MVCInitializer> navigation = new ArrayDeque<>();
private static Deque<MVCInitInterface> navigation = new ArrayDeque<>();
private static boolean navigatingBack = false;

public static void goBack() {
Expand All @@ -50,7 +48,7 @@ public static void reloadCurrent() {
navigatingBack = false;
}

private static void mark(MVCInitializer initializer) {
private static void mark(MVCInitInterface initializer) {
if (!navigatingBack) {
navigation.push(initializer);
}
Expand All @@ -62,6 +60,8 @@ public static boolean isFinal() {

public static Parent createStartView() {

navigation.clear();

StartModel model = new StartModel();
StartView view = new StartView();
StartController controller = new StartController(model);
Expand All @@ -72,13 +72,14 @@ public static Parent createStartView() {
return view.getInstance();

}
public static Parent createPortfolioView(){

public static Parent createPortfolioView() {

mark(() -> createPortfolioView());

PortfolioModel model = new PortfolioModel();
PortfolioView view = new PortfolioView();
PortfolioController controller = new PortfolioController(model);
PortfolioController controller = new PortfolioController(model);

view.setModel(model);
view.setController(controller);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,42 +8,42 @@
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;

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

public class PlayerPortfolioComponent extends HBox {

public PlayerPortfolioComponent(Portfolio portfolio){
this.setMaxSize(Double.MAX_VALUE, 500);
this.getStyleClass().add("light");
public PlayerPortfolioComponent(Portfolio portfolio) {
this.setMaxSize(Double.MAX_VALUE, 500);
this.getStyleClass().add("light");

Label userTitle = new Label(String.format("%s's Portfolio", UserSession.getInstance().getPlayer().getName()));
Label netWorth = new Label();
netWorth.textProperty().bind(
UserSession.getInstance().netWorthProperty().asString("Net Worth: %.2f $"));
Label percentageChange = new Label("Percentage change: "+ UserSession.getInstance().netWorthProperty().doubleValue());
Label playerStatus = new Label("Player status: "+ UserSession.getInstance().getPlayer().getStatus());
Label totalShares = new Label("Total shares owned: "+ UserSession.getInstance().getPlayer().getPortfolio().getShares().size());
ArrayList<Label> labels = new ArrayList<>();
Collections.addAll(labels, netWorth, percentageChange, playerStatus, totalShares);
labels.forEach(e -> e.getStyleClass().add("portfolio-box-text"));
userTitle.getStyleClass().add("portfolio-box-title");
Label userTitle = new Label(String.format("%s's Portfolio", UserSession.getInstance().getPlayer().getName()));
Label netWorth = new Label();
netWorth.textProperty().bind(
UserSession.getInstance().netWorthProperty().asString("Net Worth: %.2f $"));
Label percentageChange = new Label(
"Percentage change: " + UserSession.getInstance().netWorthProperty().get());
Label playerStatus = new Label("Player status: " + UserSession.getInstance().getPlayer().getStatus());
Label totalShares = new Label(
"Total shares owned: " + UserSession.getInstance().getPlayer().getPortfolio().getShares().size());
ArrayList<Label> labels = new ArrayList<>();
Collections.addAll(labels, netWorth, percentageChange, playerStatus, totalShares);
labels.forEach(e -> e.getStyleClass().add("portfolio-box-text"));
userTitle.getStyleClass().add("portfolio-box-title");

UICompositor playerPortfolioComponent = new UICompositor.Builder()
.parent(new HBox())
.growWithAlignment(Pos.CENTER)
.wrap(new VBox())
.addAllContent(userTitle, netWorth, percentageChange)
.unwrap()
.filler()
.wrap(new VBox())
.growWithAlignment(Pos.BOTTOM_CENTER)
.addAllContent(playerStatus, totalShares)
.unwrap()
.build();
UICompositor playerPortfolioComponent = new UICompositor.Builder()
.parent(new HBox())
.growWithAlignment(Pos.CENTER)
.wrap(new VBox())
.addAllContent(userTitle, netWorth, percentageChange)
.unwrap()
.filler()
.wrap(new VBox())
.growWithAlignment(Pos.BOTTOM_CENTER)
.addAllContent(playerStatus, totalShares)
.unwrap()
.build();

this.getChildren().add(playerPortfolioComponent.makeUI());
}
}
this.getChildren().add(playerPortfolioComponent.makeUI());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public StockComponent(Stock stock) {

this.setStyle("-fx-background-color: #404950;");

Label title = new Label(String.format("%s (%s)", stock.getCompany(), stock.getSymbol()));
Label title = new Label(stock.toString());

Label latestText = new Label("Latest");
Label latestValue = new Label(String.format("%.2f USD", stock.getSalesPrice()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,23 @@ public class TransactionComponent extends VBox {
public TransactionComponent(Transaction transaction) {

Stock stock = transaction.getShare().getStock();
TransactionCalculator calculator = transaction.getCalculator();

Label title;
Label name = new Label(stock.getCompany() + " (" + stock.getSymbol() + ")");
Label name = new Label(stock.toString());
Label totalValue;
Label taxComissionValue;
Label grossValue;
Label amountOfShares;
Label saleProfit;

TransactionCalculator calculator = transaction.getCalculator();
totalValue = new Label(String.format("Total: %.2f USD", calculator.calculateTotal()));

taxComissionValue = new Label(String.format(
"Tax & Comission: %.2f USD", calculator.calculateTax().add(calculator.calculateCommision())));

grossValue = new Label(String.format("Gross: %.2f USD", calculator.calculateGross()));

amountOfShares = new Label(
String.format("Shares: %.2f [%s]", transaction.getShare().getQuantity(), stock.getSymbol()));

Expand Down
26 changes: 26 additions & 0 deletions src/main/java/edu/ntnu/idi/idatt/view/components/ui/UIAlert.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package edu.ntnu.idi.idatt.view.components.ui;

import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.ButtonType;

public class UIAlert {

Alert alert = new Alert(AlertType.CONFIRMATION);
ButtonType confirm = new ButtonType("Confirm");
ButtonType cancel = new ButtonType("Cancel");

public UIAlert(String title, String header, String content) {
alert.setTitle(title);
alert.setHeaderText(header);
alert.setContentText(content);

alert.getButtonTypes().setAll(confirm, cancel);
}

public boolean displayAwaitResponse() {
ButtonType type = alert.showAndWait().orElse(cancel);
return type == confirm ? true : false;
}

}
Loading
Loading