Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Coding Interview Patterns  Module 33 – Pattern recognition and mixed practice

Revision: re-solve queues and pattern notes

Re-solve missed coding problems 1, 3, 7 and 21 days later, keep one-page notes of your own templates and bugs, and plan the two weeks before an interview.

  • Beginner
  • 25 minutes
  • Examples run with Python 3.14.8 and Pyodide 314.0.7
  • By MySmartCoPilot
Free intro, Pro: 5 of 25 minutes free
Free: Why spaced re-solves help more than rereading. With a pass: the other 7 sections and 3 interview answers, about 20 minutes.

What you will learn

  • Schedule the re-solves of a missed problem, and start them again after a failed re-solve
  • Decide whether a re-solve counts as clean, and log it in one line
  • Write a one-page pattern note whose bugs each come with a test that catches them
  • Plan the last two weeks before an interview, day by day
  • Build a re-solve planner that keeps a daily cap

Before you start

What you’ll learn · Before you start

What you will learn

  • Schedule the re-solves of a missed problem, and start them again after a failed re-solve
  • Decide whether a re-solve counts as clean, and log it in one line
  • Write a one-page pattern note whose bugs each come with a test that catches them
  • Plan the last two weeks before an interview, day by day
  • Build a re-solve planner that keeps a daily cap

Before you start

On this page

A problem you solved once, after a hint, may be one you cannot solve alone a week later. A revision system is spaced repetition for coding practice: a re-solve queue brings back every problem you missed 1, 3, 7 and 21 days after the miss, to be solved again from a blank editor, and a re-solve that fails starts the steps again.

Its second part, pattern notes, keeps one page per pattern, written from your own solutions: the template you code from, the words in a problem that point to the pattern, and the bugs you made. Start both in your first week of practice and keep them until the interview.

Why spaced re-solves help more than rereading

Solving a new problem feels like progress, and rereading an old solution feels like revision, but neither shows whether you could solve that problem tomorrow without help. Memory research points to two habits that make what you learn last.

The first is the spacing effect: The finding that practice spread over separate sessions is remembered better than the same practice crammed into one session.: practice spread over separate sessions is remembered better than the same practice crammed into one. A meta-analysis pooled 839 measurements from 317 experiments, and the later the final test came, the longer the gap between sessions that worked best. The second is retrieval practice: Learning by recalling something from memory, such as solving a problem again from a blank editor, rather than reading it again.: students who wrote down what they remembered of a passage recalled more of it two days and a week later than students who read it again, although rereading did better five minutes later. A re-solve is both habits at once, a test spaced out over weeks.

The steps of 1, 3, 7 and 21 days are a planning choice, not a research finding: those studies used word lists, facts and prose, and found no single best schedule. They fit a month of preparation, and each gap is longer than the last, so a problem costs less once it sticks. Step through one problem that fails its third re-solve:

Interactive · stage by stage One missed problem through the re-solve queue Step through the problem missed on day 2 in the program below. Its third re-solve fails on day 9, and the steps start again from that day.
One missed problem through the re-solve queue, step 6 of 6. Showing Missed on day d (d = 9), Wait for the next step (step 4 on day 30), Re-solve, blank editor (day 30), Clean re-solve? (yes: step 4), Leaves the queue. In focus: Leaves the queue.yes: next stepno: d is that dayMissed on day dd = 9Wait for the next stepstep 4 on day 30Re-solve, blank editorday 30Clean re-solve?yes: step 4Leaves the queueafter its fourth stepleaves on day 30

The drawing shows the whole scene; its 6 steps are listed below.

Every step as text
  1. Day 2, the miss. The first attempt needed a hint on day 2, so the problem joins the queue with d = 2. If every re-solve is clean, they fall on days 3, 5, 9 and 23.
  2. Day 3, step 1. Day 3 is 2 + 1. The problem is solved again from a blank editor, inside its time box and with no hint, so step 2 is next, on day 5, which is 2 + 3.
  3. Day 5, step 2. Clean again. Step 3 is due on day 9, a week after the miss, because 2 + 7 is 9.
  4. Day 9, step 3 fails. On day 9 the re-solve needs a hint. That makes it a new miss, so d becomes 9 and the problem starts again at step 1, due on day 10.
  5. Days 10, 12 and 16. Counted from d = 9, the steps fall on days 10, 12 and 16, and all three re-solves are clean. Step 4 is now due on day 30, 21 days after the failure.
  6. Day 30, step 4. The fourth clean re-solve in a row, on day 30, takes the problem out of the queue. Without the failure it would have left on day 23, after 4 re-solves instead of 7.

