Skip to content

Advance newspaper #60

Merged
merged 11 commits into from
May 23, 2026
26 changes: 26 additions & 0 deletions delete-storage.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
@echo off
REM delete-storage.bat
REM Deletes the Millions JSON storage for resetting app state
REM The storage file location is chosen by StorageFile.java

SET "STORAGE_FOLDER_NAME=Millions"
SET "STORAGE_FILE=storage.json"

REM Use %APPDATA% if set, otherwise fallback to %USERPROFILE%
IF DEFINED APPDATA (
SET "STORAGE_DIR=%APPDATA%\%STORAGE_FOLDER_NAME%"
) ELSE (
SET "STORAGE_DIR=%USERPROFILE%\%STORAGE_FOLDER_NAME%"
)

SET "STORAGE_PATH=%STORAGE_DIR%\%STORAGE_FILE%"

IF EXIST "%STORAGE_PATH%" (
ECHO Deleting storage at %STORAGE_PATH% ...
DEL /F "%STORAGE_PATH%"
ECHO Storage deleted.
) ELSE (
ECHO Storage not found at %STORAGE_PATH%.
)

PAUSE
42 changes: 42 additions & 0 deletions delete-storage.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#!/usr/bin/env bash
# delete-storage.sh
# Deletes the Millions JSON storage for resetting app state
# The storage file location is chosen by StorageFile.java

set -e

STORAGE_FOLDER_NAME="Millions"
STORAGE_FILE="storage.json"

OS=$(uname | tr '[:upper:]' '[:lower:]')
HOME_DIR="$HOME"

# Determine database folder path based on OS
if [[ "$OS" == *"darwin"* ]]; then
# macOS
STORAGE_DIR="$HOME_DIR/Library/Application Support/$STORAGE_FOLDER_NAME"
elif [[ "$OS" == *"linux"* ]]; then
# Linux / Unix
STORAGE_DIR="$HOME_DIR/.local/share/$STORAGE_FOLDER_NAME"
elif [[ "$OS" == *"mingw"* || "$OS" == *"cygwin"* || "$OS" == *"msys"* ]]; then
# Windows (Git Bash, Cygwin, MSYS)
if [[ -n "$APPDATA" ]]; then
STORAGE_DIR="$APPDATA/$STORAGE_FOLDER_NAME"
else
STORAGE_DIR="$HOME_DIR/$STORAGE_FOLDER_NAME"
fi
else
echo "Unsupported OS: $OS"
exit 1
fi

STORAGE_PATH="$STORAGE_DIR/$STORAGE_FILE"

if [[ -f "$STORAGE_PATH" ]]; then
echo "Deleting storage at $STORAGE_PATH ..."
rm "$STORAGE_PATH"
echo "Storage deleted."
else
echo "Storage not found at $STORAGE_PATH"
fi

8 changes: 3 additions & 5 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.RoundingMode;
import java.util.*;

import edu.ntnu.idi.idatt.model.market.Stock;
Expand Down Expand Up @@ -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;
}

}
61 changes: 61 additions & 0 deletions src/main/java/edu/ntnu/idi/idatt/model/Newspaper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
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<NewspaperEnum> news = new ArrayList<>(List.of(NewspaperEnum.NONE_EVENT));
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 boolean hasNewNews() {
return news.getLast().equals(NewspaperEnum.NONE_EVENT) ? false : true;
}

public ArrayList<NewspaperEnum> getNews() {
return news;
}

public List<String> getNewsStrings() {

ArrayList<String> 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;

}

}
109 changes: 109 additions & 0 deletions src/main/java/edu/ntnu/idi/idatt/model/enums/NewspaperEnum.java
Original file line number Diff line number Diff line change
@@ -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;
}

}
53 changes: 53 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 @@ -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
Expand All @@ -19,6 +23,11 @@ public class Stock {
private final String symbol;
private final String company;
private final ArrayList<BigDecimal> 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.
Expand All @@ -33,6 +42,12 @@ public Stock(String symbol, String company, List<BigDecimal> 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;
}

/**
Expand Down Expand Up @@ -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() {
Expand Down
13 changes: 13 additions & 0 deletions src/main/java/edu/ntnu/idi/idatt/session/UserSession.java
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -41,10 +42,12 @@ public Exchange getExchange() {

public void setExchange(Exchange exchange) {
this.exchange = exchange;
updateGameState(); // Startup hook
}

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

public SimpleObjectProperty<BigDecimal> moneyProperty() {
return moneyProperty;
Expand All @@ -54,9 +57,19 @@ public SimpleObjectProperty<BigDecimal> 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();
}

Expand Down
Loading