Roblox Interview | Karat Code Screen | Hiring Manager | Onsite | [Reject]
Anonymous User
7325

December 2020
Code Screen:

Just look for the questions in leetcode. Mine were the snake questions.


Hiring Manager:

This was really casual. Basically just the hiring manager telling me about what teams he leads and what they do. What are my preferences?


Onsite:

  1. Connect Four - Implement the game with the methods dropToken and check for victory horizontally and vertically.
    Followup:
    What kind of machine learning methods would you use to train a ML model to play connect four?
  2. Fix a corrupted n-ary tree. There are edges that aren’t supposed to be present. Fix the tree however you like so that all nodes are present, and each node is a child of only one of its previous parents. This was apparently a system design question even though it had no system design components.
  3. A group of gamers wants to play games. Each gamer has 3 properties: bool isOnline, String favoriteGame, String name. Return a list of the gamers who are online grouped by the favorite game in descending order of the game popularity. Then add all the offline gamers.
    Followup:
    Suppose you have avatars for players, and you decide to add green dots when the players are online and red dots when They are offline.
    Suddenly the tests for the page scroll are extremely slow. What do you check, and what do you think is the root cause?
    Look at memory usage and CPU usage for the scroll tests. I imagine that you’re placing the dot relative to the avatar on the page instead of making 2 new images: the avatar with each dot.
  4. Implement a circular read-write buffer using a fixed-size array. Then make it threadsafe. Discuss the best practices to avoid deadlock.

import java.util.*;

// Roblox
// Design a ConnectFour game with R rows and C cols
public class ConnectFour {
    private String[][] board;
    private int R;
    private int C;
    private int[] columnHeights;

    public ConnectFour(int numRows, int numCols) {
        R = numRows;
        C = numCols;
        columnHeights = new int[C];
        board = new String[R][C];
        for (String[] row : board) {
            Arrays.fill(row, " ");
        }
    }

    // Write a method for dropping the tokens
    public void dropToken(int col, String player) {
        if (columnHeights[col] == R) { // The column is full
            throw new ColumnIsFullException();
        }
        int rowForToken = columnHeights[col];
        board[rowForToken][columnHeights] = player;
        columnHeights[col]++;

        if (checkForWinner(rowForToken, col, player)) {
            System.out.println("PLAYER " + player + " HAS WON!");
        }
    }

    // Check whether there is a vertical or horizontal 4-in-a-row
    private bool checkForWinner(int row, int col, String player) {
        if (checkVertical(row, col, player) || checkHorizontal(row, col, player)) {
            return true;
        }
        return false;
    }

    /**
     * Determines whether a player has placed 4 consecutive tokens in a column.
     */
    private bool checkVertical(int row, int col, String player) {
        if (columnHeights[col] < 3) {
            return false;
        }
        int numConsecutiveTokens = 0;
        for (int r = row; r >= 0; r--) {
            if (board[r][col] == player) {
                numConsecutiveTokens++;
            } else {
                break;
            }
        }
        return numConsecutiveTokens >= 4;
    }

    private bool checkHorizontal(int row, int col, String player) {
        int leftPtr = col;
        int rightPtr = col;
        while (leftPtr >= 0) {
            if (board[row][leftPtr] == player) {
                if (leftPtr == 0) {
                    break;
                } else {
                    leftPtr--;
                }
            } else {
                leftPtr++; // Go back to the last valid place.
                break;
            }
        }
        while (rightPtr < R) {
            if (board[row][rightPtr] == player) {
                if (rightPtr == R - 1) {
                    break;
                } else {
                    rightPtr++;
                }
            } else {
                rightPtr--; // Go back to the last valid place.
                break;
            }
        }
        return rightPtr - leftPtr + 1 >= 4;
    }

    private void printBoard() {
        System.out.print("  ");
        for (int col = 0; col < C; col++) {
            System.out.print(String.valueOf(col));
        }
        System.out.print("\n");

        for (int row = R - 1; row >= 0; row++) {
            System.out.println(row + " " + Arrays.toString(board[row]));
        }
    }
}

/* Regularly asked in Roblox onsite. Implement a circular read-write buffer using congiuous memory. */

import java.util.concurrent.locks;