The program walks the same problem twice, with every re-solve clean and with the failure on day 9, and all 14 languages printed the lines under the tabs:

One program · 14 languages The same missed problem twice: all clean, then a failed third re-solve

Python 3.14 · main.py

STEPS = (1, 3, 7, 21)  # days after the latest miss: a planning choice


def walk(missed_on: int, clean: list[bool]) -> None:
    """Print one problem's way through the queue; clean[i] is re-solve i's outcome."""
    print(f"day {missed_on:2}  first attempt missed")
    miss, step, day, done, failed = missed_on, 0, missed_on, 0, 0
    for ok in clean:
        day = miss + STEPS[step]
        done += 1
        head = f"day {day:2}  re-solve {step + 1} of {len(STEPS)}"
        if not ok:  # a failed re-solve is a new miss: the steps start again
            failed += 1
            miss, step = day, 0
            print(f"{head}  failed  next on day {miss + STEPS[0]}, from step 1")
        elif step + 1 < len(STEPS):
            step += 1
            print(f"{head}  clean   next on day {miss + STEPS[step]}")
        else:
            print(f"{head}  clean   leaves the queue")
            break
    print(f"{done} re-solves, {failed} failed, out of the queue on day {day}")


walk(2, [True, True, True, True])
print()
walk(2, [True, True, False, True, True, True, True])

In Python: STEPS is a tuple, so nothing in the program can change the steps by mistake, and f"{day:2}" pads each day to two characters, which keeps the column of days straight.

Recorded with CPython 3.14.8 on macOS 26 arm64. It runs in your browser with Pyodide 314.0.7 (CPython 3.14.2). To run it yourself: python3 main.py

JavaScript Node 24 · main.mjs

const STEPS = [1, 3, 7, 21]; // days after the latest miss: a planning choice

const pad = (n) => String(n).padStart(2);

// Print one problem's way through the queue; clean[i] is re-solve i's outcome.
function walk(missedOn, clean) {
  console.log(`day ${pad(missedOn)}  first attempt missed`);
  let miss = missedOn;
  let step = 0;
  let day = missedOn;
  let done = 0;
  let failed = 0;
  for (const ok of clean) {
    day = miss + STEPS[step];
    done += 1;
    const head = `day ${pad(day)}  re-solve ${step + 1} of ${STEPS.length}`;
    if (!ok) {
      // A failed re-solve is a new miss: the steps start again.
      failed += 1;
      miss = day;
      step = 0;
      console.log(`${head}  failed  next on day ${miss + STEPS[0]}, from step 1`);
    } else if (step + 1 < STEPS.length) {
      step += 1;
      console.log(`${head}  clean   next on day ${miss + STEPS[step]}`);
    } else {
      console.log(`${head}  clean   leaves the queue`);
      break;
    }
  }
  console.log(`${done} re-solves, ${failed} failed, out of the queue on day ${day}`);
}

walk(2, [true, true, true, true]);
console.log();
walk(2, [true, true, false, true, true, true, true]);

Recorded with Node.js 24.21.0 LTS (V8 13.6) on macOS 26 arm64. It runs in your browser with QuickJS (quickjs-emscripten 0.32.0). To run it yourself: node main.mjs

Java 25 LTS · Main.java

public class Main {
  static final int[] STEPS = {1, 3, 7, 21}; // days after the latest miss: a planning choice

  // Print one problem's way through the queue; clean[i] is re-solve i's outcome.
  static void walk(int missedOn, boolean[] clean) {
    System.out.println("day " + pad(missedOn) + "  first attempt missed");
    int miss = missedOn;
    int step = 0;
    int day = missedOn;
    int done = 0;
    int failed = 0;
    for (boolean ok : clean) {
      day = miss + STEPS[step];
      done++;
      String head = "day " + pad(day) + "  re-solve " + (step + 1) + " of " + STEPS.length;
      if (!ok) {
        // A failed re-solve is a new miss: the steps start again.
        failed++;
        miss = day;
        step = 0;
        System.out.println(head + "  failed  next on day " + (miss + STEPS[0]) + ", from step 1");
      } else if (step + 1 < STEPS.length) {
        step++;
        System.out.println(head + "  clean   next on day " + (miss + STEPS[step]));
      } else {
        System.out.println(head + "  clean   leaves the queue");
        break;
      }
    }
    System.out.println(done + " re-solves, " + failed + " failed, out of the queue on day " + day);
  }

