Practise every problem on your own machine
Part of the free Low-Level Design course on LogicWiz, in the chapter: Start here: your machine and Java. The class-diagram exercises run in the app.
Practise every problem on your own machine
"Your design looks fine. But I have not seen one line of it run. Show me the output."
Priya, in Arjun's first mock machine-coding round
The loop: from problem page to matching output
Every problem in the library follows one loop. Learn it once and you can use it on all of them.
- Read the problem page: the statement, the questions to ask, the requirements.
- Design the classes and how they connect, on paper or in the class designer on this site.
- Write a class named
Solutionin your IDE. - Run the
Maindriver. A driver is a small program whose only job is to call your code in a fixed order and print one line for each step. - Compare what
Mainprinted with the expected output on the page, line by line. - Fix the first line that differs, run again, and repeat until every line matches.
A real machine-coding round works the same way. You get a contract, a set of scenarios and a result to reach, and you have about 90 minutes. The kits let you rehearse that on your own laptop.
Every problem page has a section called "Build it on your machine". It always has the same five parts: the contract (Api.java), the rules, the driver (Main.java), the expected output and a few changes to try next. Together they form a kit. The contract lists the methods that Main calls, and it has no public word on it, so it works only for files in the same folder. The rules settle the details a problem statement leaves open, such as ordering and rounding, so that any sensible design prints the same lines.
A worked mini-kit: the coupon calculator
Here is a tiny problem, invented to show the shape. SwiftKart wants a calculator that applies a coupon code to a cart total. Money is in paise. These are the rules.
- A coupon takes a percentage off the cart total. The discount is
cart * percent / 100, and the fraction is dropped. - A discount never goes above the coupon's maximum.
- A coupon applies only when the cart total reaches the coupon's minimum. Otherwise the price stays as it is.
- An unknown code changes nothing. Codes are exact, so
save10is notSAVE10. - Adding a code that already exists replaces the old coupon.
The contract is Api.java.
interface CouponApi {
void addCoupon(String code, int percentOff, long minCartPaise, long maxDiscountPaise);
long priceAfterCoupon(long cartPaise, String code);
}
You write Solution. The contract says what to build. You choose how. This is one possible answer.
import java.util.HashMap;
import java.util.Map;
class Solution implements CouponApi {
private final Map<String, Coupon> coupons = new HashMap<>();
public void addCoupon(String code, int percentOff, long minCartPaise, long maxDiscountPaise) {
coupons.put(code, new Coupon(percentOff, minCartPaise, maxDiscountPaise));
}
public long priceAfterCoupon(long cartPaise, String code) {
Coupon coupon = coupons.get(code);
if (coupon == null || cartPaise < coupon.minCartPaise()) {
return cartPaise;
}
long discount = cartPaise * coupon.percentOff() / 100;
discount = Math.min(discount, coupon.maxDiscountPaise());
return cartPaise - discount;
}
}
record Coupon(int percentOff, long minCartPaise, long maxDiscountPaise) { }
Coupon is your own class. The kit never mentions it, because it is part of your design. Methods that implement an interface must be written public. The driver is Main.java. You copy it and never edit it.
public class Main {
static void show(CouponApi shop, long cartPaise, String code) {
System.out.println("cart " + cartPaise + " code " + code + " -> " + shop.priceAfterCoupon(cartPaise, code));
}
public static void main(String[] args) {
CouponApi shop = new Solution();
shop.addCoupon("SAVE10", 10, 20000, 5000);
shop.addCoupon("HALF", 50, 10000, 8000);
show(shop, 30000, "SAVE10");
show(shop, 15000, "SAVE10");
show(shop, 99999, "SAVE10");
show(shop, 25000, "HALF");
show(shop, 30000, "NOPE");
show(shop, 30000, "save10");
shop.addCoupon("SAVE10", 20, 0, 5000);
show(shop, 1999, "SAVE10");
}
}
When the design is right, this is the exact output.
cart 30000 code SAVE10 -> 27000
cart 15000 code SAVE10 -> 15000
cart 99999 code SAVE10 -> 94999
cart 25000 code HALF -> 17000
cart 30000 code NOPE -> 30000
cart 30000 code save10 -> 30000
cart 1999 code SAVE10 -> 1600
Check three lines by hand. Line 3: 10 percent of 99999 is 9999 after the fraction is dropped, the maximum is 5000, so the discount is 5000 and the price is 94999. Line 2: 15000 is below the minimum of 20000, so nothing changes. Last line: the replacement coupon takes 20 percent of 1999, which is 399.8, and the fraction is dropped to 399, so the price is 1600.
Here is the shape of every kit.
classDiagram
class Main
class CouponApi {
<<interface>>
}
class Solution
class Coupon
Main ..> CouponApi : calls
Solution ..|> CouponApi
Solution --> Coupon : keeps
Main knows only the contract. Solution implements the contract and uses classes you design. Draw that shape yourself.
Create the files in IntelliJ, run them and read the errors
Make a new project as in the first lesson of this chapter, called coupon-kit. Then:
- Right-click
src, choose New, then Java Class, typeApiand press Enter. IntelliJ writes a class with that name. - Click in the editor, select all (Control+A, or Command+A on a Mac) and paste the kit's contract over it. The file is named
Api.javabut holds an interface with another name. That is allowed because the interface is not public. If IntelliJ shows a yellow hint there, it is not an error and the program still runs. - Repeat for
Solution, which you write, andMain, which you paste. - Click the green triangle beside
maininMainand choose Run 'Main.main()'.
Put all three files directly in src, so none has a package line. Compare the Run window with the expected output. A faster way is to copy the expected output, then right-click in an empty file and choose Compare with Clipboard. The first differing line is where to start.
If you wrote a mistake, the Run window or the Build window shows a compile error. Read it from the top. Here is one that nearly everyone meets.
Solution.java:11: error: priceAfterCoupon(long,String) in Solution cannot implement priceAfterCoupon(long,String) in CouponApi
long priceAfterCoupon(long cartPaise, String code) {
^
attempting to assign weaker access privileges; was public
The first line gives the file, the line number and the message. The next lines repeat the code and point at the problem with ^. The last line explains it: interface methods are public, so a method without public is less open than the promise. Add public. A missing semicolon gives ';' expected, and a misspelt name gives cannot find symbol. IntelliJ also underlines these in red as you type. Fix the first error first, because later ones are often its echo.
💡 Tip: Click a name with a red underline and press Alt+Enter (Option+Return on a Mac). IntelliJ offers fixes, such as "Implement methods" for a class that misses methods of an interface.
Debugging: pause the program and look inside
Arjun's third line printed 90000, not 94999. Printing guesses is slow. A debugger pauses the program at a line you choose, so you can see every variable.
- Click in the grey strip beside the line
long discount = .... A red dot appears. This is a breakpoint. - Start the program in debug mode: click the green triangle beside
mainand choose Debug 'Main.main()'. Or press Shift+F9 on Windows and Linux, or Control+D on a Mac, to debug the selected run configuration. - The program stops before that line. The line is highlighted. The Debug tool window, usually at the bottom, shows the variables in scope with their current values. Current versions label that tab Threads & Variables, and older ones show Frames and Variables.
- Use the buttons on the Debug toolbar. Step Over (F8) runs the line and moves to the next one. Step Into (F7) goes inside the method that the line calls. Resume Program (F9 on Windows and Linux, Option+Command+R on a Mac) runs until the next breakpoint. Stop is the red square.
- To test an idea without editing code, choose Evaluate Expression (Alt+F8, Option+F8 on a Mac) and type
cartPaise * coupon.percentOff() / 100. A watch keeps such an expression on screen at every pause.
The breakpoint stops on every call, and the driver makes several. Right-click the line in the grey strip and choose Add Conditional Breakpoint, then type cartPaise == 99999. Now it stops only for the call you care about. On many laptops the F keys need the Fn key, so use the toolbar buttons if a key does nothing.
The 90-minute clock and a tidy project
Use a timer. In a real round nobody gives you extra minutes. This plan keeps you moving.
| Minutes | What to do |
|---|---|
| 0 to 10 | Read the problem, ask your questions, write down the rules |
| 10 to 20 | Sketch classes and the main flow, and fix the method names |
| 20 to 30 | Write an empty Solution that compiles, and run Main once |
| 30 to 70 | Build one behaviour at a time and run Main after each |
| 70 to 90 | Handle refusals and edge cases, tidy names, prepare to explain |
The empty run matters most. A Solution that returns zeros still compiles, and then every later change is small and checked within seconds.
Keep the project tidy. Use one project per problem, or one package per problem, a named group of classes with its own folder, such as parkinglot. Packages are optional. If you use one, every file of the kit needs the same first line, package parkinglot;. Keep each class in its own file, keep fields private and delete debugging prints before you finish.
When you are stuck, do three things in order
- Reread the rules. Most wrong lines come from a rule you skipped, such as rounding down or a tie in ordering.
- Print the state. Print the values the wrong line depends on, such as the discount before the maximum is applied. Or set a breakpoint, as above.
- Shrink the scenario. Comment out every call in
Mainexcept the failing one, with the smallest numbers that still fail.
Never change the driver to make a line match. Fix your design, not the test.
📌 Summary: Read, design, write Solution, run Main, compare with the expected output. Compile first, run early, debug with breakpoints, and when stuck reread the rules, print the state, then shrink the scenario.
What Arjun learns next
Arjun can now set up, write, run and debug a problem on his laptop. Next, Priya shows him how the SwiftKart code looks in the other languages interviewers accept, side by side with Java.
Check your understanding
- The third line prints cart 99999 code SAVE10 -> 90000, but the expected output says 94999. What should Arjun do first?
- At minute 25 of a 90-minute round, Arjun has a Solution that returns zeros for every call. Why run Main now?
- The driver calls priceAfterCoupon for many carts, but Arjun wants to pause only for the cart of 99999. Which tool fits?
- The compiler says "attempting to assign weaker access privileges; was public" for Solution.priceAfterCoupon. What is the fix?