Summary

In this lesson, you learned that:

  • A coroutine is a function that can pause in the middle and later continue from the same spot, with its local variables still there. Games use coroutines for things that take several turns or frames.
  • coroutine.create(f) makes a coroutine from the function f (no parentheses after f). coroutine.resume(co) runs it until the next coroutine.yield() or the end of the function. A coroutine is a value of the type thread, not a function, so co() fails with attempt to call a thread value.
  • coroutine.status(co) is suspended (not started yet, or paused), running or dead (finished).
  • coroutine.yield() outside a coroutine, for example in a function you called yourself, ends with attempt to yield from outside a coroutine.
  • coroutine.yield(v) hands v out: resume returns true and v. coroutine.resume(co, x) hands x in: the first resume to the function's parameters, every later one as the result of the yield the coroutine is waiting at.
  • A while true do loop with a yield inside isn't endless: it pauses at every yield.
  • coroutine.wrap(f) returns a generator: a function that resumes the coroutine and returns only the values from yield. It fits into for … in, which stops when the coroutine's function ends. A used-up generator can't start over; make a new one.
  • In a game, each character can have a coroutine with its script. The game loop resumes each of them once per turn. They take turns; they never run at the same time.
  • An error inside a coroutine stops only the coroutine: resume returns false and the message, and the coroutine is dead. Resuming a dead coroutine gives false and cannot resume dead coroutine. So check the first value. A generator from wrap passes errors on, and the program stops.

In the next lesson, the final project, you'll put the whole course together: a season on Emma's farm, one program built step by step, from buying seeds to selling the harvest. Hugo the trader will be there too, as a coroutine, of course.

But first, practice this lesson. Emma is about to water her field; the list of her plots is in the editor.

Instructions

Write a function thirstyPlots(plots) that returns a generator made with coroutine.wrap. The generator yields every plot of the list plots whose water is more than 0. Loop over thirstyPlots(field) and print each plot's crop and water, like this:

To water: carrots (2 l)
To water: pumpkins (3 l)
To water: potatoes (2 l)
To water: tomatoes (1 l)

Emma's watering can is a coroutine. Write a function wateringCan(liters) with a loop that runs forever. In the loop, the can yields how many liters it holds and waits for how many to pour, local amount = coroutine.yield(liters), and then pours them: it subtracts amount from liters. Create a coroutine can from it, start it with coroutine.resume(can, 6) and print how much it holds. Then loop over a new generator thirstyPlots(field), resume can with each plot's water, and print what's left. Below the list from step 1, the program prints (the last two lines are wrong; you'll fix them in the next step):

The can holds 6 l
carrots: 4 l left
pumpkins: 1 l left
potatoes: -1 l left
tomatoes: -2 l left

A can can't pour more water than it holds. In wateringCan, when amount is more than liters, end with error("not enough water", 0). In the loop over the plots, check what resume returns: when it fails, print the crop and the message, and end the loop with break. After the loop, print Emma goes to the well. The whole program prints:

To water: carrots (2 l)
To water: pumpkins (3 l)
To water: potatoes (2 l)
To water: tomatoes (1 l)
The can holds 6 l
carrots: 4 l left
pumpkins: 1 l left
potatoes: not enough water
Emma goes to the well.

Start programming for free

By signing up, you agree to the Terms of Service and Privacy Policy.

Or sign up with:

6/6