Simulation

beginner20 min

The category

Some problems do not need an insight. They describe a process — a machine, a game, a set of movement rules — and ask what state it ends in. The solution is to carry out the process. These are simulation problems, and they are a large share of entry-level contest problems.

The difficulty is never conceptual. It is that the statement has five rules and your code implements four of them, or implements the fifth slightly differently. Simulation problems are lost to misreading, not to algorithms.

The core discipline

Translate the statement into code one rule at a time, in the order the statement gives them, and keep the names the statement uses.

Why naming matters here

If the problem says “the cow at position p moves d steps”, write pos and dist, not x and y. When you re-read the statement to check a rule — and you will, several times — matching names let you compare line by line instead of translating in your head each time.

A worked example

A robot starts at position 0 on a number line facing right. You are given a string of commands: F moves one step forward, B moves one step backward, and R reverses its facing direction. Print the final position.

The statement gives three rules. The code has three cases.

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
        String commands = in.readLine().trim();

        int pos = 0;
        int facing = 1;          // +1 is right, -1 is left

        for (int i = 0; i < commands.length(); i++) {
            char c = commands.charAt(i);
            if (c == 'F') {
                pos += facing;
            } else if (c == 'B') {
                pos -= facing;
            } else if (c == 'R') {
                facing = -facing;
            }
        }

        System.out.println(pos);
    }
}

Encoding the direction as +1 or -1 is the small design decision that makes this clean. “Forward” becomes pos += facing regardless of which way the robot faces, so there is no separate case for each direction.

Representing direction

Storing direction as a signed multiplier, or as an index into an offset array, removes duplicated branches. For grid movement, the array form is standard:

// right, down, left, up — turning clockwise is (dir + 1) % 4
int[] dr = {0, 1, 0, -1};
int[] dc = {1, 0, -1, 0};

int r = 0, c = 0, dir = 0;
r += dr[dir];
c += dc[dir];

Turning right becomes dir = (dir + 1) % 4. Turning left becomes dir = (dir + 3) % 4. Both are one line instead of a four-way branch.

Grid simulation

Most simulation problems are on a grid. The pattern is a position, a direction, and a bounds check.

A robot walks forward on an R × C grid. If the next cell is off the grid or blocked, it turns right instead of moving. Report its position after K steps.

int r = 0, c = 0, dir = 0;
int[] dr = {0, 1, 0, -1};
int[] dc = {1, 0, -1, 0};

for (int step = 0; step < k; step++) {
    int nr = r + dr[dir];
    int nc = c + dc[dir];

    if (nr < 0 || nr >= rows || nc < 0 || nc >= cols || grid[nr][nc] == '#') {
        dir = (dir + 1) % 4;
    } else {
        r = nr;
        c = nc;
    }
}

Computing the candidate position into nr and nc before committing is the important habit. It lets you test the move for legality without having to undo it.

The bounds check order

if (nr < 0 || nr >= rows || nc < 0 || nc >= cols || grid[nr][nc] == '#')

The bounds tests must come before the grid access. Java evaluates || left to right and stops at the first true condition, so an out-of-range index is never used. Reordering these — putting grid[nr][nc] first — throws ArrayIndexOutOfBoundsException. This is a frequent runtime error.

When the step count is huge

If the problem says K ≤ 109 steps, you cannot simulate each one. That is the constraint telling you the state must repeat.

A finite amount of state means the process eventually cycles. Find the cycle, then skip ahead with arithmetic.

Cycle detection

  1. Record each state you reach, mapping it to the step number at which you first saw it.
  2. When a state repeats, you have found a cycle: it starts at the first occurrence and its length is the difference between the two step numbers.
  3. Compute how many steps remain after reaching the cycle start, reduce that modulo the cycle length, and simulate only that remainder.
Map<String, Integer> firstSeen = new HashMap<>();
int step = 0;
while (step < k) {
    String state = encode(r, c, dir);
    if (firstSeen.containsKey(state)) {
        int cycleStart = firstSeen.get(state);
        int cycleLen = step - cycleStart;
        int remaining = (k - step) % cycleLen;
        for (int i = 0; i < remaining; i++) advance();
        break;
    }
    firstSeen.put(state, step);
    advance();
    step++;
}

The state must include everything that affects the future — for the walking robot, that is position and facing direction. Leaving out the direction produces a wrong cycle length, and the answer will be wrong only on large inputs, which is a hard bug to find later.

The checklist

Before submitting a simulation problem

  • Re-read the statement and check off each rule against your code.
  • Confirm the order of operations. “Move then collect” and “collect then move” give different answers.
  • Check what happens on step 0 and on the final step. Does the count include both endpoints?
  • Check the tie-breaking rule. If two things happen at once, the statement says which wins.
  • Confirm bounds tests precede array accesses.
  • Compare K against the constraint table. If K is large, look for a cycle.

Practice

Implement each directly from the description. Resist the urge to look for a shortcut — that is the point of the category.

  1. A light switch starts off. Given a string of T (toggle) and N (no-op), print the final state.
  2. Three cups sit in positions 1, 2, 3 with a ball under cup 1. Given a list of swaps, each naming two positions, print where the ball ends up.
  3. On an R × C grid, a robot starts at the top-left facing right and moves forward, turning right whenever the next cell would be off the grid or already visited. Print how many cells it visits before it can no longer move.
  4. A number starts at n. Each step: halve it if even, otherwise triple it and add one. Print how many steps until it reaches 1.
  5. Same as problem 3, but the grid is 3 × 3 and you must report the position after exactly 109 steps.
Hints
  1. A boolean, flipped on each T.
  2. Track the ball’s position; on each swap, if the ball is at either named position, move it to the other.
  3. A boolean[][] visited. The loop ends when all four directions are blocked.
  4. Straightforward loop. Use long — the intermediate values grow well past int.
  5. The grid has 9 cells and 4 directions, so at most 36 distinct states. It must cycle quickly. Use the cycle-detection pattern above, and make sure your encoded state includes the direction and the visited set.

Next