  static String pad(int n) {
    return n < 10 ? " " + n : String.valueOf(n);
  }

  public static void main(String[] args) {
    walk(2, new boolean[] {true, true, true, true});
    System.out.println();
    walk(2, new boolean[] {true, true, false, true, true, true, true});
  }
}

In Java: The lines are joined with +, so no format string and no locale is involved; pad adds the space that %2d would add in printf.

Recorded with OpenJDK 25.0.4.1 LTS (Eclipse Temurin) on macOS 26 arm64. It runs in your browser with a TeaVM 0.16.0 build in QuickJS (quickjs-emscripten 0.32.0). To run it yourself: java Main.java

C++23 · main.cpp

#include <array>
#include <cstddef>
#include <format>
#include <print>
#include <span>
#include <string>

// Days after the latest miss: a planning choice.
constexpr std::array<int, 4> kSteps = {1, 3, 7, 21};

// Print one problem's way through the queue; clean[i] is re-solve i's outcome.
void walk(int missed_on, std::span<const bool> clean) {
  std::println("day {:2}  first attempt missed", missed_on);
  int miss = missed_on;
  std::size_t step = 0;
  int day = missed_on;
  int done = 0;
  int failed = 0;
  for (const bool ok : clean) {
    day = miss + kSteps[step];
    ++done;
    const std::string head = std::format("day {:2}  re-solve {} of {}", day,
                                         step + 1, kSteps.size());
    if (!ok) {
      // A failed re-solve is a new miss: the steps start again.
      ++failed;
      miss = day;
      step = 0;
      std::println("{}  failed  next on day {}, from step 1", head,
                   miss + kSteps[0]);
    } else if (step + 1 < kSteps.size()) {
      ++step;
      std::println("{}  clean   next on day {}", head, miss + kSteps[step]);
    } else {
      std::println("{}  clean   leaves the queue", head);
      break;
    }
  }
  std::println("{} re-solves, {} failed, out of the queue on day {}", done,
               failed, day);
}

int main() {
  walk(2, std::array{true, true, true, true});
  std::println();
  walk(2, std::array{true, true, false, true, true, true, true});
}

In C++: std::span<const bool> lets walk read an array of any length without copying it, and std::format builds the start of each line once for the three cases.

Recorded with Apple clang 21.0.0 (C++23) and GCC 16.2.0 on macOS 26 arm64. It runs in your browser with Clang in WebAssembly (YoWASP) 22.0.0-git20542-10. To run it yourself: clang++ -std=c++23 -O2 -Wall -Wextra -Werror -pedantic -fsanitize=address,undefined main.cpp -o main && ./main

C# 14 · .NET 10 · app.cs

using System.Globalization;

CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;

Walk(2, [true, true, true, true]);
Console.WriteLine();
Walk(2, [true, true, false, true, true, true, true]);

// Print one problem's way through the queue; clean[i] is re-solve i's outcome.
static void Walk(int missedOn, bool[] clean)
{
    int[] steps = [1, 3, 7, 21]; // days after the latest miss: a planning choice
    Console.WriteLine($"day {missedOn,2}  first attempt missed");
    int miss = missedOn, step = 0, day = missedOn, done = 0, failed = 0;
    foreach (bool ok in clean)
    {
        day = miss + steps[step];
        done++;
        string head = $"day {day,2}  re-solve {step + 1} of {steps.Length}";
        if (!ok)
        {
            // A failed re-solve is a new miss: the steps start again.
            failed++;
            miss = day;
            step = 0;
            Console.WriteLine($"{head}  failed  next on day {miss + steps[0]}, from step 1");
        }
        else if (step + 1 < steps.Length)
        {
            step++;
            Console.WriteLine($"{head}  clean   next on day {miss + steps[step]}");
        }
        else
        {
            Console.WriteLine($"{head}  clean   leaves the queue");
            break;
        }
    }
    Console.WriteLine($"{done} re-solves, {failed} failed, out of the queue on day {day}");
}

