A Real Frame? Let's Think About That
A twitter pal has been working on bowling with a frame display notion, like at the bowling alley. Hmm ...
Let’s think about that. The balls arrive incrementally, so the various frame displays have to deal with having no idea. They also have to deal with having a partial idea: As soon as one ball is rolled in a frame, I think you display that value in one of the little squares, and then the second value if not a spare in the other little square? A / if it is a spare? And of course X in the first little square if it is a strike? And then the total down in the big area when the frame value is known? I think that’s it.
So a frame knows three values, the first small square, the second small square, and the big part, the running total. These “light up” only when the corresponding value is known. And the tenth frame is different: it has three small squares and the final total. Some scoreboards have a big display of the total, as well. I’ll ignore that, and the tenth frame display for now.
Looking at my Lua frame, seems like it “just” needs to be able to respond with the values of the three squares, and then it could display the scoreboard for the completed game. To deal with the game being incomplete, we’ll need to do a bit more messing around. That can be later.
So, I think I’ll start by putting a rudimentary displayString into my frame object, that will return something like 4,5,9 in the first frame, 4,5,18 in the second, and so on.
I begin, however, by repackaging the testing a bit. In the previous article, I had all my functions basically bound into one script with sections for bowling, the framer, and the assertions. My tests are green. To make space to build this new functionality, I need to improve the modularity of this little system. Remember: it’s Red / Green / Refactor, not just Red / Green / Hack Away. Here’s what I came up with:
-- testing.lua
-- unit testing
Testing = {}
local t = Testing
t.beginTests = function()
print "begin tests"
t.asserts = 0
t.successes = 0
t.failures = 0
end
t.endTests = function() print( t.asserts .. “ asserts, “ .. t.successes .. “ successes “ .. t.failures .. “ failures”) if t.asserts == t.successes then print “GREEN BAR!” else print “RED BAR!” end end
</code>
t.assertEquals = function(expected, actual)
t.asserts = t.asserts + 1
if expected ~= actual then
print ( "expected " .. expected .. ", was " .. actual)
t.failures = t.failures + 1
else
t.successes = t.successes + 1
end
end
That’s just a separate iLua script file that defines a public table called Testing, which contains functions beginTests, endTests, and assertEquals, plus the variables asserts, successes, and failures. Once this file is loaded, any script can access Testing and run tests. Let’s look at the corresponding bowling file, first with an eye to the way the testing now works:
-- bowling12.lua dofile("testing.lua") -- frame display -- frame function firstFrame(rollList) local frame = {} frame.rolls = rollList frame.offset = 0 frame.roll = function (n) return frame.rolls[n + frame.offset] end frame.step = function(n) frame.offset = frame.offset + n end frame.frameSize = function() return frame.roll(1) == 10 and 1 or 2 end frame.nextFrame = function() frame.step(frame.frameSize()) end frame.score = function() base = frame.roll(1) + frame.roll(2) if frame.roll(1) == 10 or base == 10 then base = base + frame.roll(3) end return base end frame.frameString = function(frameNumber) return "xxx" end return frame end -- bowling function score(rollList) local frame = firstFrame(rollList) total = 0 for frameNumber = 1,10 do total = total + frame.score() frame.nextFrame() end return total end function frameDisplay(rollList, frameNumber) local frame = firstFrame(rollList) return frame.frameString(frameNumber) end -- bowling tests t = Testing t.beginTests() zeros = {0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0,} t.assertEquals(0, score(zeros)) opens = {} for frame = 1,10 do opens[#opens+1] = 4 opens[#opens+1] = 5 end t.assertEquals(90, score( opens)) opens = {} for frame = 1,5 do opens[#opens+1] = 4 opens[#opens+1] = 5 opens[#opens+1] = 3 opens[#opens+1] = 5 end t.assertEquals(85, score( opens)) -- spare spare = { 4,5, 5,5, 4,5, 4,5, 4,5, 4,5, 4,5, 4,5, 4,5, 4,5} t.assertEquals(95, score(spare)) t.assertEquals( "4,5,9", frameDisplay(spare,1)) -- strike strike = { 4,5, 10, 4,5, 4,5, 4,5, 4,5, 4,5, 4,5, 4,5, 4,5} t.assertEquals(100, score(strike)) -- perfect perfect = { 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10 } t.assertEquals( 300, score(perfect)) t.endTests()
Take a look at the red highlighted bits above. We read in the new testing.lua file, defining the table “Testing”. For typing convenience, we save that in a variable t (which I see now should be declared local). Then we refer to t whenever we want to do our beginTests, endTests, or assertEquals. Not much to it.
Let’s now look at what I did about the frame display. Here’s the same code again, with the frame display code highlighted:
-- bowling12.lua dofile("testing.lua") -- frame display -- frame function firstFrame(rollList) local frame = {} frame.rolls = rollList frame.offset = 0 frame.roll = function (n) return frame.rolls[n + frame.offset] end frame.step = function(n) frame.offset = frame.offset + n end frame.frameSize = function() return frame.roll(1) == 10 and 1 or 2 end frame.nextFrame = function() frame.step(frame.frameSize()) end frame.score = function() base = frame.roll(1) + frame.roll(2) if frame.roll(1) == 10 or base == 10 then base = base + frame.roll(3) end return base end frame.frameString = function(frameNumber) return "xxx" end return frame end -- bowling function score(rollList) local frame = firstFrame(rollList) total = 0 for frameNumber = 1,10 do total = total + frame.score() frame.nextFrame() end return total end function frameDisplay(rollList, frameNumber) local frame = firstFrame(rollList) return frame.frameString(frameNumber) end -- bowling tests t = Testing t.beginTests() zeros = {0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0,} t.assertEquals(0, score(zeros)) opens = {} for frame = 1,10 do opens[#opens+1] = 4 opens[#opens+1] = 5 end t.assertEquals(90, score( opens)) opens = {} for frame = 1,5 do opens[#opens+1] = 4 opens[#opens+1] = 5 opens[#opens+1] = 3 opens[#opens+1] = 5 end t.assertEquals(85, score( opens)) -- spare spare = { 4,5, 5,5, 4,5, 4,5, 4,5, 4,5, 4,5, 4,5, 4,5, 4,5} t.assertEquals(95, score(spare)) t.assertEquals( "4,5,9", frameDisplay(spare,1)) -- strike strike = { 4,5, 10, 4,5, 4,5, 4,5, 4,5, 4,5, 4,5, 4,5, 4,5} t.assertEquals(100, score(strike)) -- perfect perfect = { 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10 } t.assertEquals( 300, score(perfect)) t.endTests()
Note that I just slipped a new assert into the spare test, as my first test of the frame display. I posited a function frameDisplay(rolls, frameNumber). Then I wrote that function in the bowling section. It just creates a frame, using firstFrame, just like scoring does, then forwards the request to that frame object, including the desired frameNumber. And that function returns “xxx”, which is enough to make the test fail.
This is incremental design, folks!
At first blush, you may think that’s nothing. But in fact it’s a lot more than nothing. In the new test, I’ve made the design decision that to get the frame display, you use the function frameDisplay, which is a function in the bowling area. (One day, this will probably become the bowling object. Right now, our solution is procedural: that’s how far the design has evolved so far.)
And I’ve made the design decision that the bowling code will forward that request to our blossoming frame object. That makes sense to me: the frame already calculates the frame score, so why wouldn’t it know all the intermediate information required in order to return the frame display.
Oh, and I decided that the frame display information will be a comma-separated string with the values of the little score boxes followed by the frame score. I’m anticipating that for the tenth frame, it will be a list of four items but we don’t have a test for that yet.
So this tiny bit of code embodies several design decisions: the internal format of the display information, the name and location of the code that accesses it, and the name and location of the code that computes it. One new test, a quick decision about a string format, and a couple of trivial methods.
Naturally these design decisions are subject to change. As always, we’ll push forward, first making this test run (or deciding that it’s too hard), then refining the code, then adding new tests.
Down the road a ways, we’ll have to deal with partial information, of course. In the full implementation, we envision this frame display information providing the data for a game display that shows the running values as the game progresses. I’m putting that problem off, quite intentionally. I’m sure that once I can calculate the frame display string for a completed frame, it will be easy enough to detect that the frame is not complete and adjust the value returned.
I could be wrong. That’s OK, too. I’m going in tiny steps, and when I discover I’ve gone the wrong way, I’ll just take a step or two back and adjust. At least, that’s my plan. Let’s see how it goes in the next update.
Thanks!