Board Simulation: Bottom-Insert Pieces with Uniform Row and Column Detection
Company: Jane Street
Role: Machine Learning Engineer
Category: Coding & Algorithms
Difficulty: medium
Interview Round: Technical Screen
## Board Simulation: Bottom-Insert Pieces with Uniform Row and Column Detection
You must simulate a 2-D game board with `R` rows and `C` columns. Each cell is either **empty** or holds a piece of one of **two types**, `"A"` or `"B"`. Rows are numbered from `0` (the **bottom** row) to `R - 1` (the top row); columns are numbered `0` to `C - 1`.
### Insertion rule
Pieces are always inserted at the **bottom** of a column:
- When a piece `p` is inserted into column `c`, every piece already in column `c` shifts **up** by one row, and the new piece occupies the bottom cell `(row 0, column c)`.
- Because pieces only enter from the bottom, the pieces in any column always occupy a contiguous block of rows starting at row `0`.
- If column `c` already holds `R` pieces (the column is **full**), the insertion **fails** and the board state does not change.
### Task
Process a sequence of insertion operations. Each operation is a pair `(c, p)` meaning "insert a piece of type `p` into column `c`". After processing **each** operation (whether it succeeded or failed), report three values describing the board state at that moment:
1. `ok` — `true` if the insertion succeeded, `false` if it failed because column `c` was already full.
2. `uniform_column` — `true` if **any** column is completely filled (all `R` cells occupied) with pieces of the **same type**; otherwise `false`.
3. `uniform_row` — `true` if **any** row is completely filled (all `C` cells in that row occupied) with pieces of the **same type**; otherwise `false`.
Return the list of these triples, one per operation, in order.
### Input
- An integer `R` — the number of rows (`1 <= R <= 50`).
- An integer `C` — the number of columns (`1 <= C <= 50`).
- A list `ops` of operations, where each operation is `[c, p]` with `0 <= c < C` and `p` equal to the string `"A"` or `"B"` (`1 <= len(ops) <= 10^4`).
### Output
A list of `len(ops)` triples `[ok, uniform_column, uniform_row]` (booleans), where the `i`-th triple describes the board immediately after processing the `i`-th operation.
### Example
Input:
```
R = 2, C = 2
ops = [[0, "A"], [1, "A"], [0, "B"], [1, "B"], [1, "A"]]
```
Output:
```
[[true, false, false],
[true, false, true],
[true, false, false],
[true, false, true],
[false, false, true]]
```
Explanation (rows listed bottom-up; `.` = empty):
1. `add(0, "A")`: column 0 becomes `[A]`. Board: row 0 = `A .`. No full column; row 0 is not fully occupied. -> `[true, false, false]`
2. `add(1, "A")`: column 1 becomes `[A]`. Row 0 = `A A` — fully occupied, all type `A`, so a uniform row exists. Neither column is full. -> `[true, false, true]`
3. `add(0, "B")`: the new `B` enters at the bottom of column 0 and pushes the existing `A` up, so column 0 is `[B, A]` (row 0 = `B`, row 1 = `A`). Column 0 is full but mixed. Row 0 = `B A` (mixed); row 1 = `A .` (not fully occupied). -> `[true, false, false]`
4. `add(1, "B")`: column 1 becomes `[B, A]`. Both columns are full but mixed. Row 0 = `B B` (uniform); row 1 = `A A` (uniform). -> `[true, false, true]`
5. `add(1, "A")`: column 1 is full, so the insertion fails and the board is unchanged. -> `[false, false, true]`
### Notes
- A column counts as uniform only when **all** `R` of its cells are occupied by the same piece type; a partially filled column never counts, even if its current pieces all match.
- A row counts as uniform only when **all** `C` of its cells are occupied by the same piece type. Row `r` is fully occupied exactly when every column currently holds more than `r` pieces.
- Failed insertions must leave the board — and therefore the two uniformity flags — unchanged from the previous state.
- A brute-force `O(R * C)` scan after each operation is acceptable at these constraints, but an incremental solution that updates per-column and per-row bookkeeping in `O(R)` or better per operation is preferred.
Quick Answer: This question evaluates the ability to simulate a 2-D game board and maintain dynamic state invariants, testing array manipulation, state-tracking, and detection of uniform rows and columns under bottom-insert semantics.
You simulate a 2-D game board with `R` rows and `C` columns. Each cell is empty or holds a piece of type `"A"` or `"B"`. Rows are numbered from `0` (bottom) to `R-1` (top); columns from `0` to `C-1`.
**Insertion rule.** A piece is always inserted at the bottom of a column. When piece `p` is inserted into column `c`, every piece already in column `c` shifts up by one row and the new piece occupies the bottom cell `(row 0, column c)`. Pieces in any column therefore always occupy a contiguous block of rows starting at row `0`. If column `c` already holds `R` pieces (it is full), the insertion fails and the board is unchanged.
**Task.** Process a sequence of operations `ops`, each an `[c, p]` pair meaning "insert a piece of type `p` into column `c`". After processing each operation (whether it succeeded or failed), report a triple `[ok, uniform_column, uniform_row]`:
1. `ok` — `true` if the insertion succeeded, `false` if column `c` was already full.
2. `uniform_column` — `true` if any column is completely filled (all `R` cells) with pieces of the same type; else `false`.
3. `uniform_row` — `true` if any row is completely filled (all `C` cells) with pieces of the same type; else `false`.
Return the list of these triples, one per operation, in order.
**Input.** `R` (1 <= R <= 50), `C` (1 <= C <= 50), and `ops` (1 <= len(ops) <= 10^4) where each op is `[c, p]` with `0 <= c < C` and `p` equal to `"A"` or `"B"`.
**Output.** A list of `len(ops)` boolean triples `[ok, uniform_column, uniform_row]`, the i-th describing the board immediately after the i-th operation.
**Notes.**
- A column counts as uniform only when all `R` of its cells are occupied by the same type; a partially filled column never counts.
- A row counts as uniform only when all `C` of its cells are occupied by the same type. Row `r` is fully occupied exactly when every column currently holds more than `r` pieces.
- Failed insertions leave the board — and both uniformity flags — unchanged from the previous state.
Constraints
- 1 <= R <= 50
- 1 <= C <= 50
- 1 <= len(ops) <= 10^4
- Each op is [c, p] with 0 <= c < C and p in {"A", "B"}
- Inserting into a full column (already holding R pieces) fails and leaves the board unchanged
Examples
Input: (2, 2, [[0, 'A'], [1, 'A'], [0, 'B'], [1, 'B'], [1, 'A']])
Expected Output: [[True, False, False], [True, False, True], [True, False, False], [True, False, True], [False, False, True]]
Explanation: The worked example. Op2 fills row 0 with A A (uniform row). Op3 fills column 0 as [B, A] (full but mixed, so not a uniform column) and breaks the uniform row. Op4 makes row 0 = B B (uniform). Op5 targets full column 1, so it fails; the board and flags stay as after op4.
Input: (1, 1, [[0, 'A'], [0, 'B']])
Expected Output: [[True, True, True], [False, True, True]]
Explanation: A 1x1 board. The first A fills the only cell: the single column is full and uniform, and row 0 is full and uniform. The second insert hits a full column, so it fails and the flags remain true.
Hints
- Model each column as a stack that grows from the bottom: inserting a piece puts it at row 0 and pushes existing pieces up, so column c holds a contiguous block of rows 0..len-1.
- A column is uniform only when it is completely full (length == R) and every one of its pieces is the same type — a partially filled matching column does not count.
- Row r is fully occupied only when every column currently has more than r pieces; check that first, then verify all C cells in that row share one type.
- On a failed insertion (column already full), do not touch the board — re-report the same uniformity flags as before. A brute-force O(R*C) rescan after each op is fast enough here.