In C#: A static local function cannot read the variables of the top-level statements, so the steps live inside Walk; {day,2} pads the day to two characters.

Recorded with C# 14 on .NET 10.0.12 (SDK 10.0.401) on macOS 26 arm64. It runs in your browser with the .NET runtime for WebAssembly 10.0.12. To run it yourself: dotnet run app.cs

C23 · main.c

#include <stddef.h>
#include <stdio.h>

// Days after the latest miss: a planning choice.
static const int steps[] = {1, 3, 7, 21};
enum { STEP_COUNT = sizeof steps / sizeof steps[0] };

// Print one problem's way through the queue; clean[i] is re-solve i's outcome.
static void walk(int missed_on, const bool clean[], size_t n) {
  printf("day %2d  first attempt missed\n", missed_on);
  int miss = missed_on;
  int step = 0;
  int day = missed_on;
  int done = 0;
  int failed = 0;
  for (size_t i = 0; i < n; i++) {
    day = miss + steps[step];
    done++;
    printf("day %2d  re-solve %d of %d", day, step + 1, STEP_COUNT);
    if (!clean[i]) {
      // A failed re-solve is a new miss: the steps start again.
      failed++;
      miss = day;
      step = 0;
      printf("  failed  next on day %d, from step 1\n", miss + steps[0]);
    } else if (step + 1 < STEP_COUNT) {
      step++;
      printf("  clean   next on day %d\n", miss + steps[step]);
    } else {
      printf("  clean   leaves the queue\n");
      break;
    }
  }
  printf("%d re-solves, %d failed, out of the queue on day %d\n", done, failed,
         day);
}

int main(void) {
  static const bool all_clean[] = {true, true, true, true};
  static const bool third_fails[] = {true, true, false, true, true, true, true};
  walk(2, all_clean, sizeof all_clean / sizeof all_clean[0]);
  printf("\n");
  walk(2, third_fails, sizeof third_fails / sizeof third_fails[0]);
  return 0;
}

In C: Where the other languages build the start of each line as a string, C prints it with its own printf before the branch, and each array of outcomes comes with its length.

Recorded with Apple clang 21.0.0 (C23) and GCC 16.2.0 on macOS 26 arm64. It runs in your browser with Clang in WebAssembly (YoWASP) 22.0.0-git20542-10. To run it yourself: clang -std=c23 -O2 -Wall -Wextra -Werror -pedantic -fsanitize=address,undefined main.c -o main && ./main

PHP 8.5 · main.php

<?php

declare(strict_types=1);

const STEPS = [1, 3, 7, 21]; // days after the latest miss: a planning choice

/**
 * Print one problem's way through the queue; $clean[$i] is re-solve $i's outcome.
 *
 * @param list<bool> $clean
 */
function walk(int $missedOn, array $clean): void
{
    printf("day %2d  first attempt missed\n", $missedOn);
    $miss = $missedOn;
    $step = 0;
    $day = $missedOn;
    $done = 0;
    $failed = 0;
    foreach ($clean as $ok) {
        $day = $miss + STEPS[$step];
        $done++;
        $head = sprintf("day %2d  re-solve %d of %d", $day, $step + 1, count(STEPS));
        if (!$ok) {
            // A failed re-solve is a new miss: the steps start again.
            $failed++;
            $miss = $day;
            $step = 0;
            printf("%s  failed  next on day %d, from step 1\n", $head, $miss + STEPS[0]);
        } elseif ($step + 1 < count(STEPS)) {
            $step++;
            printf("%s  clean   next on day %d\n", $head, $miss + STEPS[$step]);
        } else {
            printf("%s  clean   leaves the queue\n", $head);
            break;
        }
    }
    printf("%d re-solves, %d failed, out of the queue on day %d\n", $done, $failed, $day);
}

walk(2, [true, true, true, true]);
echo "\n";
walk(2, [true, true, false, true, true, true, true]);

Recorded with PHP 8.5.11 (CLI) on macOS 26 arm64. It runs in your browser with php-wasm 0.2.0 (PHP 8.5.2). To run it yourself: php main.php

Rust 1.99 · main.rs

