353. Design Snake Game

T: O(1)
S: O(mn + food_len)
```java
class SnakeGame {
    private Set<Integer> set;
    private LinkedList<Integer> body;
    private LinkedList<Integer> food;
    private Map<String, int[]> directionMap;
    private int m;
    private int n;

    public SnakeGame(int width, int height, int[][] food) {
        this.m = height;
        this.n = width;
        this.set = new HashSet<>();
        this.set.add(encode(0, 0));
        
        this.body = new LinkedList<>();
        this.body.add(encode(0, 0));

        this.directionMap = new HashMap<>();
        this.directionMap.put("R", new int[] {0, 1});
        this.directionMap.put("L", new int[] {0, -1});
        this.directionMap.put("U", new int[] {-1, 0});
        this.directionMap.put("D", new int[] {1, 0});
        
        this.food = new LinkedList<>();
        for (int[] f : food) {
            this.food.add(encode(f[0], f[1]));
        }
    }
    
    public int move(String direction) {
        int head = body.getFirst();
        int[] dir = directionMap.get(direction);
        int x = head/n + dir[0]; // 5/3 = 1, 4/3 = 1
        int y = head%n + dir[1]; // 2, 1
        int newPositionCode = encode(x, y);
        //System.out.println("setszie"+set.size()+ "," + set.toString());
        //System.out.println("d:"+direction+ ","+x + "," + y);
        if (x < 0 || x >= m || y < 0 || y >= n) { // can't check tail here, because tail is still there
            return -1;
        }

        body.addFirst(newPositionCode);
        if (!food.isEmpty() && newPositionCode == food.getFirst()) {
            food.removeFirst();
        } else {
            int removed = body.removeLast();
            //System.out.println("removed:"+removed);
            set.remove(removed);
        }
        //System.out.println("newPositionCode:"+newPositionCode);
        if (set.contains(newPositionCode)) { // after we remove tail step, we can check the body is hitted by snake itself
            return -1;
        }
        set.add(newPositionCode);

        return body.size() - 1;
    }
    
    private int encode(int x, int y) {
        return x*n + y;
    }
}

/**
 * Your SnakeGame object will be instantiated and called as such:
 * SnakeGame obj = new SnakeGame(width, height, food);
 * int param_1 = obj.move(direction);


1. body: head tail -> if eat food, no need to delete tail , vise versa
2. if this move not hit yourself, then we can add this move to head


T: O(1)
S: O(mn + food_len)


[[3,3,[[2,0],[0,0],[0,2],[2,2]]],

["D"],["D"],["R"],["U"],["U"],["L"],["D"],["R"],["R"],["U"],["L"],["D"]]
        x     x.    x.    x.    x     x     x     x      x
  012 
0  ..   
1  .x  
2     

   ..
    .


// after we remove tail step, we can check the body is hitted by snake itself

ex:
last step-> 
["D"],["D"],["R"],["U"],["U"],["L"],["D"],["R"],["R"],["U"],["L"],["D"]]


   ["L"] , in here set still has set_size=4,[1, 2, 4, 5] means [0,1] [0,2], [1,1], [1,2]

  012    you can see, there is still a 1,1 is mark with ?, in here we haven't check, because if we eat a food, we can keep it
0  ..    if not we will remove it
1  ?.  
2   

to   ["D"]] ok, no food so remove it [1,1] (the last one), so now set become [0,1] [0,2], [1,2](?) the virtual tail
             then we can check is new position [1,1] in set? not in set, so we can add it to set  
  012        then we have new position [1,1], so add it to set again -> become [0,1] [0,2], [1,1], [1,2] again
0  ..   
1  .?  
2   



 */
```

Last updated