From 020303a8dd605856d967b26af9563f0989cc90e7 Mon Sep 17 00:00:00 2001 From: bvanvugt <1531419+bvanvugt@users.noreply.github.com> Date: Tue, 11 Jan 2022 22:44:37 +0000 Subject: [PATCH] Isolate and improve logic to determine default move. --- standard.go | 60 +++++++++++++++++++++++++++++-------------- standard_test.go | 66 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+), 19 deletions(-) diff --git a/standard.go b/standard.go index 9cee9c6..96e2573 100644 --- a/standard.go +++ b/standard.go @@ -98,8 +98,20 @@ func (r *StandardRuleset) moveSnakes(b *BoardState, moves []SnakeMove) error { for _, move := range moves { if move.ID == snake.ID { - var newHead = Point{} + appliedMove := move.Move switch move.Move { + case MoveUp, MoveDown, MoveRight, MoveLeft: + break + default: + appliedMove = r.getDefaultMove(snake.Body) + } + + newHead := Point{} + switch appliedMove { + // Guaranteed to be one of these options given the clause above + case MoveUp: + newHead.X = snake.Body[0].X + newHead.Y = snake.Body[0].Y + 1 case MoveDown: newHead.X = snake.Body[0].X newHead.Y = snake.Body[0].Y - 1 @@ -109,24 +121,6 @@ func (r *StandardRuleset) moveSnakes(b *BoardState, moves []SnakeMove) error { case MoveRight: newHead.X = snake.Body[0].X + 1 newHead.Y = snake.Body[0].Y - case MoveUp: - newHead.X = snake.Body[0].X - newHead.Y = snake.Body[0].Y + 1 - default: - // Default to UP - var dX int32 = 0 - var dY int32 = 1 - // If neck is available, use neck to determine last direction - if len(snake.Body) >= 2 { - dX = snake.Body[0].X - snake.Body[1].X - dY = snake.Body[0].Y - snake.Body[1].Y - if dX == 0 && dY == 0 { - dY = 1 // Move up if no last move was made - } - } - // Apply - newHead.X = snake.Body[0].X + dX - newHead.Y = snake.Body[0].Y + dY } // Append new head, pop old tail @@ -137,6 +131,34 @@ func (r *StandardRuleset) moveSnakes(b *BoardState, moves []SnakeMove) error { return nil } +func (r *StandardRuleset) getDefaultMove(snakeBody []Point) string { + if len(snakeBody) >= 2 { + // Use neck to determine last move made + head, neck := snakeBody[0], snakeBody[1] + // Situations where neck is next to head + if head.X == neck.X+1 { + return MoveRight + } else if head.X == neck.X-1 { + return MoveLeft + } else if head.Y == neck.Y+1 { + return MoveUp + } else if head.Y == neck.Y-1 { + return MoveDown + } + // Consider the wrapped cases using zero axis to anchor + if head.X == 0 && neck.X > 0 { + return MoveRight + } else if neck.X == 0 && head.X > 0 { + return MoveLeft + } else if head.Y == 0 && neck.Y > 0 { + return MoveUp + } else if neck.Y == 0 && head.Y > 0 { + return MoveDown + } + } + return MoveUp +} + func (r *StandardRuleset) reduceSnakeHealth(b *BoardState) error { for i := 0; i < len(b.Snakes); i++ { if b.Snakes[i].EliminatedCause == NotEliminated { diff --git a/standard_test.go b/standard_test.go index 33d5878..776fc64 100644 --- a/standard_test.go +++ b/standard_test.go @@ -658,6 +658,72 @@ func TestMoveSnakesDefault(t *testing.T) { } } +func TestGetDefaultMove(t *testing.T) { + tests := []struct { + SnakeBody []Point + ExpectedMove string + }{ + // Default is always up + { + SnakeBody: []Point{}, + ExpectedMove: MoveUp, + }, + { + SnakeBody: []Point{{0, 0}}, + ExpectedMove: MoveUp, + }, + { + SnakeBody: []Point{{-1, -1}}, + ExpectedMove: MoveUp, + }, + // Stacked (fallback to default) + { + SnakeBody: []Point{{2, 2}, {2, 2}}, + ExpectedMove: MoveUp, + }, + // Neck next to head + { + SnakeBody: []Point{{2, 2}, {2, 1}}, + ExpectedMove: MoveUp, + }, + { + SnakeBody: []Point{{2, 2}, {2, 3}}, + ExpectedMove: MoveDown, + }, + { + SnakeBody: []Point{{2, 2}, {1, 2}}, + ExpectedMove: MoveRight, + }, + { + SnakeBody: []Point{{2, 2}, {3, 2}}, + ExpectedMove: MoveLeft, + }, + // Board wrap cases + { + SnakeBody: []Point{{0, 0}, {0, 2}}, + ExpectedMove: MoveUp, + }, + { + SnakeBody: []Point{{0, 0}, {2, 0}}, + ExpectedMove: MoveRight, + }, + { + SnakeBody: []Point{{0, 2}, {0, 0}}, + ExpectedMove: MoveDown, + }, + { + SnakeBody: []Point{{2, 0}, {0, 0}}, + ExpectedMove: MoveLeft, + }, + } + + r := StandardRuleset{} + for _, test := range tests { + actualMove := r.getDefaultMove(test.SnakeBody) + require.Equal(t, test.ExpectedMove, actualMove) + } +} + func TestReduceSnakeHealth(t *testing.T) { b := &BoardState{ Snakes: []Snake{