// Days after the latest miss: a planning choice.
const STEPS: [u32; 4] = [1, 3, 7, 21];

/// Print one problem's way through the queue; `clean[i]` is re-solve i's outcome.
fn walk(missed_on: u32, clean: &[bool]) {
    println!("day {missed_on:2}  first attempt missed");
    let (mut miss, mut step, mut day, mut done, mut failed) = (missed_on, 0, missed_on, 0, 0);
    for &ok in clean {
        day = miss + STEPS[step];
        done += 1;
        let head = format!("day {day:2}  re-solve {} of {}", step + 1, STEPS.len());
        if !ok {
            // A failed re-solve is a new miss: the steps start again.
            failed += 1;
            miss = day;
            step = 0;
            println!(
                "{head}  failed  next on day {}, from step 1",
                miss + STEPS[0]
            );
        } else if step + 1 < STEPS.len() {
            step += 1;
            println!("{head}  clean   next on day {}", miss + STEPS[step]);
        } else {
            println!("{head}  clean   leaves the queue");
            break;
        }
    }
    println!("{done} re-solves, {failed} failed, out of the queue on day {day}");
}

fn main() {
    walk(2, &[true, true, true, true]);
    println!();
    walk(2, &[true, true, false, true, true, true, true]);
}

In Rust: for &ok in clean copies each bool out of the slice, and step is a usize because it indexes the array. Rust has no ++, so the counters use += 1.

Recorded with rustc 1.99.0 (edition 2024) on macOS 26 arm64. It runs in your browser with a WebAssembly build on the WASI runner (browser_wasi_shim 0.4.2). To run it yourself: rustc --edition 2024 -O main.rs && ./main

TypeScript 6.0 · main.ts

const STEPS: readonly number[] = [1, 3, 7, 21]; // days after the latest miss: a planning choice

const pad = (n: number): string => String(n).padStart(2);

// Print one problem's way through the queue; clean[i] is re-solve i's outcome.
function walk(missedOn: number, clean: readonly boolean[]): void {
  console.log(`day ${pad(missedOn)}  first attempt missed`);
  let miss = missedOn;
  let step = 0;
  let day = missedOn;
  let done = 0;
  let failed = 0;
  for (const ok of clean) {
    day = miss + STEPS[step];
    done += 1;
    const head = `day ${pad(day)}  re-solve ${step + 1} of ${STEPS.length}`;
    if (!ok) {
      // A failed re-solve is a new miss: the steps start again.
      failed += 1;
      miss = day;
      step = 0;
      console.log(`${head}  failed  next on day ${miss + STEPS[0]}, from step 1`);
    } else if (step + 1 < STEPS.length) {
      step += 1;
      console.log(`${head}  clean   next on day ${miss + STEPS[step]}`);
    } else {
      console.log(`${head}  clean   leaves the queue`);
      break;
    }
  }
  console.log(`${done} re-solves, ${failed} failed, out of the queue on day ${day}`);
}

walk(2, [true, true, true, true]);
console.log();
walk(2, [true, true, false, true, true, true, true]);

Recorded with TypeScript 6.0.3 (strict), run by Node.js 24.21.0 on macOS 26 arm64. It runs in your browser with QuickJS (quickjs-emscripten 0.32.0) after Sucrase 3.35.1. To run it yourself: npx -p typescript@6.0.3 tsc --noEmit --strict main.ts && node main.ts

Go 1.27 · main.go

package main

import "fmt"

// Days after the latest miss: a planning choice.
var steps = [...]int{1, 3, 7, 21}

// walk prints one problem's way through the queue; clean[i] is re-solve i's outcome.
func walk(missedOn int, clean []bool) {
	fmt.Printf("day %2d  first attempt missed\n", missedOn)
	miss, step, day, done, failed := missedOn, 0, missedOn, 0, 0
resolves:
	for _, ok := range clean {
		day = miss + steps[step]
		done++
		head := fmt.Sprintf("day %2d  re-solve %d of %d", day, step+1, len(steps))
		switch {
		case !ok:
			// A failed re-solve is a new miss: the steps start again.
			failed++
			miss, step = day, 0
			fmt.Printf("%s  failed  next on day %d, from step 1\n", head, miss+steps[0])
		case step+1 < len(steps):
			step++
			fmt.Printf("%s  clean   next on day %d\n", head, miss+steps[step])
		default:
			fmt.Printf("%s  clean   leaves the queue\n", head)
			break resolves
		}
	}
	fmt.Printf("%d re-solves, %d failed, out of the queue on day %d\n", done, failed, day)
}

