Candidate:
Elizabeth Blackwell
Assessed by:
Mary Mentor
Python (2023) ~ Grade 1 (Initial)
Poetry Please
Recreating an educational cardboard computer project from the 1980s, that writes limericks on demand.
Attached files
Filename (click to download) | Size | Uploaded by |
---|
poem4.png | 1.2 MB | lizblackwell |
Markdown code

poem3.png | 1.1 MB | lizblackwell |
Markdown code

poem2.png | 556.2 KB | lizblackwell |
Markdown code

poem1.png | 575.9 KB | lizblackwell |
Markdown code

book_cover.png | 2.0 MB | lizblackwell |
Markdown code

Status: Closed (results delivered). 69/100 ~ Pass (17/03/2022).
Elizabeth Blackwell
~ 17 Mar 2022 9:35 a.m.
(updated: 17 Mar 2022 9:37 a.m.)
Over forty years ago, when I was a young primary school teacher, I bought a book to help me teach basic computing to my class of ten and eleven year old students. One of the projects I most enjoyed teaching and most valued as a learning resource was a cardboard computer for writing limericks in an algorithmic fashion.
I'd like to recreate this resource in Python, so I have something to help me learn a modern programming language (I hear Python is today's BASIC). I had made this as BBC BASIC back in the 1980s but the disks with those programs are long lost. Now that I'm retired I'd like to revisit those long lost resources, from a more creative and gentle time in computing education.
I still have the book on my shelf (full of all sorts of resources I picked up during my teaching career). It was one of the original (and now famous) Usborne books about computing. I've attached some photos so you get an idea of what I'm aiming to recreate.
Here's the cover. One of Usborne's classic computing books from the 1980s (this one being published in 1981):
Here are the sections in the book describing how the cardboard computer generates poetry:
What I really appreciate is how the first group of instructions is about the "hardware", and the second group of instructions are about running the "software". The random number spinner and "memory store" pad for A, B and N make real what are quite advanced abstract concepts for ten and eleven year old students. By interacting with the cardboard computer they become the computer itself.
A very playful way to get into the "mindset" of a computer.
Elizabeth Blackwell
~ 17 Mar 2022 9:38 a.m.
I have a working piece of code that very closely matches the way the original cardboard computer program works.
Since I had dim and distant memories of BASIC, I found it useful to map concepts with which I was familiar to the core concepts for Python, grade 1.
- A BASIC array is similar to the Python list.
- BASIC's
RND
is Python'srandom
. - You don't have to prepend
$
to string values (e.g.$WORD
in BASIC is justWORD
in Python). - Nor is the
%
suffix needed to indicate a variable containing a whole number (e.g.VALUE%
is justVALUE
in Python). - You just assign in Python, no need to
LET A = 0
as in BASIC. - Indenting as a way to organise code, that felt strange but now I like it. I can see at a glance what my code is doing.
- Mu's "Check" and "Tidy" buttons are magic (in a good way). They helped me a lot and I wish we'd had something like this back in the 1980s.
- I also liked that I could poke around with Python commands once my code had finished (or crashed). This REPL thing is hard to learn but a good way to "talk to Python" in the same way we used to talk to the BBC microcomputers / ZX Spectrums with the BASIC prompt.
- Print is the same (but with parenthesis). Also,
==
and=
are different. That caught me out a few times.
I had to figure out a way to get around the GOTO
statement in the original program, and realised that I could just loop around (which is simply what the GOTO 2
line is doing) and check that A
is less than 5. On that note, I also learned Python counts lists from position 0 (zero), rather than from 1, like BASIC (and human beings) do with arrays. I Googled for how to find a random number between 0 and 3. The if
statement just repeats the same code again for finding a new random word. I tried to figure out how to do this with a loop (like with the other GOTO
statement) but this was the best I could do.
import random
DATALINES = [
"THERE WAS A YOUNG MAN FROM",
"WHO",
"HIS",
"ONE NIGHT AFTER DARK",
"AND HE NEVER WORKED OUT",
]
DATAWORDS = [
["TASHKENT", "TRENT", "KENT", "GHENT"],
["WRAPPED UP", "COVERED", "PAINTED", "FASTENED"],
["HEAD", "HAND", "DOG", "FOOT"],
["IN A TENT", "WITH CEMENT", "WITH SOME SCENT", "THAT WAS BENT"],
["IT RAN OFF", "IT GLOWED", "IT BLEW UP", "IT TURNED BLUE"],
["IN THE PARK", "LIKE A QUARK", "FOR A LARK", "WITH A BARK"],
["WHERE IT WENT", "ITS INTENT", "WHY IT WENT", "WHAT IT MEANT"],
]
A = 0
B = 0
while A < 5:
LINE = DATALINES[A]
N = random.randint(0, 3)
WORD = DATAWORDS[B][N]
RESULT = LINE + " " + WORD
if B == 2 or B == 4:
B = B + 1
N = random.randint(0, 3)
WORD = DATAWORDS[B][N]
RESULT = RESULT + " " + WORD
print(RESULT)
B = B + 1
A = A + 1
It produces results like this:
THERE WAS A YOUNG MAN FROM TASHKENT
WHO FASTENED
HIS HAND THAT WAS BENT
ONE NIGHT AFTER DARK IT GLOWED IN THE PARK
AND HE NEVER WORKED OUT WHAT IT MEANT
Since this is a working project, requiring refinement I'll submit it for assessment and see what my mentor suggests.
Elizabeth Blackwell
~ 17 Mar 2022 9:39 a.m.
For the sake of completeness (the pre-submission check-list has prompted me to add these pieces of information):
- The target audience is just myself. I enjoy poetry and this is a fun vehicle for me to learn about code too.
- All the code required to make this work is in the previous entry.
- To run the code, copy it into Mu, save the file (I use the filename
poetry.py
) and click the "Run" button. The output will appear at the bottom of the screen. It should look something like the example poem I pasted into the previous comment. - I think I've described the technical decisions I made, but they're mostly me trying to translate the cardboard computer into half-remembered BASIC, and then translating that into something similar in the core concepts for this grade in Python.
- Clearly, the idea and "algorithm" is taken from Usborne books, but the implementation is entirely my own (figured out over the course of a couple of weekends).
Elizabeth Blackwell
~ 17 Mar 2022 9:40 a.m.
I just wanted to check I was allowed to adapt the code from the Usborne books, and it turns out I can (I should have checked this at the start).
According to their website, they say:
You may adapt any of the programs in these books to modern computer languages, and share the adaptations freely online. You may not use the adaptations for commercial purposes. Please credit the name of the Usborne book from which you adapted the program, and provide a link to this webpage.
By linking to their web page I think I've fulfilled the requirements they ask for such adaptions.
Mary Mentor
~ 17 Mar 2022 9:44 a.m.
Hi Elizabeth (or is it Liz?),
I'm Mary, and I'll be your mentor.
I find your project immediately appealing for two reasons:
- The nostalgia value (I was a child of the 1980s and remember coding in BASIC).
- The expressive and artistic use of computers is something I'm passionate about (those limericks are wonderful).
I have some initial thoughts, questions and suggestions to help you improve the project before I finally assess it for grade 1.
- The code is "correct" in the sense that it works: you get a limerick. But, it's not Pythonic. By that I mean you're not using Python in an idiomatic way. Given your background in BASIC it looks like you're speaking Python with a heavy BASIC accent. Most of the suggestions I'll make are to help you code in Python "Pythonically", if you see what I mean.
- What is Pythonic? Well, famously, Python will tell you itself. Open up the REPL in Mu and type
import this
to see what Python thinks about Python. - In Python we use all caps names to represent constant values. You can lowercase all the names you use, and then your code won't feel so "shouty" either. :-)
- The same goes for the content in strings. That doesn't need to be in all caps.
- The names
A
,B
andN
are not very informative. Can you think of better names for them? - If you want to increment an integer value (whole number), use
a += 1
instead ofa = a + 1
. It's just the Pythonic way. Can you change your code to reflect this convention please? - When you've refined your code given the feedback above, I think you should start to look at f-strings in Python or using the
str
class'sformat
method to simplify how you create the output. Perhaps something to look into?
While I won't ever write code for you, I am here to help so please don't hesitate to ask questions or seek guidance. Your engagement is yet more evidence I use to form an assessment of your project.
Best of luck!
Elizabeth Blackwell
~ 17 Mar 2022 5:32 p.m.
Hi Mary,
I usually go by "Liz", and thank you for asking.
I've read through your feedback and looked into various aspects of what you've written. This is all really helpful and thank you for giving me so much food for thought. I'll do the simple things first and post an updated version of the code as each change is made.
Elizabeth Blackwell
~ 17 Mar 2022 5:33 p.m.
Here's the updated code. I have changed the following (it took surprisingly little time, and Mu's "Check" and "Tidy" buttons made it very easy):
- Lowercase the names of things. I even learned what snake case is and that it's used in Python code!
- I've renamed
A
toline_position
,B
toword_position
andN
toword_choice
. I like it because when you read the code the names help explain what's going on. - I've used the
+=
method to add a value to a variable.
import random
datalines = [
"There was a young man from",
"Who",
"his",
"One night after dark",
"And he never worked out",
]
datawords = [
["Tashkent", "Trent", "Kent", "Ghent"],
["wrapped up", "covered", "painted", "fastened"],
["head", "hand", "dog", "foot"],
["in a tent", "with cement", "with some scent", "that was bent"],
["it ran off", "it glowed", "it blew up", "it turned blue"],
["in the park", "like a quark", "for a lark", "with a bark"],
["where it went", "its intent", "why it went", "what it meant"],
]
line_position = 0
word_position = 0
while line_position < 5:
line = datalines[line_position]
word_choice = random.randint(0, 3)
word = datawords[word_position][word_choice]
result = line + " " + word
if word_position == 2 or word_position == 4:
word_position += 1
word_choice = random.randint(0, 3)
word = datawords[word_position][word_choice]
result = result + " " + word
print(result)
word_position += 1
line_position += 1
I'll take a look at string formatting now.
Mary Mentor
~ 17 Mar 2022 5:34 p.m.
Liz,
Ahhhh, that's better. It's starting to look like Python code. :-)
I have a bug report for you. When I run the revised code I see something like:
There was a young man from Trent
Who fastened
his head that was bent
One night after dark it ran off in the park
And he never worked out why it went
Since this is a limerick, that has certain conventions about how it should be written down, I was expecting it to look like this:
There was a young man from Trent,
Who fastened his head that was bent.
One night after dark,
it ran off in the park,
And he never worked out why it went.
Note the indentation and use of punctuation at the end of lines.
Can you update your code so it follows these conventions?
Don't hesitate to ask for guidance or help if need be.
Mary Mentor
~ 17 Mar 2022 5:35 p.m.
One other thing, your code doesn't contain any comments.
Can you add some, so the future you and/or anyone else looking at the code understand what it was you're trying to do? (Hint: comments should describe intent rather than how the code works, that should be obvious from reading the code.)
Thank you!
Elizabeth Blackwell
~ 17 Mar 2022 5:37 p.m.
I think I've made a bit of a mess in trying to answer your request for a properly formatted limerick. The code is quite convoluted and certainly not as understandable or simple as the original approach based on the cardboard computer. Is this why we need comments?
# A simple program to generate 16,384 different limerick poems.
# by Liz Blackwell.
# Based on a project found in an Usborne book.
import random
# These lines always stay the same.
datalines = [
"There was a young man from",
"Who",
"his",
"One night after dark",
"And he never worked out",
]
# These fragments are included in certain positions in the poem.
# Each row of fragments is a selection of choices for a certain position.
datawords = [
["Tashkent", "Trent", "Kent", "Ghent"],
["wrapped up", "covered", "painted", "fastened"],
["head", "hand", "dog", "foot"],
["in a tent", "with cement", "with some scent", "that was bent"],
["it ran off", "it glowed", "it blew up", "it turned blue"],
["in the park", "like a quark", "for a lark", "with a bark"],
["where it went", "its intent", "why it went", "what it meant"],
]
# The current static line to output.
line_position = 0
# The current choice of words/fragments to choose from.
word_position = 0
# Loop five times for five lines.
while line_position < 5:
line = datalines[line_position]
word_choice = random.randint(0, 3)
word = datawords[word_position][word_choice]
result = line + " " + word
if word_position == 2 or word_position == 4:
word_position += 1
word_choice = random.randint(0, 3)
word = datawords[word_position][word_choice]
result = result + " " + word
# Depending on the line, the output needs to be printed differently.
if line_position == 1:
print(result, end=" ")
elif line_position == 3:
print(" " + line + ",")
print(" " + result.replace("One night after dark ", "") + ",")
elif line_position == 4:
print(result + ".")
else:
print(result + ",")
word_position += 1
line_position += 1
Is this what you were expecting? Are the comments suitable? That line segment of if
and elif
statements feels like I've missed something.
I'm going to look into string formatting in the hope THAT makes sense.
In any case, the output now looks something like:
There was a young man from Trent,
Who wrapped up his foot with cement,
One night after dark,
it turned blue with a bark,
And he never worked out where it went.
Mary Mentor
~ 17 Mar 2022 5:39 p.m.
(updated: 17 Mar 2022 5:39 p.m.)
Liz,
Well done on making the layout work. I think you're seeing why I might be asking you to look into string formatting in Python ~ it should help you simplify your code and make it easier to understand.
Coders often talk about the KISS principle (keep it simple, stupid!), as a guide to evaluating the quality of code. If a program is complicated try to look for ways to make it simpler. In this case, like I said, I think string formatting in Python will be helpful for you to learn about. A quick search online brought up this article that provides a good overview.
Here's some sample code that may clear up what I'm getting at:
selected_name = random.choice(["John", "Paul", "Ringo", "George", ])
template = "Hello {name},\n\nI hope you're well."
print(template.format(name=selected_name))
You should know that \n
means "newline", I hope the rest is clear. The output should look something like this:
Hello Paul,
I hope you're well.
Elizabeth Blackwell
~ 17 Mar 2022 5:40 p.m.
Bingo..! I know exactly what to do.
This is very exciting..! I was feeling quite disappointed with my revised code, but the article to which you have linked and your kind and clear example has given me the spark of insight to fix things up.
Basically, I can do it all with some random choices and a single template string to be formatted with the choices.
Mary Mentor
~ 17 Mar 2022 5:41 p.m.
Got it in one. I look forward to your revised code.
Remember to include comments!
Elizabeth Blackwell
~ 17 Mar 2022 5:45 p.m.
I have a solution. It's so simple now that I understand how format
works with strings. It's just a really easy template system!
Here's the code:
# A simple program to generate 16,384 different limerick poems.
# by Liz Blackwell.
# Based on a project found in an Usborne book.
import random
# Contains the static shape of the poem.
template = "There was a young man from {place},\nWho {activity} his {item} {with_something}.\n One night after dark,\n {it_did_something} {in_a_place},\nAnd he never worked out {reason}."
# Choose the various items to insert into the template.
place = random.choice(["Tashkent", "Trent", "Kent", "Ghent"])
activity = random.choice(["wrapped up", "covered", "painted", "fastened"])
item = random.choice(["head", "hand", "dog", "foot"])
with_something = random.choice(
["in a tent", "with cement", "with some scent", "that was bent"]
)
it_did_something = random.choice(
["It ran off", "It glowed", "It blew up", "It turned blue"]
)
in_a_place = random.choice(["in the park", "like a quark", "for a lark", "with a bark"])
reason = random.choice(["where it went", "its intent", "why it went", "what it meant"])
# Output the formatted result.
print(
template.format(
place=place,
activity=activity,
item=item,
with_something=with_something,
it_did_something=it_did_something,
in_a_place=in_a_place,
reason=reason,
)
)
The output remains the same.
I have a question. Mu's "Check" button complains that the line where I assign the static shape of the poem to template
is too long ("Line too long (189 > 88 characters)"). Any idea how I fix that?
Elizabeth Blackwell
~ 17 Mar 2022 5:45 p.m.
Actually, I can answer my question myself. A quick search on the internet leads me to this article that suggests I split the string up into sub-strings and enclose them in parenthesis.
Like this:
# A simple program to generate 16,384 different limerick poems.
# by Liz Blackwell.
# Based on a project found in an Usborne book.
import random
# Contains the static shape of the poem.
template = (
"There was a young man from {place},\n"
"Who {activity} his {item} {with_something}.\n"
" One night after dark,\n"
" {it_did_something} {in_a_place},\n"
"And he never worked out {reason}."
)
# Choose the various items to insert into the template.
place = random.choice(["Tashkent", "Trent", "Kent", "Ghent"])
activity = random.choice(["wrapped up", "covered", "painted", "fastened"])
item = random.choice(["head", "hand", "dog", "foot"])
with_something = random.choice(
["in a tent", "with cement", "with some scent", "that was bent"]
)
it_did_something = random.choice(
["It ran off", "It glowed", "It blew up", "It turned blue"]
)
in_a_place = random.choice(["in the park", "like a quark", "for a lark", "with a bark"])
reason = random.choice(["where it went", "its intent", "why it went", "what it meant"])
# Output the formatted result.
print(
template.format(
place=place,
activity=activity,
item=item,
with_something=with_something,
it_did_something=it_did_something,
in_a_place=in_a_place,
reason=reason,
)
)
I like how I was able to split up the string so the way it is formatted in the code resembles how it looks when it's printed out.
Mary Mentor
~ 17 Mar 2022 5:48 p.m.
Great work Liz.
You can also use something called triple-quoted strings, that work over several lines, like this:
template = """
There was a young man from {place},
Who {activity} his {item} {with_something}.
One night after dark,
{it_did_something} {in_a_place},
And he never worked out {reason}.
"""
Notice how the triple-quoted string doesn't need the \n
character to show the newlines. They're already a part of the way the triple-quoted string is formatted! Nice and simple.
Perhaps consider doing this instead (but both approaches are valid).
Elizabeth Blackwell
~ 17 Mar 2022 5:50 p.m.
Mary,
Thank you so much for your suggestion. I've revised my code to this:
# A simple program to generate 16,384 different limerick poems.
# by Liz Blackwell.
# Based on a project found in an Usborne book.
import random
# Contains the static shape of the poem.
template = """
There was a young man from {place},
Who {activity} his {item} {with_something}.
One night after dark,
{it_did_something} {in_a_place},
And he never worked out {reason}.
"""
# Choose the various items to insert into the template.
place = random.choice(["Tashkent", "Trent", "Kent", "Ghent"])
activity = random.choice(["wrapped up", "covered", "painted", "fastened"])
item = random.choice(["head", "hand", "dog", "foot"])
with_something = random.choice(
["in a tent", "with cement", "with some scent", "that was bent"]
)
it_did_something = random.choice(
["It ran off", "It glowed", "It blew up", "It turned blue"]
)
in_a_place = random.choice(["in the park", "like a quark", "for a lark", "with a bark"])
reason = random.choice(["where it went", "its intent", "why it went", "what it meant"])
# Output the formatted result.
print(
template.format(
place=place,
activity=activity,
item=item,
with_something=with_something,
it_did_something=it_did_something,
in_a_place=in_a_place,
reason=reason,
)
)
It just feels neater this way, and the code is embarrassingly simple.
Mary Mentor
~ 17 Mar 2022 5:52 p.m.
Hi Liz,
I think I have all the evidence I need to be able to make an assessment. Your results should be with you very soon.
Really well done with this project.
Mary.
Elizabeth Blackwell
~ 17 Mar 2022 5:54 p.m.
Thank you for your help, guidance and support Mary. I look forward to your feedback in the assessment.