ex-col-02-bucket-chain/BucketChain/Person.cs
MarcUs7i c3bd3a7416 Changed fields to match the README.adoc
Operate_Initially_NotLongEnough is currently in an infinite loop
2024-12-18 08:09:44 +01:00

194 lines
No EOL
7.2 KiB
C#

namespace BucketChain;
/// <summary>
/// Represents a person working together with others in a bucket chain to extinguish a fire.
/// </summary>
public sealed class Person
{
private Bucket? _backwardBucket;
private Bucket? _forwardBucket;
/// <summary>
/// Creates a new person.
/// We are not interested in any specific details of the person,
/// only the position in the chain is relevant.
/// </summary>
public Person()
{
// TODO
}
/// <summary>
/// Gets if the person currently has at least one bucket (forward or backward).
/// </summary>
public bool HasBucket => _forwardBucket != null || _backwardBucket != null;
/// <summary>
/// Gets the left neighbor of the person. Can be null.
/// </summary>
public Person? LeftNeighbor { get; private set; }
/// <summary>
/// Gets the right neighbor of the person. Can be null.
/// </summary>
public Person? RightNeighbor { get; private set; }
/// <summary>
/// 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.
/// </summary>
public int LastStepPerformed { get; private set; }
/// <summary>
/// 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.
/// </summary>
/// <param name="neighbor">The neighbor in the bucket chain</param>
/// <param name="isLeftNeighbor">If true the passed person will become the left hand side
/// neighbor of this person; otherwise it will become the right hand side neighbor</param>
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;
}
}
}
/// <summary>
/// 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.
/// </summary>
/// <param name="currentStep">Number of the current (chain) operation step</param>
/// <returns>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</returns>
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;
RightNeighbor.LastStepPerformed = currentStep;
return true;
}
/// <summary>
/// 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.
/// </summary>
/// <param name="fire">The fire to throw water at</param>
public void FightFire(Fire fire)
{
var amount = _forwardBucket?.Empty();
fire.GetHitByWater(amount ?? 0);
_backwardBucket = _forwardBucket;
_forwardBucket = null;
}
/// <summary>
/// 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.
/// </summary>
/// <param name="well">The well to use for filling the bucket</param>
/// <param name="additionalBucket">A new bucket to use; can be null</param>
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)
{
if (_forwardBucket == null)
{
_forwardBucket = additionalBucket;
well.FillBucket(_forwardBucket);
}
else if (_backwardBucket == null)
{
_backwardBucket = additionalBucket;
well.FillBucket(_backwardBucket);
}
}
}
/// <summary>
/// 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.
/// </summary>
/// <returns>String representation of the person</returns>
public override string ToString()
{
if (_forwardBucket != null)
{
return _forwardBucket.IsEmpty ? "🔘" : "🔵";
}
if (_backwardBucket != null)
{
return _backwardBucket.IsEmpty ? "🔘" : "🔵";
}
return "🫲";
}
}