func main() {
	walk(2, []bool{true, true, true, true})
	fmt.Println()
	walk(2, []bool{true, true, false, true, true, true, true})
}

In Go: break inside a switch leaves only the switch, so the loop carries the label resolves, and break resolves ends the walk once the problem leaves the queue.

Recorded with Go 1.27.1 on macOS 26 arm64. It runs in your browser with the Go compiler in WebAssembly 1.27.1. To run it yourself: go run main.go

Swift 6.4 · main.swift

// Days after the latest miss: a planning choice.
let steps = [1, 3, 7, 21]

func pad(_ n: Int) -> String {
  n < 10 ? " \(n)" : "\(n)"
}

// Prints one problem's way through the queue; clean[i] is re-solve i's outcome.
func walk(missedOn: Int, clean: [Bool]) {
  print("day \(pad(missedOn))  first attempt missed")
  var (miss, step, day, done, failed) = (missedOn, 0, missedOn, 0, 0)
  for ok in clean {
    day = miss + steps[step]
    done += 1
    let head = "day \(pad(day))  re-solve \(step + 1) of \(steps.count)"
    if !ok {
      // A failed re-solve is a new miss: the steps start again.
      failed += 1
      (miss, step) = (day, 0)
      print("\(head)  failed  next on day \(miss + steps[0]), from step 1")
    } else if step + 1 < steps.count {
      step += 1
      print("\(head)  clean   next on day \(miss + steps[step])")
    } else {
      print("\(head)  clean   leaves the queue")
      break
    }
  }
  print("\(done) re-solves, \(failed) failed, out of the queue on day \(day)")
}

walk(missedOn: 2, clean: [true, true, true, true])
print()
walk(missedOn: 2, clean: [true, true, false, true, true, true, true])

In Swift: Without Foundation the standard library has no padded number format, so pad adds the space, and (miss, step) = (day, 0) assigns both at once.

Recorded with Apple Swift 6.4 on macOS 26 arm64. It runs in your browser with a WebAssembly build on the WASI runner (browser_wasi_shim 0.4.2). To run it yourself: swiftc -O main.swift -o main && ./main

Ruby 4.0 · main.rb

# frozen_string_literal: true

# Days after the latest miss: a planning choice.
STEPS = [1, 3, 7, 21].freeze

# One missed problem in the re-solve queue: the day of its latest miss and its next step.
class Problem
  attr_reader :day, :done, :failed

  def initialize(missed_on)
    @miss = missed_on
    @day = missed_on
    @step = 0
    @done = 0
    @failed = 0
  end

  # Records one re-solve and returns its line; clean is the re-solve's outcome.
  def resolve(clean)
    @day = @miss + STEPS[@step]
    @done += 1
    head = "day #{@day.to_s.rjust(2)}  re-solve #{@step + 1} of #{STEPS.size}"
    return fail_resolve(head) unless clean

    @step += 1
    return "#{head}  clean   leaves the queue" if left?

    "#{head}  clean   next on day #{@miss + STEPS[@step]}"
  end

  def left?
    @step == STEPS.size
  end

  private

  # A failed re-solve is a new miss: the steps start again.
  def fail_resolve(head)
    @failed += 1
    @miss = @day
    @step = 0
    "#{head}  failed  next on day #{@miss + STEPS[0]}, from step 1"
  end
end

# Prints one problem's way through the queue; clean[i] is re-solve i's outcome.
def walk(missed_on, clean)
  puts "day #{missed_on.to_s.rjust(2)}  first attempt missed"
  problem = Problem.new(missed_on)
  clean.each do |ok|
    puts problem.resolve(ok)
    break if problem.left?
  end
  puts "#{problem.done} re-solves, #{problem.failed} failed, out of the queue on day #{problem.day}"
end

walk(2, [true, true, true, true])
puts
walk(2, [true, true, false, true, true, true, true])

In Ruby: RuboCop's default limit of 10 lines a method splits the walk, so a small Problem class keeps the day of the latest miss and the next step between re-solves.

