Skip to content

Commit

Permalink
Feat: Basic improvements, testing.
Browse files Browse the repository at this point in the history
  • Loading branch information
tommyah committed May 25, 2026
1 parent adaec06 commit 19611bd
Show file tree
Hide file tree
Showing 5 changed files with 214 additions and 80 deletions.
119 changes: 76 additions & 43 deletions src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/engine/Exchange.java
Original file line number Diff line number Diff line change
@@ -1,38 +1,34 @@
package edu.ntnu.idi.idatt2003.g40.mappe.engine;

import edu.ntnu.idi.idatt2003.g40.mappe.model.Player;
import edu.ntnu.idi.idatt2003.g40.mappe.model.Purchase;
import edu.ntnu.idi.idatt2003.g40.mappe.model.Sale;
import edu.ntnu.idi.idatt2003.g40.mappe.model.Share;
import edu.ntnu.idi.idatt2003.g40.mappe.model.Stock;
import edu.ntnu.idi.idatt2003.g40.mappe.model.Transaction;
import edu.ntnu.idi.idatt2003.g40.mappe.service.*;
import edu.ntnu.idi.idatt2003.g40.mappe.service.event.EventData;
import edu.ntnu.idi.idatt2003.g40.mappe.service.event.EventManager;
import edu.ntnu.idi.idatt2003.g40.mappe.service.event.EventPublisher;
import edu.ntnu.idi.idatt2003.g40.mappe.service.event.EventType;
import edu.ntnu.idi.idatt2003.g40.mappe.utils.Validator;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;

