Start learning free

LESSON 253 OF 572 · FUNCTIONS & CLASSES

Water Bottle in C#: Limits Inside a Class

Practice O(1) Read the full explanation

A water bottle holds at most a fixed amount of water: you can't pour in more than fits, and you can't drink more than is in it. Write a class WaterBottle that keeps track of how much water is in one bottle as you fill it and drink from it. Amounts are whole milliliters.

Implement the class WaterBottle:

  • WaterBottle(capacity) creates an empty bottle that holds at most capacity.
  • fill(amount) pours in amount, but only as much as fits: the water never goes above the capacity. It returns how much actually went in.
  • drink(amount) drinks amount, but only as much as is in the bottle: the water never goes below 0. It returns how much was actually drunk.
  • level() returns how much water is in the bottle now.

The input is a bottle's capacity and a list of operations, applied in order to one new WaterBottle. The output lists what the fill, drink and level calls return, in order. The starter code spells these names the way your language does (in Go and Rust, WaterBottle is a struct with methods). Give the fields names that differ from the methods' names: in several languages a field named level would clash with the method level().

 

Example 1:

Input:
    WaterBottle(500)
    fill(200)
    drink(50)
    level()
Output: 200 50 150
Explanation: The empty bottle has room for 500, so all 200 go in. Drinking 50 of them leaves 150.

Example 2:

Input:
    WaterBottle(300)
    fill(250)
    fill(100)
    level()
    fill(10)
Output: 250 50 300 0
Explanation: After the first fill, only 50 more fit: fill(100) pours in 50 and returns 50, and the bottle
is full. A full bottle takes nothing more, so fill(10) returns 0.

Example 3:

Input:
    WaterBottle(100)
    drink(10)
    fill(60)
    drink(100)
    level()
    drink(5)
Output: 0 60 60 0 0
Explanation: The bottle starts empty, so the first drink gets nothing. drink(100) can only drink
the 60 that are there, which empties the bottle again.

 

Constraints:

  • 1 <= capacity <= 104
  • 0 <= amount <= 104
  • At most 250 operations on one WaterBottle.

Note:

Each method should run in O(1) time: the bottle only needs to remember its capacity and how much water it holds.

STEP 1 OF 10
    Ctrl + Enter to check

    C# · FUNCTIONS & CLASSES

    Water Bottle in C#, explained

    Problem overview

    A WaterBottle holds at most capacity milliliters. Fill(amount) pours water in, but only as much as fits, and returns how much actually went in. Drink(amount) drinks, but only as much as is in the bottle, and returns how much was actually drunk. Level() returns the water in the bottle now. A new bottle is empty.

    For a capacity of 300: Fill(250) returns 250. Then Fill(100) returns only 50, because only 50 more fit, and the bottle is full. A further Fill(10) returns 0.

    Approach

    The class remembers two numbers: the capacity, which never changes, and the water, which does. The rule for both methods is the same: take the smaller of what was asked and what is possible.

    • For Fill, what is possible is the room left, capacity - water.
    • For Drink, what is possible is the water in the bottle.

    Computing the amount that really moved, then applying it and returning it, keeps the water between 0 and the capacity without any extra checks. Each method is O(1).

    Step by step

    1. Fields: a public int capacity, and public int water = 0, since every bottle starts empty.
    2. Constructor: store the parameter in this.capacity. The parameter has the field's name, so this. picks the field. water already starts at 0.
    3. In Fill, compute added as Math.Min(amount, capacity - water). If everything fits, that is amount; otherwise it is the room left and the bottle ends exactly full. A full bottle has no room, so added is 0.
    4. Add added, not amount, to water, then return added, so the caller learns what really happened.
    5. In Drink, compute drunk as Math.Min(amount, water), subtract it from water and return it. From an empty bottle, drunk is 0.
    6. Level returns water.

    The solution in C#

    class WaterBottle {
        public int capacity;
        public int water = 0;
    
        public WaterBottle(int capacity) {
            this.capacity = capacity;
        }
    
        public int Fill(int amount) {
            int added = Math.Min(amount, capacity - water);
            water += added;
            return added;
        }
    
        public int Drink(int amount) {
            int drunk = Math.Min(amount, water);
            water -= drunk;
            return drunk;
        }
    
        public int Level() {
            return water;
        }
    }
    

    Using it:

    var b = new WaterBottle(300);
    Console.WriteLine(b.Fill(250));
    Console.WriteLine(b.Fill(100));
    Console.WriteLine(b.Level());
    Console.WriteLine(b.Drink(500));
    Console.WriteLine(b.Level());
    

    This prints:

    250
    50
    300
    300
    0
    

    Math.Min comes from the System namespace, so a program of your own needs using System; at the top.

    Edge cases and common mistakes

    • Overfilling. Writing water += amount lets the level pass the capacity. Add only what fits.
    • Returning the request. Fill returns what went in, not what was offered: Fill(100) into a bottle with room for 50 returns 50.
    • Drinking from an empty bottle. The result is 0 and the level stays at 0, never negative.
    • Amount 0. Both methods return 0 and change nothing.
    • Draining more than is there. Drink(100) on a bottle holding 60 returns 60 and leaves it empty.

    Why the class guards its own limits

    The bottle's rule is that the water never goes above the capacity or below 0. Putting that rule inside Fill and Drink means every caller gets it for free: nobody has to check the level before pouring. The return values also carry information. A caller who pours 100 and gets 50 back knows the bottle is now full and 50 did not fit.

    Think of the arithmetic as a small table for a bottle with capacity 300 that holds 250:

    CallAskedPossibleMoved
    Fill(100)100300 - 250 = 5050
    Drink(500)500300300
    Drink(5) on the empty bottle500

    Testing

    Try these sequences on a new bottle:

    • Capacity 500: Fill(200) returns 200, Drink(50) returns 50, Level() returns 150.
    • Capacity 100: Drink(10) returns 0 because the bottle is empty. Fill(60) returns 60. Drink(100) returns 60. Level() returns 0, and Drink(5) returns 0.

    Separate bottles

    Every bottle has its own capacity and water. If you create new WaterBottle(100) and new WaterBottle(10) and call Fill(60) on each, the first holds 60 and the second holds 10, because the second one stops when it is full. The same method gives different results because each object brings its own fields.

    The capacity could be made unchangeable with the readonly keyword, but the lesson keeps it a plain field: no method ever changes it, which is what matters here.

    Mistakes worth recognising

    • Checking after changing. Subtracting first and then testing for a negative level is harder to get right than clamping the amount first, which never produces an invalid level.
    • Mixing up the two limits. The limit for Fill is the free space, capacity - water, not the capacity itself. Using Math.Min(amount, capacity) lets a half-full bottle overflow.
    • Returning the level instead of the moved amount. Fill and Drink return how much moved. Level is the method that reports what is in the bottle.
    • Reusing one name. The field is water, not level, because a field and a method in the same class can't share a name.

    Key takeaways

    • Take the smaller of the request and what is possible, apply it, and return it.
    • Putting the limits in the methods means every caller gets them.
    • Each bottle has its own fields, so two bottles never interfere.

    The statement allows 0 for amount, and nothing special is needed for it: the smaller of 0 and anything non-negative is 0, so the level stays the same.

    Keep going with C#500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free