namespace BucketChain; /// /// Represents a person working together with others in a bucket chain to extinguish a fire. /// public sealed class Person { private Bucket? _backwardBucket; private Bucket? _forwardBucket; /// /// Creates a new person. /// We are not interested in any specific details of the person, /// only the position in the chain is relevant. /// public Person() { // TODO } /// /// Gets if the person currently has at least one bucket (forward or backward). /// public bool HasBucket => _forwardBucket != null || _backwardBucket != null; /// /// Gets the left neighbor of the person. Can be null. /// public Person? LeftNeighbor { get; private set; } /// /// Gets the right neighbor of the person. Can be null. /// public Person? RightNeighbor { get; private set; } /// /// Gets the number of the last step in which this person performed a bucket move action. /// Each person can only move, at most, once per step. /// public int LastStepPerformed { get; private set; } /// /// The person joins an existing bucket chain at a specific position. /// This position is defined by the passed person and the information if this person will join /// the chain on the left or right side of the passed person. /// If the passed person already had a neighbor at this side this person joins between them. /// /// The neighbor in the bucket chain /// If true the passed person will become the left hand side /// neighbor of this person; otherwise it will become the right hand side neighbor public void JoinChain(Person neighbor, bool isLeftNeighbor) { var neighborOfNeighbor = isLeftNeighbor ? neighbor.RightNeighbor : neighbor.LeftNeighbor; if (neighborOfNeighbor != null) { if(isLeftNeighbor) { neighborOfNeighbor.LeftNeighbor = this; RightNeighbor = neighborOfNeighbor; LeftNeighbor = neighbor; neighbor.RightNeighbor = this; } else { neighborOfNeighbor.RightNeighbor = this; LeftNeighbor = neighborOfNeighbor; RightNeighbor = neighbor; neighbor.LeftNeighbor = this; } } else { if (isLeftNeighbor) { neighbor.RightNeighbor = this; LeftNeighbor = neighbor; } else { neighbor.LeftNeighbor = this; RightNeighbor = neighbor; } } } /// /// Attempts to move the forward bucket along the chain towards the fire. /// The person negotiates with the right hand side neighbor. /// If that neighbor can accept a forward bucket it received it. /// They also check if the right hand side neighbor has a backward (towards the well) /// bucket. If this person is able to receive the backward bucket it receives it. /// If at least one bucket is exchanged both involved people are done for the current step. /// /// Number of the current (chain) operation step /// True if the move attempt produced no errors - this does not mean /// that a bucket was moved, only that no invalid state was detected; false otherwise public bool MoveBucket(int currentStep) { if (currentStep < 0 || currentStep == LastStepPerformed || RightNeighbor == null) { return false; } if ((RightNeighbor._forwardBucket == null || RightNeighbor._forwardBucket.IsEmpty) && _forwardBucket != null) { RightNeighbor._forwardBucket = _forwardBucket; _forwardBucket = null; } if (RightNeighbor._backwardBucket != null && _backwardBucket == null) { _backwardBucket = RightNeighbor._backwardBucket; RightNeighbor._backwardBucket = null; } LastStepPerformed = currentStep; return true; } /// /// Fights the passed fire with water from the forward bucket. This empties the bucket. /// After using it the now empty bucket is moved to the backward position. /// /// The fire to throw water at public void FightFire(Fire fire) { var amount = _forwardBucket?.Empty(); fire.GetHitByWater(amount ?? 0); _backwardBucket = _forwardBucket; _forwardBucket = null; } /// /// Uses the passed well to fill the bucket. /// If this person currently has no empty bucket to fill but there are still unused buckets /// available for the chain a new bucket will be introduced with this method. /// If the person has no forward bucket but a backward bucket that one is filled. /// /// The well to use for filling the bucket /// A new bucket to use; can be null public void UseWell(Well well, Bucket? additionalBucket) { // fill forward bucket if empty if (_forwardBucket is { IsEmpty: true }) // using here pattern, bc rider suggests it { well.FillBucket(_forwardBucket); } // else fill backward bucket if empty else if (_forwardBucket == null && _backwardBucket is { IsEmpty: true }) { well.FillBucket(_backwardBucket); // if successful, swap buckets if (!_backwardBucket.IsEmpty) { _forwardBucket = _backwardBucket; _backwardBucket = null; } } // else add additional bucket else if (additionalBucket != null && _forwardBucket == null && _backwardBucket == null) { _forwardBucket = additionalBucket; well.FillBucket(_forwardBucket); } } /// /// Creates a string representation of this person instance. /// Shows if the person has none, an empty or full bucket. /// If the person has both forward and backward buckets the forward bucket is displayed. /// /// String representation of the person public override string ToString() { if (_forwardBucket != null) { return _forwardBucket.IsEmpty ? "🔘" : "🔵"; } if (_backwardBucket != null) { return _backwardBucket.IsEmpty ? "🔘" : "🔵"; } return "🫲"; } }