namespace BucketChain;
///
/// Represents a bucket chain which connects a well with a fire by cooperating people with buckets.
///
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;
///
/// Creates a new bucket chain based on the supplied parameters.
///
/// Min. amount of people required to reach the fire
/// Max. amount of buckets that can be active in the chain
/// Size of the buckets used in the chain - all buckets have the same size
/// A reference to the well, used as water source
/// A reference to the fire this chain attempts to extinguish
/// Reference to the first person in the chain
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;
}
///
/// 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 always has to
/// determine the current first person before operating.
///
/// The current step
/// Set to true if an error occurred while operating, e.g. invalid state of the chain
/// True if the fire could be extinguished in this step; false otherwise
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;
}
///
/// Represents this with the referenced ,
/// and instances as string, ready to print to the terminal.
///
/// String representation of the bucket chain
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;
}
}