coding-horror/basic-computer-games · error · Exception

YOU CAN'T PLACE A LARGER DISK ON TOP OF A SMALLER ONE, IT MI

Error message

YOU CAN'T PLACE A LARGER DISK ON TOP OF A SMALLER ONE, IT MIGHT CRUSH IT!

What it means

Raised as a generic Exception by Tower.add() in the Tower-of-Hanoi game when the disk being added is larger than the disk currently on top of the needle. This enforces the core rule of Tower of Hanoi (no larger disk may rest on a smaller one). It fires in take_turn() after the player picks a source disk and a destination needle (tower.py:107-121).

Source

Thrown at 90_Tower/python/tower.py:31

        print(f"[ {self.size()} ]")


class Tower:
    def __init__(self) -> None:
        self.__disks: List[Disk] = []

    def empty(self) -> bool:
        return len(self.__disks) == 0

    def top(self) -> Optional[Disk]:
        return None if self.empty() else self.__disks[-1]

    def add(self, disk: Disk) -> None:
        if not self.empty():
            t = self.top()
            assert t is not None  # cannot happen as it's not empty
            if disk.size() > t.size():
                raise Exception(
                    "YOU CAN'T PLACE A LARGER DISK ON TOP OF A SMALLER ONE, IT MIGHT CRUSH IT!"
                )
        self.__disks.append(disk)

    def pop(self) -> Disk:
        if self.empty():
            raise Exception("empty pop")
        return self.__disks.pop()

    def print(self) -> None:
        r = f'Needle: [{", ".join([str(x.size()) for x in self.__disks])}]'
        print(r)


class Game:
    def __init__(self) -> None:
        # use fewer sizes to make debugging easier
        # self.__sizes = [3, 5, 7]  # ,9,11,13,15]

View on GitHub (pinned to 5301155192)

Solutions

  1. Choose a destination needle that is empty or whose top disk is larger than the moved disk.
  2. Wrap the move in a check comparing disk.size() to to_tower.top() before calling add.
  3. Catch the Exception in take_turn and re-prompt the player for a valid needle (the game already validates disk/tower selection but add is the final guard).

Example fix

# before
disk = from_tower.pop()
to_tower.add(disk)  # raises if disk bigger than top

# after: validate before moving
disk = from_tower.pop()
if not to_tower.empty() and disk.size() > to_tower.top().size():
    from_tower.add(disk)  # undo
    print("ILLEGAL MOVE. LARGER DISK CAN'T GO ON SMALLER ONE.")
else:
    to_tower.add(disk)
Defensive patterns

Strategy: validation

Validate before calling

# Python: check before calling Tower.add
def can_place(to_tower, disk):
    return to_tower.empty() or disk.size() <= to_tower.top().size()

if can_place(to_tower, disk):
    to_tower.add(disk)
else:
    print("Illegal move.")

Type guard

from typing import Optional
def is_valid_move(to_tower: 'Tower', disk: 'Disk') -> bool:
    top: Optional[Disk] = to_tower.top()
    return top is None or disk.size() <= top.size()

Try / catch

# Catch in take_turn and re-prompt the player
try:
    to_tower.add(disk)
except Exception:
    from_tower.add(disk)  # undo pop
    print("ILLEGAL MOVE. TRY AGAIN.")

Prevention

When it happens

Trigger: Player selects a disk and tries to place it on a needle whose top disk is smaller; e.g. moving disk size 9 onto a needle topped by size 3. Triggered via the take_turn flow after which_disk()/which_tower() input.

Common situations: An illegal move in the game; a programmatic solver invoking Tower.add without checking sizes; the game's own move logic being called with an out-of-order disk.

Related errors


AI-assisted analysis of coding-horror/basic-computer-games@5301155192 (2026-08-13). Data as JSON: /api/errors/51350f350c8227d8. Report an issue: GitHub.