Recorded with CRuby 4.0.7 on macOS 26 arm64. It runs in your browser with ruby.wasm 2.10.1 (Ruby 4.0.0). To run it yourself: ruby main.rb

Kotlin 2.4 · Main.kt

// Days after the latest miss: a planning choice.
val steps = intArrayOf(1, 3, 7, 21)

fun pad(n: Int) = n.toString().padStart(2)

// Prints one problem's way through the queue; clean[i] is re-solve i's outcome.
fun walk(
    missedOn: Int,
    clean: BooleanArray,
) {
    println("day ${pad(missedOn)}  first attempt missed")
    var miss = missedOn
    var step = 0
    var day = missedOn
    var done = 0
    var failed = 0
    for (ok in clean) {
        day = miss + steps[step]
        done++
        val head = "day ${pad(day)}  re-solve ${step + 1} of ${steps.size}"
        when {
            !ok -> {
                // A failed re-solve is a new miss: the steps start again.
                failed++
                miss = day
                step = 0
                println("$head  failed  next on day ${miss + steps[0]}, from step 1")
            }

            step + 1 < steps.size -> {
                step++
                println("$head  clean   next on day ${miss + steps[step]}")
            }

            else -> {
                println("$head  clean   leaves the queue")
                break
            }
        }
    }
    println("$done re-solves, $failed failed, out of the queue on day $day")
}

fun main() {
    walk(2, booleanArrayOf(true, true, true, true))
    println()
    walk(2, booleanArrayOf(true, true, false, true, true, true, true))
}

In Kotlin: when without an argument replaces the if-else chain, and a break inside when leaves the for loop around it.

Recorded with Kotlin 2.4.20 on Java 25.0.4.1 on macOS 26 arm64. It runs in your browser with a WebAssembly build on the WASI runner (browser_wasi_shim 0.4.2). To run it yourself: kotlinc Main.kt -include-runtime -d main.jar && java -jar main.jar

Dart 3.13 · main.dart

// Days after the latest miss: a planning choice.
const steps = [1, 3, 7, 21];

String pad(int n) => n.toString().padLeft(2);

// Prints one problem's way through the queue; clean[i] is re-solve i's outcome.
void walk(int missedOn, List<bool> clean) {
  print('day ${pad(missedOn)}  first attempt missed');
  var (miss, step, day, done, failed) = (missedOn, 0, missedOn, 0, 0);
  for (final ok in clean) {
    day = miss + steps[step];
    done++;
    final head = 'day ${pad(day)}  re-solve ${step + 1} of ${steps.length}';
    if (!ok) {
      // A failed re-solve is a new miss: the steps start again.
      failed++;
      (miss, step) = (day, 0);
      print('$head  failed  next on day ${miss + steps[0]}, from step 1');
    } else if (step + 1 < steps.length) {
      step++;
      print('$head  clean   next on day ${miss + steps[step]}');
    } else {
      print('$head  clean   leaves the queue');
      break;
    }
  }
  print('$done re-solves, $failed failed, out of the queue on day $day');
}

void main() {
  walk(2, [true, true, true, true]);
  print('');
  walk(2, [true, true, false, true, true, true, true]);
}

In Dart: The record pattern var (miss, step, day, done, failed) = (…) declares the five counters in one line, and (miss, step) = (day, 0) updates two of them together.

Recorded with Dart 3.13.5 on macOS 26 arm64. It runs in your browser with a WebAssembly build by dart2wasm 3.13.5. To run it yourself: dart run main.dart

What every language printed

day  2  first attempt missed
day  3  re-solve 1 of 4  clean   next on day 5
day  5  re-solve 2 of 4  clean   next on day 9
day  9  re-solve 3 of 4  clean   next on day 23
day 23  re-solve 4 of 4  clean   leaves the queue
4 re-solves, 0 failed, out of the queue on day 23

day  2  first attempt missed
day  3  re-solve 1 of 4  clean   next on day 5
day  5  re-solve 2 of 4  clean   next on day 9
day  9  re-solve 3 of 4  failed  next on day 10, from step 1
day 10  re-solve 1 of 4  clean   next on day 12
day 12  re-solve 2 of 4  clean   next on day 16
day 16  re-solve 3 of 4  clean   next on day 30
day 30  re-solve 4 of 4  clean   leaves the queue
7 re-solves, 1 failed, out of the queue on day 30

