Plots as Objects
A plot is a record, and two functions work with it: growPlot and describePlot. Data and the functions that work with them belong together, and that's what a class is for, as in the lesson on objects. In this step, plots become objects of the class Plot. The program will do exactly the same as before, and its output won't change at all. Only its code will be tidier. Changing how a program is written without changing what it does is called refactoring, and programmers do it all the time.
Here's a reminder of how a class works, with one of Emma's hens:
local Hen = {}
Hen.__index = Hen
function Hen.new(name)
local hen = {name = name, eggs = 0}
return setmetatable(hen, Hen)
end
function Hen:lay(count)
self.eggs = self.eggs + count
end
function Hen:__tostring()
return self.name .. " (" .. self.eggs .. " eggs)"
end
local goldie = Hen.new("Goldie")
goldie:lay(2)
goldie:lay(1)
print(goldie)
print("Best hen: " .. tostring(goldie))This prints:
Goldie (3 eggs)
Best hen: Goldie (3 eggs)Hen.new creates a table with the hen's data, gives it the metatable Hen and returns it: setmetatable returns the table it gets. Thanks to Hen.__index = Hen, a hen that doesn't have a field, such as lay, looks for it in Hen. The colon in goldie:lay(2) passes the hen itself as self. And __tostring says what print and tostring make of a hen (%s in string.format uses it too). But .. doesn't, so "Best hen: " .. goldie would end with an error. That's why the example calls tostring(goldie).
The same goes for table.concat. If you add the plots themselves to the day's list instead of their texts, the program stops:
main.lua:108: invalid value (table) at index 1 in table for 'concat'So add tostring(plot) to the list. Another mistake is a dot instead of the colon. plot.grow(growth[today]) passes the number of days as self, and the method can't read a field of a number:
main.lua:44: attempt to index a number value (local 'self')The harvest in the main program can stay as it is. An object is still a table, so plot.grown and plot.crop work as before.
Instructions
Under findCrop, create the class Plot: local Plot = {} and Plot.__index = Plot. Then write Plot.new(crop, seeds), which creates the table {crop = crop, seeds = seeds, grown = 0}, gives it the metatable Plot with setmetatable and returns it.
Turn the two functions into methods. Plot:grow(amount) does what growPlot does, with self instead of plot, and Plot:__tostring() returns the same text as describePlot.
Use the class in the main program: plant with Plot.new(crop, item.seeds), let the plots grow with plot:grow(growth[today]), and add tostring(plot) to the day's list. Then delete the functions growPlot and describePlot, because nothing needs them anymore. (If a call to one of them were left somewhere, the program would stop with attempt to call a nil value (global 'growPlot').) The output stays exactly the same as in the previous step:
PRICE LIST
carrot: seed 2, sells for 5
pumpkin: seed 5, sells for 20
strawberry: seed 4, sells for 12
wheat: seed 1, sells for 3
Plot 1: carrot x4 for 8 coins
Plot 2: wheat x6 for 6 coins
Plot 3: pumpkin x2 for 10 coins
Not enough coins for strawberry x2
Coins: 6
Day 1 (sunny): carrot 1/3, wheat 1/2, pumpkin 1/6
Day 2 (sunny): carrot 2/3, wheat 2/2, pumpkin 2/6
Day 3 (rainy): carrot 3/3, wheat 2/2, pumpkin 4/6
Day 4 (cloudy): carrot 1/3, wheat 1/2, pumpkin 5/6
Day 5 (sunny): carrot 2/3, wheat 2/2, pumpkin 6/6
Day 6 (cloudy): carrot 3/3, wheat 1/2, pumpkin 1/6
Market: carrot 8 x 5, pumpkin 2 x 20, wheat 18 x 3 = 134
End of the season: 140 coinsStart programming for free
5/7