/**
* Represents a stock exchange where stocks can be traded.
*
* <p>Holds a map of stocks where stock symbol is key and stock is value.</p>
* <p>Holds a map of {@link Stock} objects where stock symbol is key and
* stock is value.</p>
*
* <p>Delegates buying and selling to player elements using calculators</p>
*
* <p>Advances week.</p>
*
* @see Player
* @see TransactionCalculator
*
* @version 1.0.0
* */
public final class Exchange {

Expand All @@ -59,13 +55,16 @@ public final class Exchange {
/**
* Constructor.
*
* @param name name of exchange.
* @param stocks list of {@link Stock} objects.
* @param name name of exchange.
* @param stocks list of {@link Stock} objects.
*
* @throws IllegalArgumentException if name or stocks are empty/null.
* */
public Exchange(final String name, final List<Stock> stocks) throws IllegalArgumentException {
if (!Validator.NOT_EMPTY.isValid(name) || stocks == null || stocks.isEmpty()) {
public Exchange(final String name, final List<Stock> stocks)
throws IllegalArgumentException {
if (!Validator.NOT_EMPTY.isValid(name)
|| stocks == null
|| stocks.isEmpty()) {
throw new IllegalArgumentException("Invalid exchange parameters!");
}
this.name = name;
Expand Down Expand Up @@ -108,7 +107,7 @@ public IntegerProperty weekProperty() {
*
* @param symbol the stock symbol.
*
* @return true or false.
* @return true or false.
* */
public boolean hasStock(final String symbol) {
return stockMap.containsKey(symbol);
Expand All @@ -117,16 +116,16 @@ public boolean hasStock(final String symbol) {
/**
* Getter method for stock element.
*
* @param symbol the symbol of the stock to get.
* @param symbol the symbol of the stock to get.
*
* @return {@link Stock} element gotten.
* @return {@link Stock} element gotten.
*
* @throws IllegalArgumentException if symbol is invalid.
* */
public Stock getStock(final String symbol) throws IllegalArgumentException {
if (!Validator.VALID_STOCK_NAME.isValid(symbol)) {
if (!Validator.VALID_STOCK_SYMBOL.isValid(symbol)) {
throw new IllegalArgumentException(
Validator.VALID_STOCK_NAME.getErrorMessage());
Validator.VALID_STOCK_SYMBOL.getErrorMessage());
}
return stockMap.get(symbol);
}
Expand All @@ -136,7 +135,7 @@ public Stock getStock(final String symbol) throws IllegalArgumentException {
*
* @param searchTerm the term to search for.
*
* @return a list of {@link Stock} objects.
* @return a list of {@link Stock} objects.
* */
public List<Stock> findStocks(final String searchTerm) {
List<Stock> result = new ArrayList<>();
Expand All @@ -154,35 +153,37 @@ public List<Stock> findStocks(final String searchTerm) {
/**
* Method called when a player buys a stock.
*
* @param symbol the stock this player buys.
* @param quantity the amount of stock to buy.
* @param player the player buying stock.
* @param symbol the stock this player buys.
* @param quantity the amount of stock to buy.
* @param player the player buying stock.
*
* @return Transaction representing the transaction.
* @return Transaction representing the transaction.
*
* @throws IllegalArgumentException if symbol or player is invalid.
* */
public Transaction buy(final String symbol,
final BigDecimal quantity,
final Player player) throws IllegalArgumentException {
if (!Validator.VALID_STOCK_NAME.isValid(symbol) || player == null) {
if (!Validator.VALID_STOCK_SYMBOL.isValid(symbol) || player == null) {
throw new IllegalArgumentException("Invalid purchase!");
}
Stock stock = stockMap.get(symbol);
Share share = new Share(stock, quantity, stock.getSalesPrice());
TransactionCalculator calculator = new PurchaseCalculator(share);
Purchase purchase = new Purchase(share, week.get(), calculator);
Transaction purchase = TransactionFactory.createTransaction(
TransactionType.PURCHASE, share, getWeek(), calculator
);
player.handleTransaction(purchase);
return purchase;
}

/**
* Method called when a player sells share.
*
* @param share the share to sell.
* @param player the player buying stock.
* @param share the share to sell.
* @param player the player buying stock.
*
* @return Transaction representing the transaction.
* @return Transaction representing the transaction.
*
* @throws IllegalArgumentException if share or player is null.
* */
Expand All @@ -192,35 +193,46 @@ public Transaction sell(final Share share, final Player player)
throw new IllegalArgumentException("Invalid sell!");
}
TransactionCalculator calculator = new SaleCalculator(share);
Sale sale = new Sale(share, week.get(), calculator);
Transaction sale = TransactionFactory.createTransaction(
TransactionType.SALE, share, getWeek(), calculator
);
player.handleTransaction(sale);
return sale;
}

/**
* Method called when a player sells share.
* Method called when a player sells share,
* defined by an amount instead of specific {@link Share} object.
*
* <p>Might split shares into multiples to ensure proper selling.
* Uses the {@link edu.ntnu.idi.idatt2003.g40.mappe.model.Portfolio}
* to split.</p>
*
* @param amount the amount of "shares" to sell.
* @param amount the amount of "shares" to sell.
* @param stockSymbol the stock to sell shares in.
* @param player the player buying stock.
* @param player the player buying stock.
*
* @return Transaction representing the transaction.
* @return List of transactions commited to sell the shares.
*
* @throws IllegalArgumentException if any parameter is null, or if player does not have enough shares.
* @throws IllegalArgumentException if any parameter is null,
* or if player does not have enough shares.
* */
public List<Transaction> sell(BigDecimal amount,
final String stockSymbol,
final Player player)
throws IllegalArgumentException {
if (amount == null || player == null || !Validator.NOT_EMPTY.isValid(stockSymbol)) {
if (amount == null
|| player == null
|| !Validator.VALID_STOCK_SYMBOL.isValid(stockSymbol)) {
throw new IllegalArgumentException("Invalid sell!");
} else {

List<Share> sharesOfStock = player.getPortfolio().getShares().stream()
.filter(s -> s.getStock().getSymbol().equals(stockSymbol))
.toList();

BigDecimal totalOwned = player.getPortfolio().getTotalSharesBySymbol(stockSymbol);
BigDecimal totalOwned = player.getPortfolio()
.getTotalSharesBySymbol(stockSymbol);

if (amount.compareTo(totalOwned) > 0) {
amount = totalOwned;
Expand All @@ -239,7 +251,9 @@ public List<Transaction> sell(BigDecimal amount,
remainingToSell = remainingToSell.subtract(shareQty);
transactions.add(sell(share, player));
} else {
Share newShare = player.getPortfolio().splitShare(share, remainingToSell);
Share newShare = player.getPortfolio().splitShare(
share, remainingToSell
);
remainingToSell = BigDecimal.ZERO;
transactions.add(sell(newShare, player));
}
Expand All @@ -250,12 +264,19 @@ public List<Transaction> sell(BigDecimal amount,

/**
* Method for advancing time, increasing the amount of weeks.
*
* <p>Applies a random price change from -5% to 5% to every stock,
* plus a flat percent determined by their fortune, that can range from
* -10% to +10%.</p>
*
* @see edu.ntnu.idi.idatt2003.g40.mappe.view.widgets.minigames.GameEngineView
* */
public void advance() {
for (Stock stock : stockMap.values()) {
BigDecimal currentPrice = stock.getSalesPrice();

double change = ((random.nextDouble() * 0.10) - 0.05) + stock.getFortune();
double change = (
(random.nextDouble() * 0.10) - 0.05) + stock.getFortune();
stock.setFortune(0);
BigDecimal factor = BigDecimal.valueOf(1 + change);

Expand All @@ -269,11 +290,17 @@ public void advance() {
* Method for getting the stocks with the most
* amount of increase since last week.
*
* @param limit the maximum amount of stocks returned
* @param limit the maximum amount of stocks returned
*
* @return list of {@link Stock} objects.
*
* @throws IllegalArgumentException if limit is invalid (negative or zero).
* */
public List<Stock> getGainers(final int limit) {
public List<Stock> getGainers(final int limit)
throws IllegalArgumentException {
if (limit < 1) {
throw new IllegalArgumentException("Invalid limit for getting gainers!");
}
return stockMap.entrySet().stream()
// We only want the stocks with a positive price change.
.filter(e ->
Expand All @@ -294,11 +321,17 @@ public List<Stock> getGainers(final int limit) {
* Method for getting the stocks with the highest
* loss of price since last week.
*
* @param limit the maximum amount of stocks returned
* @param limit the maximum amount of stocks returned
*
* @return list of {@link Stock} objects.
*
* @throws IllegalArgumentException if limit is invalid (negative or zero).
* */
public List<Stock> getLosers(final int limit) {
public List<Stock> getLosers(final int limit)
throws IllegalArgumentException {
if (limit < 1) {
throw new IllegalArgumentException("Invalid limit for getting losers!");
}
return stockMap.entrySet().stream()
// Only get entries with negative price change.
.filter(e ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,9 @@ public List<Share> getShares() {
* @throws IllegalArgumentException if symbol is invalid.
*/
public List<Share> getShares(final String symbol) throws IllegalArgumentException {
if (!Validator.VALID_STOCK_NAME.isValid(symbol)) {
if (!Validator.VALID_STOCK_SYMBOL.isValid(symbol)) {
throw new IllegalArgumentException(
Validator.VALID_STOCK_NAME.getErrorMessage());
Validator.VALID_STOCK_SYMBOL.getErrorMessage());
}
return shares.stream()
.filter(s -> symbol.equalsIgnoreCase(s.getStock().getSymbol()))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ public final class Stock {
public Stock(final String symbol,
final String company,
final BigDecimal salesPrice) throws IllegalArgumentException {
if (!Validator.VALID_STOCK_NAME.isValid(symbol)) {
if (!Validator.VALID_STOCK_SYMBOL.isValid(symbol)) {
throw new IllegalArgumentException(
Validator.VALID_STOCK_NAME.getErrorMessage());
Validator.VALID_STOCK_SYMBOL.getErrorMessage());
} else {
this.symbol = symbol;
this.company = company;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ protected void initInteractions() {
* @return the total quantity, or 0 if the symbol is invalid.
* */
private int ownedQuantity(final String symbol) {
if (!Validator.VALID_STOCK_NAME.isValid(symbol)) {
if (!Validator.VALID_STOCK_SYMBOL.isValid(symbol)) {
return 0;
}
return player.getPortfolio().getShares(symbol).stream()
Expand Down
Loading

0 comments on commit 19611bd

Please sign in to comment.