algomiBlogs
← All posts

Machine Coding Case Studies in Java, Part 2: A Weekend Hotel Trip with Splitwise, Elevator, Tic-Tac-Toe, Logger, and Design Sketches

Algomi Research · Created 2026-09-29 · Updated 2026-10-15 · System Design

Interview Roadmap Deep Dives · Part 22 of 28
Machine Coding Case Studies, Part 1: Parking Lot, LRU Cache, Rate Limiter, Vending Machine (Java, via a Shopping Mall)ML System Design and Recommendation Systems: From Data to Ranking in Production
Page 1 of 1

One trip, ten problems

Imagine a group of friends (Asha, Ben, Chen and company) on a weekend trip to a hotel. Over two days they split bills, ride the hotel lift, play a game in the lobby, write a trip diary, and then get through a long list of small chores. Every one of those moments is a classic machine-coding problem. This post follows that one trip from the first paragraph to the last, and all the code is modern Java (17 or newer: records, sealed types, enums, and switch expressions).

This second set moves from single-structure problems (Part 1) to problems with multiple actors and rules. Four are worked in full with code that was compiled and run. Six more are class sketches with the design decisions and follow-ups an interviewer will push on, since in a real round you would build one of them, not all.

Trip scene Software problem Key idea
Friends share the bills and settle up Splitwise Integer money, split strategies, net balances, greedy settling
The hotel lift Elevator system LOOK/SCAN scheduling, state enum, cost-based dispatch
Noughts and crosses in the lobby Tic-tac-toe, Snake and Ladders Counters for O(1) win checks, rules as data
The trip diary Logging framework Levels, handlers, formatters, thread safety
Cinema, reminders, library corner, cab, food, chess Six design sketches Locking, strategies, state machines, snapshots

Where the analogy stops being accurate: friends settle up once and trust each other, but software must handle two phones editing the same bill at once. A hotel lift is one shaft you can see, but a real elevator controller must cope with hardware faults. A paper diary has one writer at a time, while a program has many threads. Each section says where the story and the software part ways.

1. Splitwise: the friends share the trip bills

The trip scene (ELI5). Asha pays for dinner, Ben pays for the taxi, and Chen pays for nothing yet. Nobody wants to pull out a calculator at every meal. So they write each bill in a notebook with who paid and who shared it. At the end of the trip, instead of everyone paying everyone, they work out who is up and who is down overall, and the people who are down pay the people who are up. The goal is the fewest handovers of cash.

Requirements: users add expenses paid by one person and split among several (equally, by exact amounts, or by percentage); show who owes whom; simplify debts.

Design decisions:

  • Store money as integer paise (a long), never double. Floating point cannot represent 0.1 exactly: 0.1 + 0.2 prints 0.30000000000000004. Money that drifts by a hair is a bug. BigDecimal is the other correct choice, especially when you need fractions of a paisa, rates, or many currencies; see the BigDecimal Javadoc. Integer paise is simpler and fast when you only add and subtract.
  • Splitting 10000 paise three ways gives 3334, 3333, 3333: the leftover paise go to someone, and the shares must sum to the bill.
  • A split rule per expense (Strategy pattern): equal, exact, percent. Here they are records implementing one interface. The exact split is the same shape and is left out for space.
  • Keep a balance per friend: positive means they are owed, negative means they owe. Net balances are enough to answer "who owes whom" after simplification.
  • Simplify by repeatedly matching the biggest creditor with the biggest debtor (greedy). This gives few payments, but finding the true minimum is NP-hard (it behaves like subset-sum partitioning), so say so.
// Java 17
import java.util.*;

record Payment(String from, String to, long paise) {}

interface SplitRule {
    Map<String, Long> shares(long totalPaise);

    record Equal(List<String> friends) implements SplitRule {
        public Map<String, Long> shares(long total) {
            long base = total / friends.size();
            long extra = total % friends.size();          // leftover paise
            Map<String, Long> out = new LinkedHashMap<>();
            for (int i = 0; i < friends.size(); i++) {
                out.put(friends.get(i), base + (i < extra ? 1 : 0));
            }
            return out;
        }
    }

    record Percent(Map<String, Integer> pct) implements SplitRule {
        public Map<String, Long> shares(long total) {
            if (pct.values().stream().mapToInt(Integer::intValue).sum() != 100)
                throw new IllegalArgumentException("percentages must add up to 100");
            Map<String, Long> out = new LinkedHashMap<>();
            long given = 0;
            for (var e : pct.entrySet()) {
                long s = total * e.getValue() / 100;       // rounds down
                out.put(e.getKey(), s);
                given += s;
            }
            long left = total - given;                     // hand leftover paise out one by one
            for (String f : out.keySet()) {
                if (left-- <= 0) break;
                out.merge(f, 1L, Long::sum);
            }
            return out;
        }
    }
}

You've reached the free preview of this post.

Subscribe to the Blogs plan (₹99/mo) — or use any Algomi Suite, Career, Investor, or Trader plan — to keep reading.

See plans

Sources

Interview Roadmap Deep Dives · Part 22 of 28
Machine Coding Case Studies, Part 1: Parking Lot, LRU Cache, Rate Limiter, Vending Machine (Java, via a Shopping Mall)ML System Design and Recommendation Systems: From Data to Ranking in Production