fix: implement direction input queue for rapid key presses

This commit is contained in:
Heller
2026-06-17 19:16:05 +00:00

View File

@@ -3,7 +3,7 @@ namespace Snake.Core;
public sealed class Snake
{
private readonly LinkedList<Position> _segments = new();
private Direction _pendingDirection;
private readonly Queue<Direction> _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<Position> Segments => _segments;
/// <summary>
/// Enqueues a direction change to be applied on a future tick.
/// </summary>
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);
}
/// <summary>
/// Returns the head position after the next move without changing state.
/// Invalid queued directions (same as current or opposite) are discarded.
/// </summary>
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);
}
/// <summary>
/// Applies at most one valid queued direction change, then moves the snake.
/// </summary>
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);
}
/// <summary>
/// Removes queued directions that would turn backwards or repeat the current direction.
/// </summary>
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
{