fix: implement direction input queue for rapid key presses
This commit is contained in:
@@ -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
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user