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; } }