initial
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
|
||||
namespace AfterHours.Economy
|
||||
{
|
||||
/// <summary>
|
||||
/// Reine Preis-/Nachfragelogik. Engine-frei und damit testbar.
|
||||
/// Keine UnityEngine-Referenzen in dieser Datei.
|
||||
/// </summary>
|
||||
public static class PricingService
|
||||
{
|
||||
/// <summary>Ab diesem Aufschlag über Empfehlung kauft niemand mehr.</summary>
|
||||
public const float RejectionThreshold = 1.75f;
|
||||
|
||||
/// <summary>
|
||||
/// Kaufwahrscheinlichkeit relativ zum empfohlenen Preis.
|
||||
/// 1.0 bei Empfehlungspreis oder darunter, 0.0 ab RejectionThreshold.
|
||||
/// Dazwischen quadratisch abfallend – kleine Aufschläge tun kaum weh,
|
||||
/// große brechen die Nachfrage schnell ein.
|
||||
/// </summary>
|
||||
public static float PurchaseProbability(float askingPrice, float recommendedPrice)
|
||||
{
|
||||
if (recommendedPrice <= 0f) throw new ArgumentOutOfRangeException(nameof(recommendedPrice));
|
||||
if (askingPrice <= 0f) return 1f;
|
||||
|
||||
float ratio = askingPrice / recommendedPrice;
|
||||
if (ratio <= 1f) return 1f;
|
||||
if (ratio >= RejectionThreshold) return 0f;
|
||||
|
||||
float t = (ratio - 1f) / (RejectionThreshold - 1f); // 0..1
|
||||
return 1f - (t * t);
|
||||
}
|
||||
|
||||
/// <summary>Marge in Prozent des Verkaufspreises.</summary>
|
||||
public static float MarginRatio(float askingPrice, float wholesalePrice)
|
||||
{
|
||||
if (askingPrice <= 0f) return 0f;
|
||||
return (askingPrice - wholesalePrice) / askingPrice;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Erwarteter Deckungsbeitrag pro angebotenem Stück.
|
||||
/// Nützlich für den späteren "Preis-Optimum"-Hinweis im Preis-UI.
|
||||
/// </summary>
|
||||
public static float ExpectedProfitPerUnit(float askingPrice, float recommendedPrice, float wholesalePrice)
|
||||
{
|
||||
return PurchaseProbability(askingPrice, recommendedPrice) * (askingPrice - wholesalePrice);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 488bfdde2f57adf4e8d4ce3a8ec065c0
|
||||
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace AfterHours.Economy
|
||||
{
|
||||
/// <summary>Eine Position einer Großhandelsbestellung.</summary>
|
||||
[Serializable]
|
||||
public struct OrderLine
|
||||
{
|
||||
public int ProductId;
|
||||
public int Quantity;
|
||||
public float UnitPrice;
|
||||
|
||||
public float LineTotal => Quantity * UnitPrice;
|
||||
}
|
||||
|
||||
/// <summary>Bestellung beim Großhandel. Wird nur serverseitig gehalten.</summary>
|
||||
public sealed class PurchaseOrder
|
||||
{
|
||||
private readonly List<OrderLine> _lines = new();
|
||||
|
||||
public IReadOnlyList<OrderLine> Lines => _lines;
|
||||
|
||||
/// <summary>Spielzeit in Sekunden, zu der die Lieferung eintrifft.</summary>
|
||||
public float DeliveryTime { get; set; }
|
||||
|
||||
public bool Delivered { get; set; }
|
||||
|
||||
public void Add(int productId, int quantity, float unitPrice)
|
||||
{
|
||||
if (quantity <= 0) return;
|
||||
_lines.Add(new OrderLine { ProductId = productId, Quantity = quantity, UnitPrice = unitPrice });
|
||||
}
|
||||
|
||||
public float Total()
|
||||
{
|
||||
float sum = 0f;
|
||||
foreach (var line in _lines) sum += line.LineTotal;
|
||||
return sum;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ca74ad78a2bea0e4d985e96612714923
|
||||
Reference in New Issue
Block a user