From 798be609ba3f3613312a654419fcaa1c2b1aeec9 Mon Sep 17 00:00:00 2001 From: Brad Van Vugt <1531419+bvanvugt@users.noreply.github.com> Date: Tue, 31 Dec 2019 20:43:45 -0800 Subject: [PATCH] Create standard_test.go --- standard_test.go | 876 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 876 insertions(+) create mode 100644 standard_test.go diff --git a/standard_test.go b/standard_test.go new file mode 100644 index 0000000..3fc899e --- /dev/null +++ b/standard_test.go @@ -0,0 +1,876 @@ +package rulesets + +import ( + "math" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestSanity(t *testing.T) { + r := StandardRuleset{} + next, err := r.ResolveMoves( + &Game{}, + &GameState{}, + []*SnakeMove{}, + ) + + require.NoError(t, err) + require.NotNil(t, next) +} + +func TestMoveSnakes(t *testing.T) { + gs := &GameState{ + Snakes: []*Snake{ + { + ID: "one", + Body: []*Point{{10, 110}, {11, 110}}, + Health: 111111, + }, + { + ID: "two", + Body: []*Point{{23, 220}, {22, 220}, {21, 220}, {20, 220}}, + Health: 222222, + }, + }, + } + + tests := []struct { + MoveOne string + ExpectedOne []*Point + MoveTwo string + ExpectedTwo []*Point + }{ + { + MOVE_UP, + []*Point{{10, 109}, {10, 110}}, + MOVE_DOWN, + []*Point{{23, 221}, {23, 220}, {22, 220}, {21, 220}}, + }, + { + MOVE_RIGHT, + []*Point{{11, 109}, {10, 109}}, + MOVE_LEFT, + []*Point{{22, 221}, {23, 221}, {23, 220}, {22, 220}}, + }, + { + MOVE_RIGHT, + []*Point{{12, 109}, {11, 109}}, + MOVE_LEFT, + []*Point{{21, 221}, {22, 221}, {23, 221}, {23, 220}}, + }, + { + MOVE_RIGHT, + []*Point{{13, 109}, {12, 109}}, + MOVE_LEFT, + []*Point{{20, 221}, {21, 221}, {22, 221}, {23, 221}}, + }, + { + MOVE_UP, + []*Point{{13, 108}, {13, 109}}, + MOVE_DOWN, + []*Point{{20, 222}, {20, 221}, {21, 221}, {22, 221}}, + }, + } + + r := StandardRuleset{} + for _, test := range tests { + moves := []*SnakeMove{ + {Snake: gs.Snakes[0], Move: test.MoveOne}, + {Snake: gs.Snakes[1], Move: test.MoveTwo}, + } + err := r.moveSnakes(gs, moves) + + require.NoError(t, err) + require.Len(t, gs.Snakes, 2) + require.Equal(t, int32(111111), gs.Snakes[0].Health) + require.Equal(t, int32(222222), gs.Snakes[1].Health) + require.Len(t, gs.Snakes[0].Body, 2) + require.Len(t, gs.Snakes[1].Body, 4) + + require.Equal(t, len(gs.Snakes[0].Body), len(test.ExpectedOne)) + for i, e := range test.ExpectedOne { + require.Equal(t, *e, *gs.Snakes[0].Body[i]) + } + require.Equal(t, len(gs.Snakes[1].Body), len(test.ExpectedTwo)) + for i, e := range test.ExpectedTwo { + require.Equal(t, *e, *gs.Snakes[1].Body[i]) + } + } +} + +func TestMoveSnakesDefault(t *testing.T) { + tests := []struct { + Body []*Point + Move string + Expected []*Point + }{ + { + Body: []*Point{{0, 0}}, + Move: "asdf", + Expected: []*Point{{0, -1}}, + }, + { + Body: []*Point{{5, 5}, {5, 5}}, + Move: "", + Expected: []*Point{{5, 4}, {5, 5}}, + }, + { + Body: []*Point{{5, 5}, {5, 4}}, + Expected: []*Point{{5, 6}, {5, 5}}, + }, + { + Body: []*Point{{5, 4}, {5, 5}}, + Expected: []*Point{{5, 3}, {5, 4}}, + }, + { + Body: []*Point{{5, 4}, {5, 5}}, + Expected: []*Point{{5, 3}, {5, 4}}, + }, + { + Body: []*Point{{4, 5}, {5, 5}}, + Expected: []*Point{{3, 5}, {4, 5}}, + }, + { + Body: []*Point{{5, 5}, {4, 5}}, + Expected: []*Point{{6, 5}, {5, 5}}, + }, + } + + r := StandardRuleset{} + for _, test := range tests { + gs := &GameState{ + Snakes: []*Snake{ + {Body: test.Body}, + }, + } + moves := []*SnakeMove{{Snake: gs.Snakes[0], Move: test.Move}} + + err := r.moveSnakes(gs, moves) + require.NoError(t, err) + require.Len(t, gs.Snakes, 1) + require.Equal(t, len(test.Body), len(gs.Snakes[0].Body)) + require.Equal(t, len(test.Expected), len(gs.Snakes[0].Body)) + for i, e := range test.Expected { + require.Equal(t, *e, *gs.Snakes[0].Body[i]) + } + } +} + +// func TestMoveSnakesDefault(t *testing.T) { +// gs := &GameState{ +// Snakes: []*Snake{ +// { +// ID: "one", +// Body: []*Point{{10, 110}, {11, 110}, {12, 110}}, +// Health: 111111, +// }, +// }, +// } + +// tests := []struct { +// Move string +// Expected []*Point +// }{ +// {"asdf", []*Point{{10, 109}, {10, 110}, {10, 111}}}, +// {"asdf", []*Point{{10, 108}, {10, 109}, {10, 110}}}, +// {"asdf", []*Point{{10, 107}, {10, 108}, {10, 109}}}, +// {"asdf", []*Point{{10, 106}, {10, 107}, {10, 108}}}, +// } + +// r := StandardRuleset{} +// for _, test := range tests { +// moves := []*SnakeMove{ +// {Snake: gs.Snakes[0], Move: test.Move}, +// } +// err := r.moveSnakes(gs, moves) + +// require.NoError(t, err) +// require.Len(t, gs.Snakes, 1) +// require.Equal(t, int32(111111), gs.Snakes[0].Health) +// require.Len(t, gs.Snakes[0].Body, 3) + +// require.Equal(t, len(gs.Snakes[0].Body), len(test.Expected)) +// for i, e := range test.ExpectedOne { +// require.Equal(t, *e, *gs.Snakes[0].Body[i]) +// } +// } +// } + +// func TestMoveSnakesDefault(t *testing.T) { +// tests := []struct { +// Body []*Point +// Expected string +// }{ +// { +// Body: []*Point{}, +// Expected: MOVE_UP, +// }, +// { +// Body: []*Point{ +// {X: 0, Y: 0}, +// }, +// Expected: MOVE_UP, +// }, +// { +// Body: []*Point{ +// {X: 5, Y: 5}, +// {X: 5, Y: 5}, +// }, +// Expected: MOVE_UP, +// }, +// { +// Body: []*Point{ +// {X: 5, Y: 5}, +// {X: 5, Y: 4}, +// }, +// Expected: MOVE_DOWN, +// }, +// { +// Body: []*Point{ +// {X: 5, Y: 4}, +// {X: 5, Y: 5}, +// }, +// Expected: MOVE_UP, +// }, +// { +// Body: []*Point{ +// {X: 4, Y: 5}, +// {X: 5, Y: 5}, +// }, +// Expected: MOVE_LEFT, +// }, +// { +// Body: []*Point{ +// {X: 5, Y: 5}, +// {X: 4, Y: 5}, +// }, +// Expected: MOVE_RIGHT, +// }, +// } + +// r := StandardRuleset{} +// for _, test := range tests { +// s := &Snake{Body: test.Body} +// actual := r.getDefaultMove(s) +// require.Equal(t, test.Expected, actual) +// } +// } + +func TestReduceSnakeHealth(t *testing.T) { + var err error + r := StandardRuleset{} + gs := &GameState{ + Snakes: []*Snake{ + &Snake{ + Body: []*Point{{0, 0}, {0, 1}}, + Health: 99, + }, + &Snake{ + Body: []*Point{{5, 8}, {6, 8}, {7, 8}}, + Health: 2, + }, + }, + } + + err = r.reduceSnakeHealth(gs) + require.NoError(t, err) + require.Equal(t, gs.Snakes[0].Health, int32(98)) + require.Equal(t, gs.Snakes[1].Health, int32(1)) + + err = r.reduceSnakeHealth(gs) + require.NoError(t, err) + require.Equal(t, gs.Snakes[0].Health, int32(97)) + require.Equal(t, gs.Snakes[1].Health, int32(0)) + + err = r.reduceSnakeHealth(gs) + require.NoError(t, err) + require.Equal(t, gs.Snakes[0].Health, int32(96)) + require.Equal(t, gs.Snakes[1].Health, int32(-1)) + + err = r.reduceSnakeHealth(gs) + require.NoError(t, err) + require.Equal(t, gs.Snakes[0].Health, int32(95)) + require.Equal(t, gs.Snakes[1].Health, int32(-2)) +} + +func TestSnakeHasStarved(t *testing.T) { + tests := []struct { + Health int32 + Expected bool + }{ + {Health: math.MinInt32, Expected: true}, + {Health: -10, Expected: true}, + {Health: -2, Expected: true}, + {Health: -1, Expected: true}, + {Health: 0, Expected: true}, + {Health: 1, Expected: false}, + {Health: 2, Expected: false}, + {Health: 10, Expected: false}, + {Health: math.MaxInt32, Expected: false}, + } + + r := StandardRuleset{} + for _, test := range tests { + s := &Snake{Health: test.Health} + require.Equal(t, test.Expected, r.snakeHasStarved(s), "Health: %+v", test.Health) + } +} + +func TestSnakeIsOutOfBounds(t *testing.T) { + var boardWidth int32 = 10 + var boardHeight int32 = 100 + + tests := []struct { + Point Point + Expected bool + }{ + {Point{X: math.MinInt32, Y: math.MinInt32}, true}, + {Point{X: math.MinInt32, Y: 0}, true}, + {Point{X: 0, Y: math.MinInt32}, true}, + {Point{X: -1, Y: -1}, true}, + {Point{X: -1, Y: 0}, true}, + {Point{X: 0, Y: -1}, true}, + {Point{X: 0, Y: 0}, false}, + {Point{X: 1, Y: 0}, false}, + {Point{X: 0, Y: 1}, false}, + {Point{X: 1, Y: 1}, false}, + {Point{X: 9, Y: 9}, false}, + {Point{X: 9, Y: 10}, false}, + {Point{X: 9, Y: 11}, false}, + {Point{X: 10, Y: 9}, true}, + {Point{X: 10, Y: 10}, true}, + {Point{X: 10, Y: 11}, true}, + {Point{X: 11, Y: 9}, true}, + {Point{X: 11, Y: 10}, true}, + {Point{X: 11, Y: 11}, true}, + {Point{X: math.MaxInt32, Y: 11}, true}, + {Point{X: 9, Y: 99}, false}, + {Point{X: 9, Y: 100}, true}, + {Point{X: 9, Y: 101}, true}, + {Point{X: 9, Y: math.MaxInt32}, true}, + {Point{X: math.MaxInt32, Y: math.MaxInt32}, true}, + } + + var s *Snake + r := StandardRuleset{} + for _, test := range tests { + // Test with point as head + s = &Snake{Body: []*Point{&test.Point}} + require.Equal(t, test.Expected, r.snakeIsOutOfBounds(s, boardWidth, boardHeight), "Head%+v", test.Point) + // Test with point as body + s = &Snake{Body: []*Point{&Point{0, 0}, &Point{0, 0}, &test.Point}} + require.Equal(t, test.Expected, r.snakeIsOutOfBounds(s, boardWidth, boardHeight), "Body%+v", test.Point) + } +} + +func TestSnakeHasBodyCollidedSelf(t *testing.T) { + tests := []struct { + Body []*Point + Expected bool + }{ + {[]*Point{{1, 1}}, false}, + // Self stacks should self collide + // (we rely on snakes moving before we check self-collision on turn one) + {[]*Point{{2, 2}, {2, 2}}, true}, + {[]*Point{{3, 3}, {3, 3}, {3, 3}}, true}, + {[]*Point{{5, 5}, {5, 5}, {5, 5}, {5, 5}, {5, 5}}, true}, + // Non-collision cases + {[]*Point{{0, 0}, {1, 0}, {1, 0}}, false}, + {[]*Point{{0, 0}, {1, 0}, {2, 0}}, false}, + {[]*Point{{0, 0}, {1, 0}, {2, 0}, {2, 0}, {2, 0}}, false}, + {[]*Point{{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}}, false}, + {[]*Point{{0, 0}, {0, 1}, {0, 2}}, false}, + {[]*Point{{0, 0}, {0, 1}, {0, 2}, {0, 2}, {0, 2}}, false}, + {[]*Point{{0, 0}, {0, 1}, {0, 2}, {0, 3}, {0, 4}}, false}, + // Collision cases + {[]*Point{{0, 0}, {1, 0}, {0, 0}}, true}, + {[]*Point{{0, 0}, {0, 0}, {1, 0}}, true}, + {[]*Point{{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}}, true}, + {[]*Point{{4, 4}, {3, 4}, {3, 3}, {4, 4}, {4, 4}}, true}, + {[]*Point{{3, 3}, {3, 4}, {3, 3}, {4, 4}, {4, 5}}, true}, + } + + var s *Snake + r := StandardRuleset{} + for _, test := range tests { + s = &Snake{Body: test.Body} + require.Equal(t, test.Expected, r.snakeHasBodyCollided(s, s), "Body%q", s.Body) + } +} + +func TestSnakeHasBodyCollidedOther(t *testing.T) { + tests := []struct { + SnakeBody []*Point + OtherBody []*Point + Expected bool + }{ + { + // Just heads + []*Point{{0, 0}}, + []*Point{{1, 1}}, + false, + }, + { + // Head-to-heads are not considered in body collisions + []*Point{{0, 0}}, + []*Point{{0, 0}}, + false, + }, + { + // Stacked bodies + []*Point{{0, 0}}, + []*Point{{0, 0}, {0, 0}}, + true, + }, + { + // Separate stacked bodies + []*Point{{0, 0}, {0, 0}, {0, 0}}, + []*Point{{1, 1}, {1, 1}, {1, 1}}, + false, + }, + { + // Stacked bodies, separated heads + []*Point{{0, 0}, {1, 0}, {1, 0}}, + []*Point{{2, 0}, {1, 0}, {1, 0}}, + false, + }, + { + // Mid-snake collision + []*Point{{1, 1}}, + []*Point{{0, 1}, {1, 1}, {2, 1}}, + true, + }, + } + + var s *Snake + var o *Snake + r := StandardRuleset{} + for _, test := range tests { + s = &Snake{Body: test.SnakeBody} + o = &Snake{Body: test.OtherBody} + require.Equal(t, test.Expected, r.snakeHasBodyCollided(s, o), "Snake%q Other%q", s.Body, o.Body) + } +} + +func TestSnakeHasLostHeadToHead(t *testing.T) { + tests := []struct { + SnakeBody []*Point + OtherBody []*Point + Expected bool + ExpectedOpposite bool + }{ + { + // Just heads + []*Point{{0, 0}}, + []*Point{{1, 1}}, + false, false, + }, + { + // Just heads colliding + []*Point{{0, 0}}, + []*Point{{0, 0}}, + true, true, + }, + { + // One snake larger + []*Point{{0, 0}, {1, 0}, {2, 0}}, + []*Point{{0, 0}}, + false, true, + }, + { + // Other snake equal + []*Point{{0, 0}, {1, 0}, {2, 0}}, + []*Point{{0, 0}, {0, 1}, {0, 2}}, + true, true, + }, + { + // Other snake longer + []*Point{{0, 0}, {1, 0}, {2, 0}}, + []*Point{{0, 0}, {0, 1}, {0, 2}, {0, 3}}, + true, false, + }, + { + // Body collision + []*Point{{0, 1}, {1, 1}, {2, 1}}, + []*Point{{0, 0}, {0, 1}, {0, 2}, {0, 3}}, + false, false, + }, + { + // Separate stacked bodies, head collision + []*Point{{3, 10}, {2, 10}, {2, 10}}, + []*Point{{3, 10}, {4, 10}, {4, 10}}, + true, true, + }, + { + // Separate stacked bodies, head collision + []*Point{{10, 3}, {10, 2}, {10, 1}, {10, 0}}, + []*Point{{10, 3}, {10, 4}, {10, 5}}, + false, true, + }, + } + + var s *Snake + var o *Snake + r := StandardRuleset{} + for _, test := range tests { + s = &Snake{Body: test.SnakeBody} + o = &Snake{Body: test.OtherBody} + require.Equal(t, test.Expected, r.snakeHasLostHeadToHead(s, o), "Snake%q Other%q", s.Body, o.Body) + require.Equal(t, test.ExpectedOpposite, r.snakeHasLostHeadToHead(o, s), "Snake%q Other%q", s.Body, o.Body) + } + +} + +func TestFeedSnakes(t *testing.T) { + r := StandardRuleset{} + gs := &GameState{ + Snakes: []*Snake{ + {Body: []*Point{ + {2, 1}, {1, 1}, {1, 2}, {2, 2}, + }}, + }, + Food: []*Point{ + {2, 1}, + }, + } + + err := r.feedSnakes(gs) + require.NoError(t, err) + require.Equal(t, 0, len(gs.Food)) + +} + +// func TestCheckForSnakesEating(t *testing.T) { +// snake := &pb.Snake{ +// Body: []*pb.Point{ +// {X: 2, Y: 1}, +// {X: 1, Y: 1}, +// {X: 1, Y: 2}, +// {X: 2, Y: 2}, +// }, +// } +// checkForSnakesEating(&pb.GameFrame{ +// Food: []*pb.Point{ +// {X: 2, Y: 1}, +// }, +// Snakes: []*pb.Snake{snake}, +// }) +// require.Len(t, snake.Body, 4) +// require.Equal(t, snake.Body[2], snake.Body[3]) +// } + +// func TestCheckForSnakesNotEating(t *testing.T) { +// snake := &pb.Snake{ +// Body: []*pb.Point{ +// {X: 2, Y: 1}, +// {X: 1, Y: 1}, +// {X: 1, Y: 2}, +// {X: 2, Y: 2}, +// }, +// } +// checkForSnakesEating(&pb.GameFrame{ +// Food: []*pb.Point{}, +// Snakes: []*pb.Snake{snake}, +// }) +// require.Len(t, snake.Body, 3) +// require.NotEqual(t, snake.Body[2], snake.Body[1]) +// } +// func TestGameFrameSnakeEats(t *testing.T) { +// snake := &pb.Snake{ +// Health: 67, +// Body: []*pb.Point{ +// {X: 1, Y: 1}, +// {X: 1, Y: 2}, +// {X: 1, Y: 3}, +// }, +// } + +// lastFrame.Snakes = []*pb.Snake{snake} + +// gt, err := GameTick(commonGame, lastFrame) +// require.NoError(t, err) +// require.Len(t, gt.Snakes, 1) +// snake = gt.Snakes[0] +// require.Equal(t, int32(100), snake.Health) +// require.Len(t, snake.Body, 4) +// } + +// func TestUpdateFood(t *testing.T) { +// updated, err := updateFood(&pb.Game{Width: 20, Height: 20}, &pb.GameFrame{ +// Food: []*pb.Point{ +// {X: 1, Y: 1}, +// {X: 1, Y: 2}, +// }, +// Snakes: []*pb.Snake{ +// { +// Body: []*pb.Point{ +// {X: 1, Y: 2}, +// {X: 2, Y: 2}, +// {X: 3, Y: 2}, +// }, +// }, +// }, +// }, []*pb.Point{ +// {X: 1, Y: 2}, +// }) +// require.NoError(t, err) +// require.Len(t, updated, 2) +// require.True(t, updated[0].Equal(&pb.Point{X: 1, Y: 1})) +// require.False(t, updated[1].Equal(&pb.Point{X: 1, Y: 2})) +// require.False(t, updated[1].Equal(&pb.Point{X: 2, Y: 2})) +// require.False(t, updated[1].Equal(&pb.Point{X: 3, Y: 2})) +// require.False(t, updated[1].Equal(&pb.Point{X: 1, Y: 1})) +// } + +// func TestUpdateFoodWithFullBoard(t *testing.T) { +// updated, err := updateFood(&pb.Game{Width: 2, Height: 2}, &pb.GameFrame{ +// Food: []*pb.Point{ +// {X: 0, Y: 0}, +// }, +// Snakes: []*pb.Snake{ +// { +// Body: []*pb.Point{ +// {X: 0, Y: 0}, +// {X: 0, Y: 1}, +// {X: 1, Y: 1}, +// {X: 1, Y: 0}, +// }, +// }, +// }, +// }, []*pb.Point{ +// {X: 0, Y: 0}, +// }) +// require.NoError(t, err) +// require.Len(t, updated, 0) +// } + +// func TestGetUnoccupiedPointEven(t *testing.T) { + +// unoccupiedPoint := getUnoccupiedPointEven(2, 2, +// []*pb.Point{}, +// []*pb.Snake{}) +// require.True(t, (unoccupiedPoint.X+unoccupiedPoint.Y)%2 == 0, "Point coordinates should sum to an even number %o ", unoccupiedPoint) +// } + +// func TestGetUnoccupiedPointOdd(t *testing.T) { +// unoccupiedPoint := getUnoccupiedPointOdd(2, 2, +// []*pb.Point{{X: 0, Y: 1}}, +// []*pb.Snake{}) +// require.True(t, (unoccupiedPoint.X+unoccupiedPoint.Y)%2 == 1, "Point coordinates should sum to an odd number %o ", unoccupiedPoint) +// } + +// func TestGetUnoccupiedPointWithFullBoard(t *testing.T) { +// unoccupiedPoint := getUnoccupiedPoint(2, 2, +// []*pb.Point{{X: 0, Y: 0}}, +// []*pb.Snake{ +// { +// Body: []*pb.Point{ +// {X: 0, Y: 1}, +// {X: 1, Y: 1}, +// {X: 1, Y: 0}, +// }, +// }, +// }) +// require.True(t, unoccupiedPoint.Equal(nil)) +// } + +// func TestGetUnoccupiedPointsWithEmptySpots(t *testing.T) { +// unoccupiedPoints := getUnoccupiedPoints(2, 2, +// []*pb.Point{{X: 0, Y: 0}}, +// []*pb.Snake{ +// { +// Body: []*pb.Point{ +// {X: 0, Y: 1}, +// }, +// }, +// }) + +// require.Len(t, unoccupiedPoints, 2) +// require.True(t, unoccupiedPoints[0].Equal(&pb.Point{X: 1, Y: 0})) +// require.True(t, unoccupiedPoints[1].Equal(&pb.Point{X: 1, Y: 1})) +// } + +// func TestGetUniqOccupiedPoints(t *testing.T) { +// unoccupiedPoints := getUniqOccupiedPoints( +// []*pb.Point{ +// {X: 0, Y: 0}, +// }, +// []*pb.Snake{ +// { +// Body: []*pb.Point{ +// {X: 0, Y: 1}, +// {X: 1, Y: 1}, +// {X: 1, Y: 1}, +// {X: 1, Y: 0}, +// }, +// }, +// }) + +// require.Len(t, unoccupiedPoints, 4) +// } + +// func TestGameTickUpdatesTurnCounter(t *testing.T) { +// gt, err := GameTick(commonGame, &pb.GameFrame{Turn: 5}) +// require.NoError(t, err) +// require.Equal(t, int32(6), gt.Turn) +// } + +// func TestGameTickUpdatesSnake(t *testing.T) { +// snake := &pb.Snake{ +// Health: 67, +// Body: []*pb.Point{ +// {X: 1, Y: 1}, +// {X: 1, Y: 2}, +// {X: 1, Y: 3}, +// }, +// } +// game := &pb.Game{ +// Width: 20, +// Height: 20, +// } +// gt, err := GameTick(game, &pb.GameFrame{ +// Turn: 5, +// Snakes: []*pb.Snake{ +// snake, +// }, +// }) +// require.NoError(t, err) +// require.Len(t, gt.Snakes, 1) +// snake = gt.Snakes[0] +// require.Equal(t, int32(66), snake.Health) +// require.Len(t, snake.Body, 3) +// require.Equal(t, &pb.Point{X: 1, Y: 0}, snake.Body[0]) +// require.Equal(t, &pb.Point{X: 1, Y: 1}, snake.Body[1]) +// require.Equal(t, &pb.Point{X: 1, Y: 2}, snake.Body[2]) +// } + +// var commonGame = &pb.Game{ +// Width: 20, +// Height: 20, +// } +// var lastFrame = &pb.GameFrame{ +// Turn: 5, +// Snakes: []*pb.Snake{}, +// Food: []*pb.Point{ +// {X: 1, Y: 0}, +// }, +// } + +// func TestGameTickDeadSnakeDoNotUpdate(t *testing.T) { +// snake := &pb.Snake{ +// Health: 87, +// Body: []*pb.Point{ +// {X: 1, Y: 1}, +// {X: 1, Y: 2}, +// {X: 1, Y: 3}, +// }, +// Death: &pb.Death{ +// Turn: 4, +// Cause: DeathCauseSnakeCollision, +// }, +// } + +// lastFrame.Snakes = []*pb.Snake{snake} + +// gt, err := GameTick(commonGame, lastFrame) +// require.NoError(t, err) +// require.Len(t, gt.Snakes, 1) +// snake = gt.Snakes[0] +// require.Equal(t, int32(87), snake.Health) +// require.Len(t, snake.Body, 3) +// require.Equal(t, &pb.Point{X: 1, Y: 1}, snake.Body[0]) +// require.Equal(t, &pb.Point{X: 1, Y: 2}, snake.Body[1]) +// require.Equal(t, &pb.Point{X: 1, Y: 3}, snake.Body[2]) +// } + +// func TestGameTickUpdatesDeath(t *testing.T) { +// snake := &pb.Snake{ +// Health: 0, +// Body: []*pb.Point{ +// {X: 3, Y: 1}, +// {X: 3, Y: 2}, +// {X: 3, Y: 3}, +// }, +// } + +// lastFrame.Snakes = []*pb.Snake{snake} + +// gt, err := GameTick(commonGame, lastFrame) +// require.NoError(t, err) +// require.NotNil(t, gt.Snakes[0].Death) +// } + +// func TestUpdateSnakes(t *testing.T) { +// snake := &pb.Snake{ +// Body: []*pb.Point{ +// {X: 1, Y: 1}, +// }, +// } +// moves := []*SnakeUpdate{ +// { +// Snake: snake, +// Err: errors.New("some error"), +// }, +// } +// updateSnakes(&pb.Game{}, &pb.GameFrame{ +// Snakes: []*pb.Snake{snake}, +// }, moves) +// require.Equal(t, &pb.Point{X: 1, Y: 0}, snake.Head(), "snake did not move up") + +// moves = []*SnakeUpdate{ +// { +// Snake: snake, +// Move: "left", +// }, +// } +// updateSnakes(&pb.Game{}, &pb.GameFrame{ +// Snakes: []*pb.Snake{snake}, +// }, moves) +// require.Equal(t, &pb.Point{X: 0, Y: 0}, snake.Head(), "snake did not move left") +// } + +// func TestCanFollowTail(t *testing.T) { +// url := setupSnakeServer(t, MoveResponse{ +// Move: "down", +// }, StartResponse{}) +// snake := &pb.Snake{ +// Body: []*pb.Point{ +// {X: 2, Y: 1}, +// {X: 1, Y: 1}, +// {X: 1, Y: 2}, +// {X: 2, Y: 2}, +// }, +// URL: url, +// Health: 100, +// } +// next, err := GameTick(&pb.Game{ +// Width: 20, +// Height: 20, +// }, &pb.GameFrame{ +// Snakes: []*pb.Snake{snake}, +// }) +// require.NoError(t, err) +// require.NotNil(t, next) +// require.Nil(t, next.Snakes[0].Death) +// } + +// func TestNextFoodSpawn(t *testing.T) { +// rand.Seed(1) // random order is 65, 85, 29 +// snakes := []*pb.Snake{ +// {URL: setupSnakeServer(t, MoveResponse{}, StartResponse{})}, +// {URL: setupSnakeServer(t, MoveResponse{}, StartResponse{})}, +// {URL: setupSnakeServer(t, MoveResponse{}, StartResponse{})}, +// {URL: setupSnakeServer(t, MoveResponse{}, StartResponse{})}, +// } +// next, err := GameTick(&pb.Game{ +// Width: 20, +// Height: 20, +// TurnsSinceLastFoodSpawn: 5, +// MaxTurnsToNextFoodSpawn: 5, +// }, &pb.GameFrame{ +// Snakes: snakes, +// }) +// require.NoError(t, err) +// require.Len(t, next.Food, 2) +// }