New implementation of merchant, based on stock.

This commit is contained in:
marcins78
2012-12-18 16:15:43 +00:00
parent 6fca13d8f6
commit 648ef3de1c
7 changed files with 270 additions and 7 deletions

View File

@@ -28,7 +28,7 @@ public class CachedMerchantDAO implements MerchantDAO, Cached {
@Override
public void addTransaction(String blueprintId, float price, Date date, TransactionType transactionType) {
_delegate.addTransaction(blueprintId, price, date, transactionType);
_blueprintIdLastTransaction.put(blueprintId, new Transaction(date, price, transactionType));
_blueprintIdLastTransaction.remove(blueprintId);
}
@Override

View File

@@ -81,7 +81,7 @@ public class DbMerchantDAO implements MerchantDAO {
try {
Connection connection = _dbAccess.getDataSource().getConnection();
try {
PreparedStatement statement = connection.prepareStatement("select blueprint_id, transaction_price, transaction_date, transaction_type from merchant_data where blueprint_id=?");
PreparedStatement statement = connection.prepareStatement("select blueprint_id, transaction_price, transaction_date, transaction_type, buy_count-sell_count from merchant_data where blueprint_id=?");
try {
statement.setString(1, blueprintId);
ResultSet rs = statement.executeQuery();
@@ -90,8 +90,9 @@ public class DbMerchantDAO implements MerchantDAO {
float price = rs.getFloat(2);
Date date = rs.getTimestamp(3);
String type = rs.getString(4);
int stock = rs.getInt(5);
return new Transaction(date, price, TransactionType.valueOf(type));
return new Transaction(date, price, TransactionType.valueOf(type), stock);
} else {
return null;
}

View File

@@ -15,11 +15,13 @@ public interface MerchantDAO {
private float _price;
private Date _date;
private TransactionType _transactionType;
private int _stock;
public Transaction(Date date, float price, TransactionType transactionType) {
public Transaction(Date date, float price, TransactionType transactionType, int stock) {
_date = date;
_price = price;
_transactionType = transactionType;
_stock = stock;
}
public Date getDate() {
@@ -33,5 +35,9 @@ public interface MerchantDAO {
public TransactionType getTransactionType() {
return _transactionType;
}
public int getStock() {
return _stock;
}
}
}

View File

@@ -11,7 +11,7 @@ import java.util.Map;
public class ParametrizedMerchant implements Merchant {
private static final int BOOSTER_PRICE = 1000;
private static final long DAY = BOOSTER_PRICE * 60 * 60 * 24;
private static final long DAY = 1000 * 60 * 60 * 24;
private Date _merchantSetupDate;
@@ -82,7 +82,7 @@ public class ParametrizedMerchant implements Merchant {
Integer basePrice = getBasePrice(blueprintId);
if (basePrice == null)
return null;
lastTrans = new MerchantDAO.Transaction(_merchantSetupDate, basePrice, MerchantDAO.TransactionType.SELL);
lastTrans = new MerchantDAO.Transaction(_merchantSetupDate, basePrice, MerchantDAO.TransactionType.SELL, 0);
}
if (lastTrans.getDate().getTime() + _priceRevertTimeMs > currentTime.getTime()) {
if (lastTrans.getTransactionType() == MerchantDAO.TransactionType.SELL) {

View File

@@ -0,0 +1,135 @@
package com.gempukku.lotro.merchant;
import com.gempukku.lotro.cards.packs.RarityReader;
import com.gempukku.lotro.cards.packs.SetRarity;
import com.gempukku.lotro.db.MerchantDAO;
import com.gempukku.lotro.game.LotroCardBlueprintLibrary;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class StorageBasedMerchant implements Merchant {
private static final int BOOSTER_PRICE = 1000;
private static final long DAY = 1000 * 60 * 60 * 24;
private static final float _profitMargin = 0.7f;
private static final int DOUBLE_AFTER_DAYS = 60;
private static final int MAX_STOCK_BUY = 100;
private static final int FOIL_PRICE_MULTIPLIER = 2;
private static final int MIN_SELL_PRICE = 2;
private static final int MIN_BUY_PRICE = 1;
private static final double PRICE_SHIFT_AFTER_TRANSACTION = 1.05;
private LotroCardBlueprintLibrary _library;
private MerchantDAO _merchantDao;
private Map<Integer, SetRarity> _rarity = new HashMap<Integer, SetRarity>();
private Date _merchantSetupDate;
public StorageBasedMerchant(LotroCardBlueprintLibrary library, MerchantDAO merchantDao, Date merchantSetupDate) {
_library = library;
_merchantDao = merchantDao;
_merchantSetupDate = merchantSetupDate;
RarityReader rarityReader = new RarityReader();
for (int i = 0; i <= 19; i++)
_rarity.put(i, rarityReader.getSetRarity(String.valueOf(i)));
}
@Override
public void cardBought(String blueprintId, Date currentTime, int price) {
if (blueprintId.endsWith("*"))
price = price / FOIL_PRICE_MULTIPLIER;
blueprintId = _library.getBaseBlueprintId(blueprintId);
_merchantDao.addTransaction(blueprintId, (price / _profitMargin), currentTime, MerchantDAO.TransactionType.BUY);
}
@Override
public void cardSold(String blueprintId, Date currentTime, int price) {
_merchantDao.addTransaction(blueprintId, price, currentTime, MerchantDAO.TransactionType.SELL);
}
@Override
public Integer getCardSellPrice(String blueprintId, Date currentTime) {
blueprintId = _library.getBaseBlueprintId(blueprintId);
MerchantDAO.Transaction lastTransaction = _merchantDao.getLastTransaction(blueprintId);
if (lastTransaction == null || lastTransaction.getStock() < 0)
return null;
Double normalPrice = getNormalPrice(lastTransaction, blueprintId, currentTime);
if (normalPrice == null)
return null;
return Math.max(MIN_SELL_PRICE, (int) Math.ceil(normalPrice));
}
@Override
public Integer getCardBuyPrice(String blueprintId, Date currentTime) {
boolean foil = false;
if (blueprintId.endsWith("*"))
foil = true;
blueprintId = _library.getBaseBlueprintId(blueprintId);
MerchantDAO.Transaction lastTransaction = _merchantDao.getLastTransaction(blueprintId);
if (lastTransaction != null && lastTransaction.getStock() >= MAX_STOCK_BUY && !foil)
return null;
Double normalPrice = getNormalPrice(lastTransaction, blueprintId, currentTime);
if (normalPrice == null)
return null;
int price = Math.max(MIN_BUY_PRICE, (int) Math.floor(_profitMargin * normalPrice));
if (foil)
return FOIL_PRICE_MULTIPLIER * price;
return price;
}
public Double getNormalPrice(MerchantDAO.Transaction lastTransaction, String blueprintId, Date currentTime) {
float basePrice;
float daysSinceLastTransaction;
int stock;
if (lastTransaction == null) {
Integer priceBasedOnRarity = getBasePrice(blueprintId);
if (priceBasedOnRarity == null)
return null;
basePrice = priceBasedOnRarity.floatValue();
daysSinceLastTransaction = (currentTime.getTime() - _merchantSetupDate.getTime()) / (DAY * 1f);
stock = 0;
} else {
basePrice = lastTransaction.getPrice();
if (lastTransaction.getTransactionType() == MerchantDAO.TransactionType.BUY)
basePrice/= PRICE_SHIFT_AFTER_TRANSACTION;
else
basePrice*= PRICE_SHIFT_AFTER_TRANSACTION;
daysSinceLastTransaction = (currentTime.getTime() - lastTransaction.getDate().getTime()) / (DAY * 1f);
stock = lastTransaction.getStock();
}
if (stock > 0)
return basePrice / (1+ Math.pow(daysSinceLastTransaction/DOUBLE_AFTER_DAYS, 1.5));
else
return basePrice * (1+ Math.pow(daysSinceLastTransaction/DOUBLE_AFTER_DAYS, 1.5));
}
private Integer getBasePrice(String blueprintId) {
int underscoreIndex = blueprintId.indexOf("_");
if (underscoreIndex == -1)
return null;
SetRarity rarity = _rarity.get(Integer.parseInt(blueprintId.substring(0, blueprintId.indexOf("_"))));
String cardRarity = rarity.getCardRarity(blueprintId);
if (cardRarity.equals("X"))
return 3 * BOOSTER_PRICE;
if (cardRarity.equals("R") || cardRarity.equals("P"))
return BOOSTER_PRICE;
if (cardRarity.equals("U") || cardRarity.equals("S"))
return BOOSTER_PRICE / 3;
if (cardRarity.equals("C"))
return BOOSTER_PRICE / 7;
throw new RuntimeException("Unknown rarity for priced card: " + cardRarity);
}
}
;

View File

@@ -10,18 +10,23 @@ public class MockMerchantDAO implements MerchantDAO {
private Map<String, Float> _prices = new HashMap<String, Float>();
private Map<String, Date> _dates = new HashMap<String, Date>();
private Map<String, TransactionType> _transactionTypes = new HashMap<String, TransactionType>();
private Map<String, Integer> _stock = new HashMap<String, Integer>();
@Override
public void addTransaction(String blueprintId, float price, Date date, TransactionType transactionType) {
_prices.put(blueprintId, price);
_dates.put(blueprintId, date);
_transactionTypes.put(blueprintId, transactionType);
Integer previousStock = _stock.get(blueprintId);
if (previousStock == null)
previousStock = 0;
_stock.put(blueprintId, previousStock + ((transactionType == TransactionType.SELL) ? -1 : 1));
}
@Override
public Transaction getLastTransaction(String blueprintId) {
if (_dates.containsKey(blueprintId))
return new Transaction(_dates.get(blueprintId), _prices.get(blueprintId), _transactionTypes.get(blueprintId));
return new Transaction(_dates.get(blueprintId), _prices.get(blueprintId), _transactionTypes.get(blueprintId), _stock.get(blueprintId));
return null;
}
}

View File

@@ -0,0 +1,116 @@
package com.gempukku.lotro.merchant;
import com.gempukku.lotro.db.MerchantDAO;
import com.gempukku.lotro.game.LotroCardBlueprintLibrary;
import org.junit.Before;
import org.junit.Test;
import java.util.Date;
import static org.junit.Assert.*;
public class StorageBasedMerchantTest {
private StorageBasedMerchant _merchant;
private static final long DAY = 1000 * 60 * 60 * 24;
@Before
public void setup() {
Date setupDate = new Date(0);
MerchantDAO merchantDao = new MockMerchantDAO();
_merchant = new StorageBasedMerchant(new LotroCardBlueprintLibrary(), merchantDao, setupDate);
}
@Test
public void cardTradingWithNoStock() {
assertNull(_merchant.getCardSellPrice("1_1", new Date(0)));
assertNotNull(_merchant.getCardBuyPrice("1_1", new Date(0)));
}
@Test
public void cardTradingWithSomeStock() {
_merchant.cardBought("1_1", new Date(0), 1000);
assertNotNull(_merchant.getCardSellPrice("1_1", new Date(0)));
assertNotNull(_merchant.getCardBuyPrice("1_1", new Date(0)));
}
@Test
public void cardTradingWithFullStock() {
for (int i=0; i<100; i++)
_merchant.cardBought("1_1", new Date(0), 1000);
assertNotNull(_merchant.getCardSellPrice("1_1", new Date(0)));
assertNull(_merchant.getCardBuyPrice("1_1", new Date(0)));
}
@Test
public void cardPriceLowersAfterMerchantBuys() {
_merchant.cardBought("1_1", new Date(0), 700);
int cardInitialPrice = _merchant.getCardSellPrice("1_1", new Date(0));
assertEqualsMoreOrLess((int) (1000 / 1.05f), cardInitialPrice);
}
@Test
public void cardPriceRisesAfterMerchantSells() {
// Setup some stock
_merchant.cardBought("1_1", new Date(0), 700);
_merchant.cardBought("1_1", new Date(0), 700);
_merchant.cardSold("1_1", new Date(0), 1000);
int cardInitialPrice = _merchant.getCardSellPrice("1_1", new Date(0));
assertEqualsMoreOrLess((int) (1000*1.05f), cardInitialPrice);
}
@Test
public void cardPriceRisesOverTimeWhenNoStock() {
int initialPrice = _merchant.getCardBuyPrice("1_1", new Date(0));
assertTrue(initialPrice < _merchant.getCardBuyPrice("1_1", new Date(DAY)));
}
@Test
public void cardPriceLowersOverTimeWhenInStock() {
_merchant.cardBought("1_1", new Date(0), 700);
int initialPrice = _merchant.getCardBuyPrice("1_1", new Date(0));
assertTrue(initialPrice > _merchant.getCardBuyPrice("1_1", new Date(DAY)));
}
// @Test
public void plotPricesAfterTransactions() {
Date setupDate = new Date(-1000 * 60 * 60 * 24 * 50L);
Date firstTrans = new Date(0);
MerchantDAO merchantDao = new MockMerchantDAO();
StorageBasedMerchant merchant = new StorageBasedMerchant(new LotroCardBlueprintLibrary(), merchantDao, setupDate);
merchant.cardSold("1_1", firstTrans, 1000);
merchant.cardBought("1_2", firstTrans, 700);
long hour = 1000 * 60 * 60;
System.out.println("-2,1000,700,1000,700");
for (long time = 0; time < hour * 24 * 35L; time += hour * 2) {
System.out.println(time / hour + "," + merchant.getCardSellPrice("1_1", new Date(time)) + "," + merchant.getCardBuyPrice("1_1", new Date(time))
+ "," + merchant.getCardSellPrice("1_2", new Date(time)) + "," + merchant.getCardBuyPrice("1_2", new Date(time)));
}
}
// @Test
public void plotPriceEasingFromSetup() {
Date setupDate = new Date(0);
MerchantDAO merchantDao = new MockMerchantDAO();
StorageBasedMerchant merchant = new StorageBasedMerchant(new LotroCardBlueprintLibrary(), merchantDao, setupDate);
long hour = 1000 * 60 * 60;
for (long time = 0; time < hour * 24 * 35L; time += hour * 2)
System.out.println(time / hour + "," + merchant.getCardSellPrice("1_1", new Date(time)) + "," + merchant.getCardBuyPrice("1_1", new Date(time)));
}
private void assertEqualsMoreOrLess(int expected, int given) {
assertTrue("Expected " + expected + " more or less, but received " + given, given <= expected + 1 && given >= expected - 1);
}
}