From 12f9297b8b803c28ee6a8f4438a2a356f6b1b9a9 Mon Sep 17 00:00:00 2001 From: bvanvugt <1531419+bvanvugt@users.noreply.github.com> Date: Tue, 19 Jan 2021 14:55:49 -0800 Subject: [PATCH] Rename 'maze' -> 'constrictor', remove use of food --- constrictor.go | 54 ++++++++++++++ constrictor_test.go | 129 ++++++++++++++++++++++++++++++++ maze.go | 41 ----------- maze_test.go | 175 -------------------------------------------- 4 files changed, 183 insertions(+), 216 deletions(-) create mode 100644 constrictor.go create mode 100644 constrictor_test.go delete mode 100644 maze.go delete mode 100644 maze_test.go diff --git a/constrictor.go b/constrictor.go new file mode 100644 index 0000000..52f0b57 --- /dev/null +++ b/constrictor.go @@ -0,0 +1,54 @@ +package rules + +import () + +type ConstrictorRuleset struct { + StandardRuleset +} + +func (r *ConstrictorRuleset) CreateInitialBoardState(width int32, height int32, snakeIDs []string) (*BoardState, error) { + initialBoardState, err := r.StandardRuleset.CreateInitialBoardState(width, height, snakeIDs) + if err != nil { + return nil, err + } + + err = r.applyConstrictorRules(initialBoardState) + if err != nil { + return nil, err + } + + return initialBoardState, nil +} + +func (r *ConstrictorRuleset) CreateNextBoardState(prevState *BoardState, moves []SnakeMove) (*BoardState, error) { + + nextState, err := r.StandardRuleset.CreateNextBoardState(prevState, moves) + if err != nil { + return nil, err + } + + err = r.applyConstrictorRules(nextState) + if err != nil { + return nil, err + } + + return nextState, nil +} + +func (r *ConstrictorRuleset) applyConstrictorRules(b *BoardState) error { + // Remove all food from the board + b.Food = []Point{} + + // Set all snakes to max health and ensure they grow next turn + for i := 0; i < len(b.Snakes); i++ { + b.Snakes[i].Health = SnakeMaxHealth + + tail := b.Snakes[i].Body[len(b.Snakes[i].Body)-1] + subTail := b.Snakes[i].Body[len(b.Snakes[i].Body)-2] + if tail != subTail { + r.growSnake(&b.Snakes[i]) + } + } + + return nil +} diff --git a/constrictor_test.go b/constrictor_test.go new file mode 100644 index 0000000..b4e21fe --- /dev/null +++ b/constrictor_test.go @@ -0,0 +1,129 @@ +package rules + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestConstrictorRulesetInterface(t *testing.T) { + var _ Ruleset = (*ConstrictorRuleset)(nil) +} + +func TestConstrictorCreateInitialBoardState(t *testing.T) { + tests := []struct { + Height int32 + Width int32 + IDs []string + }{ + {1, 1, []string{}}, + {1, 1, []string{"one"}}, + {2, 2, []string{"one"}}, + {2, 2, []string{"one", "two"}}, + {11, 1, []string{"one", "two"}}, + {11, 11, []string{}}, + {11, 11, []string{"one", "two", "three", "four", "five"}}, + } + + r := ConstrictorRuleset{} + for testNum, test := range tests { + state, err := r.CreateInitialBoardState(test.Width, test.Height, test.IDs) + require.NoError(t, err) + require.NotNil(t, state) + require.Equal(t, test.Width, state.Width) + require.Equal(t, test.Height, state.Height) + require.Len(t, state.Food, 0, testNum) + // Verify snakes + require.Equal(t, len(test.IDs), len(state.Snakes)) + for i, id := range test.IDs { + require.Equal(t, id, state.Snakes[i].ID) + require.Equal(t, state.Snakes[i].Body[2], state.Snakes[i].Body[1]) + } + } +} + +func TestConstrictorCreateNextBoardState(t *testing.T) { + tests := []struct { + prevState *BoardState + moves []SnakeMove + expectedState *BoardState + }{ + { + &BoardState{ + Width: 3, + Height: 3, + Snakes: []Snake{ + { + ID: "one", + Body: []Point{{0, 0}, {0, 0}, {0, 0}}, + Health: 100, + }, + { + ID: "two", + Body: []Point{{2, 2}, {2, 2}, {2, 2}}, + Health: 100, + }, + }, + Food: []Point{}, + }, + []SnakeMove{ + {ID: "one", Move: MoveDown}, + {ID: "two", Move: MoveUp}, + }, + &BoardState{ + Width: 3, + Height: 3, + Snakes: []Snake{ + { + ID: "one", + Body: []Point{{0, 1}, {0, 0}, {0, 0}}, + Health: 100, + }, + { + ID: "two", + Body: []Point{{2, 1}, {2, 2}, {2, 2}}, + Health: 100, + }, + }, + Food: []Point{}, + }, + }, + // Ensure snakes keep growing and are fed + { + &BoardState{ + Width: 3, + Height: 3, + Snakes: []Snake{ + { + ID: "one", + Body: []Point{{2, 0}, {1, 0}, {0, 0}, {0, 0}}, + Health: 75, + }, + }, + Food: []Point{}, + }, + []SnakeMove{ + {ID: "one", Move: MoveDown}, + }, + &BoardState{ + Width: 3, + Height: 3, + Snakes: []Snake{ + { + ID: "one", + Body: []Point{{2, 1}, {2, 0}, {1, 0}, {0, 0}, {0, 0}}, + Health: 100, + }, + }, + Food: []Point{}, + }, + }, + } + + r := ConstrictorRuleset{} + for _, test := range tests { + nextState, err := r.CreateNextBoardState(test.prevState, test.moves) + require.NoError(t, err) + require.Equal(t, test.expectedState, nextState) + } +} diff --git a/maze.go b/maze.go deleted file mode 100644 index 33e0363..0000000 --- a/maze.go +++ /dev/null @@ -1,41 +0,0 @@ -package rules - -import () - -type MazeRuleset struct { - StandardRuleset -} - -func (r *MazeRuleset) CreateInitialBoardState(width int32, height int32, snakeIDs []string) (*BoardState, error) { - initialBoardState, err := r.StandardRuleset.CreateInitialBoardState(width, height, snakeIDs) - if err != nil { - return nil, err - } - - err = r.fillBoardWithFood(initialBoardState) - if err != nil { - return nil, err - } - - return initialBoardState, nil -} - -func (r *MazeRuleset) CreateNextBoardState(prevState *BoardState, moves []SnakeMove) (*BoardState, error) { - nextState, err := r.StandardRuleset.CreateNextBoardState(prevState, moves) - if err != nil { - return nil, err - } - - err = r.fillBoardWithFood(nextState) - if err != nil { - return nil, err - } - - return nextState, nil -} - -func (r *MazeRuleset) fillBoardWithFood(b *BoardState) error { - unoccupiedPoints := r.getUnoccupiedPoints(b, true) - b.Food = append(b.Food, unoccupiedPoints...) - return nil -} diff --git a/maze_test.go b/maze_test.go deleted file mode 100644 index 8304bf0..0000000 --- a/maze_test.go +++ /dev/null @@ -1,175 +0,0 @@ -package rules - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestMazeRulesetInterface(t *testing.T) { - var _ Ruleset = (*MazeRuleset)(nil) -} - -func TestMazeCreateInitialBoardState(t *testing.T) { - tests := []struct { - Height int32 - Width int32 - IDs []string - ExpectedNumFood int - Err error - }{ - {1, 1, []string{}, 1, nil}, - {1, 1, []string{"one"}, 0, nil}, - {2, 2, []string{"one"}, 3, nil}, - {2, 2, []string{"one", "two"}, 2, nil}, - {11, 1, []string{"one", "two"}, 9, nil}, - {11, 11, []string{}, 121, nil}, - {11, 11, []string{"one", "two", "three", "four", "five"}, 116, nil}, - } - - r := MazeRuleset{} - for testNum, test := range tests { - state, err := r.CreateInitialBoardState(test.Width, test.Height, test.IDs) - require.Equal(t, test.Err, err) - if err != nil { - require.Nil(t, state) - continue - } - require.NotNil(t, state) - require.Equal(t, test.Width, state.Width) - require.Equal(t, test.Height, state.Height) - require.Equal(t, len(test.IDs), len(state.Snakes)) - for i, id := range test.IDs { - require.Equal(t, id, state.Snakes[i].ID) - } - require.Len(t, state.Food, test.ExpectedNumFood, testNum) - } -} - -func TestMazeCreateNextBoardState(t *testing.T) { - tests := []struct { - prevState *BoardState - moves []SnakeMove - expectedState *BoardState - }{ - { - &BoardState{ - Width: 3, - Height: 3, - Snakes: []Snake{ - { - ID: "one", - Body: []Point{{0, 0}, {1, 0}, {2, 0}}, - Health: 100, - }, - { - ID: "two", - Body: []Point{{2, 2}, {1, 2}, {0, 2}}, - Health: 100, - }, - }, - Food: []Point{}, - }, - []SnakeMove{ - {ID: "one", Move: MoveDown}, - {ID: "two", Move: MoveUp}, - }, - &BoardState{ - Width: 3, - Height: 3, - Snakes: []Snake{ - { - ID: "one", - Body: []Point{{0, 1}, {0, 0}, {1, 0}}, - Health: 99, - }, - { - ID: "two", - Body: []Point{{2, 1}, {2, 2}, {1, 2}}, - Health: 99, - }, - }, - Food: []Point{{0, 2}, {1, 1}, {2, 0}}, - }, - }, - // Ensure food is spawning in front of snakes - { - &BoardState{ - Width: 3, - Height: 3, - Snakes: []Snake{ - { - ID: "one", - Body: []Point{{1, 0}, {1, 0}, {1, 0}}, - Health: 75, - }, - }, - Food: []Point{}, - }, - []SnakeMove{ - {ID: "one", Move: MoveDown}, - }, - &BoardState{ - Width: 3, - Height: 3, - Snakes: []Snake{ - { - ID: "one", - Body: []Point{{1, 1}, {1, 0}, {1, 0}}, - Health: 74, - }, - }, - Food: []Point{{0, 0}, {0, 1}, {0, 2}, {1, 2}, {2, 0}, {2, 1}, {2, 2}}, - }, - }, - // Ensure eliminated snakes are immediately replace with food - { - &BoardState{ - Width: 3, - Height: 3, - Snakes: []Snake{ - { - ID: "one", - Body: []Point{{1, 0}, {2, 0}, {2, 0}}, - Health: 100, - }, - { - ID: "two", - Body: []Point{{2, 2}, {1, 2}, {0, 2}}, - Health: 100, - }, - }, - Food: []Point{}, - }, - []SnakeMove{ - {ID: "one", Move: MoveLeft}, - {ID: "two", Move: MoveDown}, - }, - &BoardState{ - Width: 3, - Height: 3, - Snakes: []Snake{ - { - ID: "one", - Body: []Point{{0, 0}, {1, 0}, {2, 0}}, - Health: 99, - }, - { - ID: "two", - Body: []Point{{2, 3}, {2, 2}, {1, 2}}, - Health: 99, - EliminatedCause: EliminatedByOutOfBounds, - }, - }, - Food: []Point{{0, 1}, {0, 2}, {1, 1}, {1, 2}, {2, 1}, {2, 2}}, - }, - }, - } - - r := MazeRuleset{} - for _, test := range tests { - nextState, err := r.CreateNextBoardState(test.prevState, test.moves) - require.NoError(t, err) - require.Equal(t, test.expectedState, nextState) - } -}