141 lines
No EOL
4.9 KiB
C#
141 lines
No EOL
4.9 KiB
C#
namespace BucketChain;
|
|
|
|
/// <summary>
|
|
/// Represents a bucket chain which connects a well with a fire by cooperating people with buckets.
|
|
/// </summary>
|
|
public sealed class Chain
|
|
{
|
|
private readonly Fire _fire;
|
|
private readonly int _requiredPeople;
|
|
private readonly Well _well;
|
|
private readonly double _bucketSize;
|
|
private Person _firstPerson;
|
|
private int _availableBuckets;
|
|
|
|
/// <summary>
|
|
/// Creates a new bucket chain based on the supplied parameters.
|
|
/// </summary>
|
|
/// <param name="requiredPeople">Min. amount of people required to reach the fire</param>
|
|
/// <param name="availableBuckets">Max. amount of buckets that can be active in the chain</param>
|
|
/// <param name="bucketSize">Size of the buckets used in the chain - all buckets have the same size</param>
|
|
/// <param name="well">A reference to the well, used as water source</param>
|
|
/// <param name="fire">A reference to the fire this chain attempts to extinguish</param>
|
|
/// <param name="firstPerson">Reference to the first person in the chain</param>
|
|
public Chain(int requiredPeople, int availableBuckets, double bucketSize,
|
|
Well well, Fire fire, Person firstPerson)
|
|
{
|
|
_requiredPeople = requiredPeople;
|
|
_availableBuckets = availableBuckets;
|
|
_bucketSize = bucketSize;
|
|
_well = well;
|
|
_fire = fire;
|
|
_firstPerson = firstPerson;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Operates the bucket chain. This includes multiple things:
|
|
/// 1) Refill the well
|
|
/// 2) Allow the fire to grow
|
|
/// 3) If first person has a bucket, fill it at the well
|
|
/// 4) Move the buckets along the chain of people
|
|
/// 5) If the last person reaches the fire and has a full bucket, use it to fight the fire
|
|
/// People can join the chain at various positions, so the <see cref="Chain"/> always has to
|
|
/// determine the current first person before operating.
|
|
/// </summary>
|
|
/// <param name="currentStep">The current step</param>
|
|
/// <param name="error">Set to true if an error occurred while operating, e.g. invalid state of the chain</param>
|
|
/// <returns>True if the fire could be extinguished in this step; false otherwise</returns>
|
|
public bool Operate(int currentStep, out bool error)
|
|
{
|
|
error = false;
|
|
|
|
// step 1
|
|
_well.Refill();
|
|
|
|
// step 2
|
|
_fire.BurnHigher();
|
|
|
|
// step 3
|
|
Bucket? bucket = null;
|
|
if (GetBucketCount() < _availableBuckets)
|
|
{
|
|
bucket = new Bucket(_bucketSize);
|
|
}
|
|
_firstPerson.UseWell(_well, bucket);
|
|
|
|
// step 4
|
|
var currentPerson = _firstPerson;
|
|
Person? lastPerson = null;
|
|
int i;
|
|
for (i = 0; i < _requiredPeople; i++)
|
|
{
|
|
if (currentPerson == null)
|
|
{
|
|
// chain is shorter than requiredPeople
|
|
break;
|
|
}
|
|
|
|
lastPerson = currentPerson;
|
|
bool movedSuccessfully = currentPerson.MoveBucket(currentStep);
|
|
|
|
if (!movedSuccessfully)
|
|
{
|
|
// check if move failed because the step was already performed
|
|
if (currentPerson.LastStepPerformed != currentStep &&
|
|
(currentPerson.RightNeighbor?.HasBucket ?? false))
|
|
{
|
|
// move failed because of an invalid state (neighbor can't accept the bucket)
|
|
error = true;
|
|
return false;
|
|
}
|
|
// else, move wasn't successful because it was already performed
|
|
// continue without error
|
|
}
|
|
|
|
currentPerson = currentPerson.RightNeighbor;
|
|
}
|
|
|
|
// Step 5
|
|
if (i == _requiredPeople && lastPerson is { HasBucket: true })
|
|
{
|
|
lastPerson.FightFire(_fire);
|
|
}
|
|
|
|
return _fire.Extinguished;
|
|
}
|
|
|
|
private int GetBucketCount()
|
|
{
|
|
int count = 0;
|
|
Person? currentPerson = _firstPerson;
|
|
while (currentPerson != null)
|
|
{
|
|
if (currentPerson.HasBucket)
|
|
{
|
|
count++;
|
|
}
|
|
currentPerson = currentPerson.RightNeighbor;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Represents this <see cref="Chain"/> with the referenced <see cref="Well"/>, <see cref="Fire"/>
|
|
/// and <see cref="Person"/> instances as string, ready to print to the terminal.
|
|
/// </summary>
|
|
/// <returns>String representation of the bucket chain</returns>
|
|
public override string ToString()
|
|
{
|
|
string result = $"{_well} | ";
|
|
Person? currentPerson = _firstPerson;
|
|
for (int i = 0; i < _requiredPeople; i++)
|
|
{
|
|
result += $"{currentPerson?.ToString() ?? "\u2754"} | ";
|
|
currentPerson = currentPerson?.RightNeighbor;
|
|
}
|
|
|
|
result += $"{_fire}";
|
|
return result;
|
|
}
|
|
} |