The failure cost more than one session: the problem stayed in the queue until day 30 instead of day 23, with 7 re-solves instead of 4. A problem you can solve only while you still remember it keeps coming back, and one you can solve a week and then three weeks later leaves.

Four habits keep the queue honest and its cost under control:

  • Budget the queue first. Re-solves come before new problems every day, because each miss brings four more sessions.
  • Count only clean re-solves. A clean re-solve uses a blank editor and a time box: A fixed amount of time set before you start a task. When it runs out you stop and record the result, finished or not., needs no hint, and passes your own tests.
  • Write notes from your own solutions. One page per pattern holds your template, its cues and your bugs, each with a test.
  • Change the plan for the last two weeks. Mixed drills and full mock interviews replace new patterns as the date gets close.

Checkpoint

Check: Spaced re-solves and the reset after a failure

  1. Question 1 of 3 Which statement does the research behind the queue support?

    Choose one answer.

    Show the answer: Question 1 of 3

    Answer: Practice spread over separate sessions is remembered better, and the later the test, the longer the best gap

    The queue rests on two findings: practice spread over separate sessions is remembered better than the same practice crammed into one, with a longer best gap for a later test, and recalling beat rereading two days and a week later. The steps of 1, 3, 7 and 21 days are a choice made from those findings, not one of them.

  2. Question 2 of 3 A problem is missed on day 4, and every re-solve after that is clean and on time. On which day is its last re-solve?

    Type a number.

    A hint

    Every step is counted from the day of the miss.

    Show the answer: Question 2 of 3

    Answer: 25

    The steps fall on 4 + 1, 4 + 3, 4 + 7 and 4 + 21, so on days 5, 7, 11 and 25. After the clean re-solve on day 25 the problem leaves the queue.

  3. Question 3 of 3 A problem missed on day 2 was re-solved cleanly on days 3 and 5. On day 9 its re-solve needs a hint. When does it come back?

    Choose one answer.

    Show the answer: Question 3 of 3

    Answer: Day 10: a failed re-solve is a new miss, so the steps start again from day 9

    A re-solve that needs a hint is a miss, so the problem goes back to step 1, counted from the day of the failure: days 10, 12, 16 and 30. The second walk of the program follows exactly this path, with 7 re-solves in all.

Four weeks of the queue on a sample log of 30 problems, measured Pro: opens with a Pro, Premium or Ultimate pass

In this part:

  • What counts as a clean re-solve, and what the log records
  • A one-page pattern note, written from your own solutions and tested
  • The last two weeks before an interview, planned day by day
  • Practise: plan four weeks under a daily cap
    • Plan four weeks of re-solves under a daily cap
  • Interview questions
    • A re-solve queue hands out the problems due today, the most overdue first. When is a min-heap keyed by due day the right structure, and when is an array of day buckets better?
    • Design the data structure behind a re-solve queue for 100,000 problems. record(problem, day, outcome) moves a problem to its next step, or back to the first after a failed re-solve; due(day) returns every problem due by that day, the most overdue first. Give the cost of each operation.
    • Each missed problem needs re-solves 1, 3, 7 and 21 days after the miss, but a learner fits at most 4 re-solves into a day. Write a planner that never breaks the cap, never plans a re-solve early and keeps each gap after a late re-solve. How do you show that it works?
    • n problems (n up to 200,000) were missed, each on a day m up to 10⁹, and each miss puts re-solves on days m + 1, m + 3, m + 7 and m + 21. Find the busiest day and its number of re-solves, then answer q queries: how many re-solves fall between day a and day b?
  • Key takeaways

2 diagrams · 4 runnable examples (final_weeks.py, four_weeks.py, practice_log.py, replay_bugs.py, resolve_queue.py, window_note.py) · 1 exercise with sample tests · 3 interview answers · about 51 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Terms in this lesson

interleaved practice
Practice that mixes problems of different kinds in one session, so that each problem has to be recognised before it is solved.
retrieval practice
Learning by recalling something from memory, such as solving a problem again from a blank editor, rather than reading it again.
spacing effect
The finding that practice spread over separate sessions is remembered better than the same practice crammed into one session.
time box
A fixed amount of time set before you start a task. When it runs out you stop and record the result, finished or not.

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.