Skip to content

Instantly share code, notes, and snippets.

@HolyMonkey
Created August 28, 2019 13:04
Show Gist options
  • Save HolyMonkey/b2b12043c81635ade275e49ff8c5b578 to your computer and use it in GitHub Desktop.
Save HolyMonkey/b2b12043c81635ade275e49ff8c5b578 to your computer and use it in GitHub Desktop.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Threading.Tasks;
namespace DmitryCherstvyi
{
class Program
{
static void Main(string[] args)
{
using (MemoryStream chanel = new MemoryStream())
{
BinaryFormatter serializer = new BinaryFormatter();
var gasStation = new GasStation(33);
var remoteFunctions = new RemoteFunctions(serializer, chanel);
var gasStationClient = new GasStationClientProxy(remoteFunctions);
var serverRouter = new ServerCallRouter(gasStation);
var car = new Car();
var payer = new Player();
gasStationClient.FillCar(car, payer);
chanel.Position = 0;
var message = serializer.Deserialize(chanel);
if (message is Call call)
{
Console.WriteLine(call.Method.Name);
serverRouter.HandleCall(call);
}
}
}
}
class RemoteFunctions
{
private readonly BinaryFormatter _serializer;
private readonly Stream _chanel;
public RemoteFunctions(BinaryFormatter serializer, Stream chanel)
{
_serializer = serializer;
_chanel = chanel;
}
public Call<T1, T2> CallOnServer<T1, T2>(Action<T1, T2> function)
{
return new Call<T1, T2>(function.Method, (call) =>
{
_serializer.Serialize(_chanel, call);
});
}
}
[Serializable]
class Call
{
public readonly MethodInfo Method;
public Call(MethodInfo method)
{
Method = method;
}
}
[Serializable]
class Call<T1, T2> : Call
{
public Tuple<T1, T2> Arguments { get; private set; }
[NonSerialized] private Action<Call<T1, T2>> _onReady;
public Call(MethodInfo method, Action<Call<T1, T2>> onReady) : base(method)
{
_onReady = onReady;
}
public void WithArguments(T1 t1, T2 t2)
{
Arguments = new Tuple<T1, T2>(t1, t2);
_onReady?.Invoke(this);
}
}
interface IGasStation
{
void FillCar(Car car, Player payer);
}
class GasStationClientProxy : IGasStation
{
private RemoteFunctions _remoteFunctions;
public GasStationClientProxy(RemoteFunctions remoteFunctions)
{
_remoteFunctions = remoteFunctions;
}
public void FillCar(Car car, Player payer)
{
_remoteFunctions.CallOnServer<Car, Player>(FillCar).WithArguments(car, payer);
}
}
class ServerCallRouter
{
private object[] _handlers;
public ServerCallRouter(params object[] handlers)
{
_handlers = handlers;
}
public void HandleCall(Call call)
{
call.GetType();
}
}
class GasStation : IGasStation
{
private float _pricePerUnit;
private float _fuel;
public GasStation(float pricePerUnit)
{
_pricePerUnit = pricePerUnit;
}
public void FillCar(Car car, Player payer)
{
if (car == null)
throw new ArgumentNullException(nameof(car));
if (payer == null)
throw new ArgumentNullException(nameof(payer));
if(payer.TryWithdraw(GetFuelingCost(car, payer)) == false)
throw new InvalidOperationException("Can't withdraw money on driver");
if(car.TryAddFuel(GetFuelingAmount(car, payer)) == false)
throw new InvalidOperationException("Can't add full fuel amount");
_fuel -= GetFuelingAmount(car, payer);
}
/// <summary>
/// Просчитываете сколько топливо можно заправить в машину с учётом количества денег у платильщика и остатка топлива на заправке
/// </summary>
/// <returns>Количество топлива</returns>
private float GetFuelingAmount(Car car, Player payer)
{
return GetFuelForPrice(GetPayAmount(payer, GetAvailableFuel(car.FuelLack)));
}
/// <summary>
/// Просчитываете сколько топливо можно заправить в машину с учётом количества денег у платильщика и остатка топлива на заправке
/// </summary>
/// <returns>Стоимость заправки</returns>
private int GetFuelingCost(Car car, Player payer)
{
return GetPayAmount(payer, GetAvailableFuel(car.FuelLack));
}
private float GetAvailableFuel(float targetAmount)
{
return Mathf.Clamp(targetAmount, 0, _fuel);
}
private int GetPayAmount(Player payer, float fuel)
{
return payer.GetClosestAmount(GetPriceForFuel(fuel));
}
private int GetPriceForFuel(float amout)
{
return (int)(amout * _pricePerUnit);
}
private float GetFuelForPrice(float amount)
{
return amount / _pricePerUnit;
}
}
[Serializable]
class Car
{
public float Fuel { get; private set; }
public float MaxFuel { get; private set; }
public float FuelLack => MaxFuel - Fuel;
public bool TryAddFuel(float amount)
{
if (Fuel + amount > MaxFuel)
return false;
Fuel += amount;
return true;
}
}
[Serializable]
class Player
{
public int Money { get; private set; }
public bool TryWithdraw(int amount)
{
if (amount < 0 || amount > Money)
return false;
Money -= amount;
return true;
}
public int GetClosestAmount(int price)
{
return (int)Mathf.Clamp(Money, 0, price);
}
}
static class Mathf
{
public static float Clamp(float value, float min, float max)
{
if (value < min)
return min;
if (value > max)
return max;
return value;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment