Design a Snake game that is played on a grid of width width
and height height
. Play the game online if you are not familiar with the game.
The snake is initially positioned at the top-left cell (0, 0)
with a length of 1
unit.
You are given a list of food's positions in the format of (row, col)
. The food is guaranteed to be placed on empty cells.
Each time the snake eats the food, its length and score increase by 1
.
When the snake moves, the first cell the snake occupies is emptied, and the snake moves to the new cell. The snake dies if it moves out of bounds (not within width x height)
or moves into a cell that is part of its body.
Implement the SnakeGame
class:
SnakeGame(int width, int height, int[][] food)
Initializes the object with the width
and height
of the game board and the food
's positions.int move(String direction)
Moves the snake. If the snake moves successfully, return the score after the move. Otherwise, return -1
.Example 1:
Input:
["SnakeGame", "move", "move", "move", "move", "move", "move"]
[[3, 2, [[1, 2], [0, 1]]], ["R"], ["D"], ["R"], ["U"], ["L"], ["U"]]
Output:
[null, 0, 0, 1, 1, 2, -1]
Explanation:
SnakeGame snakeGame = new SnakeGame(3, 2, [[1, 2], [0, 1]]);
snakeGame.move("R"); // return 0 (snake moves right, no food).
snakeGame.move("D"); // return 0 (snake moves down, no food).
snakeGame.move("R"); // return 1 (snake moves right, eats the first food. The length of snake is now 2).
snakeGame.move("U"); // return 1 (snake moves up, no food)
snakeGame.move("L"); // return 2 (snake moves left, eats the second food. The length of snake is now 3).
snakeGame.move("U"); // return -1 (game over because snake collides with border).
Constraints:
1 <= width, height <= 104
0 <= food.length <= 2000
food[i].length == 2
0 <= food[i][0] < height
0 <= food[i][1] < width
direction.length == 1
direction
is 'U'
, 'D'
, 'L'
, or 'R'
.104
calls will be made to move
.When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The brute force strategy for the Snake game involves simulating every possible move the snake can make at each step. We essentially explore every path the snake could take until it either finds food or runs into itself or a wall. This method ensures we find a solution, if one exists, but is not very efficient.
Here's how the algorithm would work step-by-step:
def design_snake_game_brute_force(grid_height, grid_width, food_positions):
snake_initial_position = [0, 0]
snake_body = [snake_initial_position[:]]
food_index = 0
def is_valid_move(new_head_position, current_snake_body):
row, col = new_head_position
if row < 0 or row >= grid_height or col < 0 or col >= grid_width:
return False
# Check if the snake hits itself
for segment in current_snake_body[:-1]:
if segment == new_head_position:
return False
return True
def find_path(current_snake_body, current_food_index):
if current_food_index == len(food_positions):
return True
food_position = food_positions[current_food_index]
head_row, head_col = current_snake_body[-1]
possible_moves = [
[head_row - 1, head_col], # Up
[head_row + 1, head_col], # Down
[head_row, head_col - 1], # Left
[head_row, head_col + 1] # Right
]
for new_head_position in possible_moves:
if is_valid_move(new_head_position, current_snake_body):
new_snake_body = current_snake_body + [new_head_position[:]]
#Check if the snake eats food
if new_head_position == food_position:
#Snake ate food, increment food
if find_path(new_snake_body, current_food_index + 1):
return True
else:
#If not eating food, remove the tail
if find_path(new_snake_body[1:], current_food_index):
return True
return False
#Start recursive call from the snake's initial position
return find_path(snake_body, food_index)
The Snake game design involves carefully managing the snake's body and movement on a game board. The optimal approach utilizes a data structure to efficiently track the snake's location and food placement, enabling quick updates and collision detection. By employing these strategies, we prevent unnecessary checks and calculations, leading to a faster and more responsive game.
Here's how the algorithm would work step-by-step:
import random
class SnakeGame:
def __init__(self, width, height, food_positions):
self.width = width
self.height = height
self.snake = [(0, 0)]
self.food_positions = food_positions
self.food_index = 0
self.score = 0
def move(self, direction):
head_row, head_col = self.snake[0]
if direction == "U":
new_head = (head_row - 1, head_col)
elif direction == "D":
new_head = (head_row + 1, head_col)
elif direction == "L":
new_head = (head_row, head_col - 1)
elif direction == "R":
new_head = (head_row, head_col + 1)
else:
return False
# Check for game over conditions.
if (new_head[0] < 0 or new_head[0] >= self.height or
new_head[1] < 0 or new_head[1] >= self.width or
new_head in self.snake):
return False
self.snake.insert(0, new_head)
# Check if the snake ate the food.
if self.food_index < len(self.food_positions) and new_head == self.food_positions[self.food_index]:
self.score += 1
self.food_index += 1
# Increase snake length on eating
else:
self.snake.pop()
# Maintain snake length
return True
Case | How to Handle |
---|---|
Snake initializes outside the board boundaries. | Initialize the snake within the board dimensions, throwing an error if initial position is invalid. |
Food spawns outside the board boundaries. | Ensure food generation respects board limits; re-generate food if necessary. |
Snake grows so large it fills the entire board. | Handle this as a winning condition, ending the game gracefully. |
Initial snake length is longer than the board dimension. | Constrain initial snake length to a reasonable size relative to the board dimensions, or throw an exception. |
Game board has zero width or zero height. | Throw an IllegalArgumentException as the game board must have positive dimensions. |
Snake tries to move into its own body immediately after initialization. | Ensure initial snake placement and movement direction avoids immediate self-collision. |
Game requests an invalid move (e.g., moving backwards without sufficient length). | Disallow invalid moves, either ignoring them or ending the game. |
Large number of food objects exist simultaneously and their locations are stored. | Use a data structure that efficiently handles additions/removals, considering performance implications with larger food quantities. |