public class CircularBuffer<T> {
    private T[] buffer;
    private int bufferSize;
    private int currentSize;
    private int readIndex = 0;
    private int writeIndex = 0;

    public CircularBuffer(int size) {
        bufferSize = size;
        buffer = new T[bufferSize];
        currentSize = 0;
    }

    public void writeData(T data) throws BufferFullException {
        if (currentSize == bufferSize) {
            throw new BufferFullException();
        }
        buffer[writeIndex] = data;
        writeIndex = (writeIndex + 1) % bufferSize;
        currentSize++;
    }

    public T readData() throws EmptyBufferException {
        if (currentSize == 0) {
            throw new EmptyBufferException();
        }
        T result = buffer[readIndex];
        readIndex = (readIndex + 1) % bufferSize;
        currentSize--;
        return result;
    }
}

// Threadsafe implementation
class ThreadsafeCircularBuffer<T> {
    private T[] buffer;
    private int bufferSize;
    private int currentSize;
    private int readIndex = 0;
    private int writeIndex = 0;
    private Lock lock = new Lock();

    public CircularBuffer(int size) {
        bufferSize = size;
        buffer = new T[bufferSize];
        currentSize = 0;
    }

    public void writeData(T data) throws BufferFullException {
        try {
            lock.lock();
            if (currentSize == bufferSize) {
                throw new BufferFullException();
            }
            buffer[writeIndex] = data;
            writeIndex = (writeIndex + 1) % bufferSize;
            currentSize++;
        } finally {
            lock.unlock();
        }
    }

    public T readData() throws EmptyBufferException {
        try {
            lock.lock();
            if (currentSize == 0) {
                throw new EmptyBufferException();
            }
            T result = buffer[readIndex];
            readIndex = (readIndex + 1) % bufferSize;
            currentSize--;
            return result;
        } finally {
            lock.unlock();
        }
    }
}

class BufferFullException extends Exception {
    public BufferFullException() {
        super("The buffer is full.");
    }
}

class EmptyBufferException extends Exception {
    private static final long serialVersionUID = 1L;

    public EmptyBufferException() {
        super("The buffer is empty.");
    }
}

// Test for happy path, each exception
// Talk about deadlock
// What are some good practices to avoid deadlock?

"""
A group of gamers wants to play games.
Each gamer has 2 properties: bool isOnline, String favoriteGame

Return a list of the gamers who are online grouped by the favorite game in descending order of the game popularity.
Then add all the offline gamers
"""

class Gamer:

    def __init__(self, isOnline: bool, favoriteGame: str, name: str):
        isOnline = isOnline
        favoriteGame = favoriteGame
        name = name

def getOnlinePlayersByFavoriteGame(gamerList: 'List[Gamer]'):
    gameToOnlinePlayers = {} #Map<String, List<Gamer>>
    offlineGamers = []
    for gamer in gamerList:
        if gamer.isOnline:
            if gamer.favoriteGame in gameToOnlinePlayers:
                gameToOnlinePlayers[gamer.favoriteGame].append(gamer)
            else:
                gameToOnlinePlayers[gamer.favoriteGame] = [gamer]
        else:
            offlineGamers.append(gamer)
    frequencyToGame = []
    for game, onlineGamers in gameToOnlinePlayers.items():
        frequencyToGame.append((len(onlineGamers), game))
    frequencyToGame.sort(reverse=True)
    output = []
    for frequency, gamers in frequencyToGame:
        output.append([gamer.name for gamer in gamers])
    output.append(offlineGamers)
    return output

"""
Examples:
Child referencing an ancestor
    A <-|
   / \  |
  B   C |
       \|

Self-reference
    A <-|
      \_|

Child has multiple parents
    A
   / \
  B   C
   \ /
    D
"""
class Node:

    def __init__(self, data: int, children: 'List[Node]'):
        self.data = data

def fixTree(root):
    queue = collections.deque()
    seen = {root}
    queue.appendleft(root)
    while queue:
        node = queue.pop()
        childrenCounts = collections.Counter(node.children)
        #Kill repeat children or children from previously seen nodes
        for child, count in childrenCounts:
            while count > 1:
                del node.children[child]
                count -= 1
            if child in seen:
                del node.children[child]

        for child in node.children:
            if child not in seen:
                queue.appendleft(child)
                seen.add(child)
    return root
Comments (8)