This is the story of how I ruined my 11-year old daughter's summer vacation. Or at any rate, filled it with some algebra, which I am led to believe was not an improvement.
In reality, her summer vacation was going to have some frequent algebra problems in any event, because she was doing ok with the pre-algebra at the end of the school year, but was not doing so well that going three months without any practice was going to be a good idea. So, granted that she is going to have to do some practice problems, how to make this less boring?
The idea I came up with was to turn it into a decoder. So, she gets a message that is encoded such that each letter (or other character such as a space or apostrophe) is replaced with a number. Then, she gets a series of algebra problems to help decode it. Thus, if she solves the problem "8a - 8 = 192" and determines that "a = 25", then everywhere in the puzzle where it has 25, she can replace it with the letter "a". Repeat for a couple dozen algebra problems, and she gets to find out what the puzzle says.
I could generate this by hand, but that would get old, and anyway would require me to do algebra as well, and who wants to do a bunch of stupid algebra all summer long? Not me, so python to the rescue.
First, we have to take the normal input text file, and turn it into one long text string. Nothing much to look at here, except that I did add a warning that if a line was longer than 20 characters, it would be too long once each one was replaced with "____" (and then a number underneath it on the next line). Also, I converted everything to lower case so that "A" and "a" would be replaced by the same number. This probably sets a bad example for my daughter in regards to proper grammar, but it does result in fewer algebra problems she has to solve each day, so I feel she will forgive me.
def convert_file_to_text_string(file_path):
input_file = open(file_path, 'r')
text_string = ''
for new_line in input_file:
if len(new_line) > 21:
print('keep your lines short, so we can print them out puzzle-fied!')
print(new_line)
sys.exit()
else:
text_string += new_line.lower().strip()
text_string += '\n'
input_file.close()
return text_string
Next, I wanted to have a dictionary of how many times each character occurs. That way, I could make the more frequently occurring letters have the bigger numbers, and vice versa, so that she doesn't solve one of the harder problems and then discover that all that got her was a letter like "q" that only occurs once in the puzzle.
def count_char_frequencies(text_string):
freqs = {}
for char in text_string:
if char not in freqs:
freqs[char] = 1
else:
freqs[char] += 1
return freqs
Now, I need to create the actual puzzle output, that will look something like this:
___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ 19 20 9 26 27 10 7 19 20 27 25 27 24 22 12 26 5
For this, we take as input the text string which will be the solution, and the "encoder" which says things like a = 15, b = 4, c = 27, etc. It makes the line of underscores (four for each character so that Juliet doesn't have to write in 8-point font), and then underneath that the number from the decoder. Nothing too hard, the only wrinkle being to check if it's a one-digit or two-digit number so that you can make the line of underscores and the line of numbers line up properly.
def create_puzzle_output(text_string,encoder):
new_text_string = ''
line_of_blanks = ''
line_of_cipher = ''
for char in text_string:
if (char == '\n'):
new_text_string += char #empty line to give more vertical space
new_text_string += line_of_blanks[:-1] #chop off last space
new_text_string += char
new_text_string += line_of_cipher[:-1] #chop off last space
new_text_string += char
line_of_blanks = ''
line_of_cipher = ''
else:
line_of_blanks += '___ '
clue = ' ' + str(encoder[char])
while (len(clue) < len('___ ')):
clue += ' '
line_of_cipher += clue
return new_text_string
Now, we need to make an algebra problem for every character that we use. I am passing in the number that should be the answer, but other than that I want it to randomly generate some (not too large) numbers for the first coefficient and the first constant. I am also keeping it to integer numbers, at least for June. Maybe in July we'll make things a little more complex, with decimal coefficients. It is summer, after all.
def generate_components_one_variable(answer):
assert(type(answer) is int) #not strictly necessary but I like to check input types
first_coefficient = determine_sign(random.randrange(1,9,1))
first_constant = determine_sign(random.randrange(1,9,1))
second_constant = (first_coefficient * answer) + first_constant
return (first_coefficient,first_constant,second_constant)
Turn that into a string of text. I should probably tweak this to not put a plus sign in front of a negative first constant, but it is legible enough. I do also use a description in brackets instead of the character if it is a punctuation mark, because otherwise it was confusing to read.
def generate_problem_text_string(coeff1,const1,const2,letter):
if letter == '_':
printable_letter = '[space]'
elif letter == '\'':
printable_letter = '[apostrophe]'
elif letter == ',':
printable_letter = '[comma]'
elif letter == '.':
printable_letter = '[period]'
else:
printable_letter = letter
pts = '{0}x{1} + {2} = {3} {1} = ___?'.format(coeff1,printable_letter,const1,const2)
return pts
So, put it all together (along with some glue logic to call the appropriate functions at the right times, and load the text input file and write the files for the puzzle and worksheet), and you can turn this:
Once upon a time, there was a little girl named Juliet. She had a good friend who was another little girl named Wolfie. One day, they were making slime at Juliet's house, when there came a knock on the window. They looked out in surprise, and saw a grackle pecking on the glass. Most surprising was that this grackle was wearing a very small tophat.
...and turn it into this:
___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ 19 20 9 26 27 10 7 19 20 27 25 27 24 22 12 26 5 ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ 24 17 26 16 26 27 15 25 21 27 25 27 18 22 24 24 18 26 ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ 14 22 16 18 27 20 25 12 26 13 27 3 10 18 22 26 24 8 ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ 21 17 26 27 17 25 13 27 25 27 14 19 19 13 ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ 4 16 22 26 20 13 27 15 17 19 27 15 25 21 ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ 25 20 19 24 17 26 16 27 18 22 24 24 18 26 27 14 22 16 18 ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ 20 25 12 26 13 27 15 19 18 4 22 26 8 27 27 19 20 26 ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ 13 25 6 5 27 24 17 26 6 27 15 26 16 26 ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ 12 25 11 22 20 14 27 21 18 22 12 26 27 25 24 ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ 3 10 18 22 26 24 2 21 27 17 19 10 21 26 5 ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ 15 17 26 20 27 24 17 26 16 26 27 9 25 12 26 27 25 ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ 11 20 19 9 11 27 19 20 27 24 17 26 27 15 22 20 13 19 15 8 ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ 24 17 26 6 27 18 19 19 11 26 13 27 19 10 24 27 22 20 ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ 21 10 16 7 16 22 21 26 5 27 25 20 13 27 21 25 15 ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ 25 27 14 16 25 9 11 18 26 27 7 26 9 11 22 20 14 ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ 19 20 27 24 17 26 27 14 18 25 21 21 8 27 27 12 19 21 24 ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ 21 10 16 7 16 22 21 22 20 14 27 15 25 21 27 24 17 25 24 ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ 24 17 22 21 27 14 16 25 9 11 18 26 27 15 25 21 ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ 15 26 25 16 22 20 14 27 25 27 1 26 16 6 ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ 21 12 25 18 18 27 24 19 7 17 25 24 8
7*_ + -5 = 184 [space] = ___? 4*' + 5 = 13 [apostrophe] = ___? 4*, + 8 = 28 [comma] = ___? -5*. + -6 = -46 [period] = ___? 8*a + -8 = 192 a = ___? -1*c + -2 = -11 c = ___? 3*d + 6 = 45 d = ___? -4*e + -3 = -107 e = ___? -8*f + -5 = -37 f = ___? -5*g + 5 = -65 g = ___? 6*h + -1 = 101 h = ___? -4*i + 5 = -83 i = ___? -4*j + -6 = -18 j = ___? -8*k + -2 = -90 k = ___? 5*l + -7 = 83 l = ___? -5*m + 4 = -56 m = ___? 1*n + -4 = 16 n = ___? -2*o + 6 = -32 o = ___? -2*p + -6 = -20 p = ___? 5*r + -1 = 79 r = ___? -6*s + -6 = -132 s = ___? 7*t + -7 = 161 t = ___? 6*u + -2 = 58 u = ___? 8*v + 2 = 10 v = ___? 1*w + 8 = 23 w = ___? -4*y + -4 = -28 y = ___?
p.s. So, we did this throughout June, and it worked pretty well. She quickly found that, by doing a few of the most common characters first (e.g. space, 'e', 't'), she could get a good guess as to others (e.g. if it's space-t-something-e-space, she could guess that the 'something' was 'h'). That way she could just confirm her guess, instead of having to do every algebra problem frontways like I intended. I decided this was ok, for a few reasons:
So, she would typically do half a dozen problems, and then the rest would be mixed about 50/50 between doing them normally or confirming a guess.
It also turned out that it took more time to do the filling in the answers after the algebra was done, than it did to solve the math problems. This was in part because she got faster and faster at doing the algebra part as June went by. At the beginning, she liked filling in the puzzle as a mental break from the math, but later just wanted to get on with solving the algebra, so I would sometimes help her out with filling in the answers, so that she could concentrate more on the math part. I should probably make a web page that would do the substitutions for her, but this will do for now. The main objective for July is to start making some slightly more involved algebra.