diff --git a/Snake.Core/Snake.cs b/Snake.Core/Snake.cs index 3be64ff..fd24a66 100644 --- a/Snake.Core/Snake.cs +++ b/Snake.Core/Snake.cs @@ -3,7 +3,7 @@ namespace Snake.Core; public sealed class Snake { private readonly LinkedList _segments = new(); - private Direction _pendingDirection; + private readonly Queue _pendingDirections = new(); public Snake(Position start, int length, Direction direction) { @@ -11,7 +11,6 @@ public sealed class Snake throw new ArgumentOutOfRangeException(nameof(length), "Length must be at least 1."); Direction = direction; - _pendingDirection = direction; var offset = DirectionToOffset(direction); for (var i = 0; i < length; i++) @@ -28,30 +27,37 @@ public sealed class Snake public IReadOnlyCollection Segments => _segments; + /// + /// Enqueues a direction change to be applied on a future tick. + /// public void SetDirection(Direction direction) { - if (direction == _pendingDirection) - return; - - if (AreOpposite(Direction, direction)) - return; - - if (_pendingDirection != Direction && AreOpposite(_pendingDirection, direction)) - return; - - _pendingDirection = direction; + _pendingDirections.Enqueue(direction); } + /// + /// Returns the head position after the next move without changing state. + /// Invalid queued directions (same as current or opposite) are discarded. + /// public Position PeekNextHead() { - var offset = DirectionToOffset(_pendingDirection); + DiscardInvalidPendingDirections(); + var direction = _pendingDirections.Count > 0 ? _pendingDirections.Peek() : Direction; + var offset = DirectionToOffset(direction); return new Position(Head.X + offset.X, Head.Y + offset.Y); } + /// + /// Applies at most one valid queued direction change, then moves the snake. + /// public Position Move(bool grow = false) { - Direction = _pendingDirection; - var newHead = PeekNextHead(); + DiscardInvalidPendingDirections(); + if (_pendingDirections.Count > 0) + Direction = _pendingDirections.Dequeue(); + + var offset = DirectionToOffset(Direction); + var newHead = new Position(Head.X + offset.X, Head.Y + offset.Y); _segments.AddFirst(newHead); @@ -69,6 +75,21 @@ public sealed class Snake return _segments.Contains(position); } + /// + /// Removes queued directions that would turn backwards or repeat the current direction. + /// + private void DiscardInvalidPendingDirections() + { + while (_pendingDirections.Count > 0) + { + var next = _pendingDirections.Peek(); + if (next != Direction && !AreOpposite(Direction, next)) + break; + + _pendingDirections.Dequeue(); + } + } + private static Position DirectionToOffset(Direction direction) => direction switch {