Build a game engine

Connect four in three stages: the board and the moves, then a win check that looks only at the disc you just dropped, then a board whose size and target are parameters, with undo.

Should the win check scan the whole board or only the last move?

Only the last move, and you should say why out loud rather than leave it as an optimisation. Before the drop there was no line of four anywhere, because the game would have ended, so any new line has to pass through the disc that just landed. That turns the check from a scan of every cell into four lines through one cell, and each of those lines is short: it stops as soon as the colour changes. On a standard board that is a couple of dozen cell reads per move, whatever the board size, and the argument for why it is complete is one sentence.

Is an Enum worth it when there are only two players?

Yes, and it costs three lines. A player written as the string "X" invites "x", "O" and 0 to mean the same thing somewhere else in the file, and a player written as 1 and 2 turns every comparison into a puzzle for the reader. An Enum gives you one place where the two values are defined, comparison by identity, a name in every traceback, and a type checker that refuses a drop taking a bare string. The same goes double for the status: in progress, won and drawn are three states of one value, and writing them as two booleans lets both be true.

How do I make the board size configurable without over-engineering it?

Do not build it in stage one. Write the two numbers as named constants and use those names everywhere, then when the interviewer asks for a different board, the change is the constructor plus a handful of names. The mistake in the other direction is a config object, a builder, or a factory for two integers and a target: all of them read as over-engineering when the requirement is three parameters with defaults. Defaults on the constructor keep the standard game one call away.

What does undo have to put back, apart from the disc?

The status, the winner and whose turn it is, which is exactly what people forget. A game undone from a win has to be in progress again with no winner, and the next drop has to be allowed, so you want a test that undoes the winning move and then plays another one. Whose turn it is does not need to be saved and restored: it is a function of how many discs have been played, so it can be recomputed from the history in one line.

What are they actually scoring in a game-engine question?

Whether the state and the rules stay separate, whether an illegal move is refused at the boundary with an error that says which rule it broke, and how much has to move when the spec grows. The algorithm is not the point: counting in four directions is not hard, and the interviewer knows it. What they cannot tell from a puzzle question is whether you name things well, whether tests arrive beside the code, and whether the second requirement lands as an addition or as a rewrite.