Categories
Programming What I’m Up To

My solution to Advent of Code 2020’s Day 1 challenge, in Python

 

December has arrived, and so has the great programming exercise known as the Advent of Code!

Think of it as an Advent calendar, but chocolates (or cheese, or wine), you’re presented with a new programming puzzle every day between the start of December and Christmas Day, in which you try to save Santa’s mission. You can use whatever programming language you want, and you don’t need to be an expert — as the site says, “just a little programming knowledge and some problem solving skills will get you pretty far.”

Advent of Code started in 2015, and has been taking place every year ever since. The 2020 edition began on Tuesday, December 1st at 12:00 midnight Eastern time (UTC-5).

Not only do I plan on participating in this year’s Advent of Code, but I might even use a couple of the challenges in the Python class I’m currently teaching on behalf of Computer Coach.

You have to sign in to play

In order to take on Advent of Code’s challenges, you have to sign in using an account from one of these popular “federated ID” services:

  • Github
  • Google
  • Twitter
  • Reddit

This is for a couple of reasons:

  • Signing in makes it easier for the site to keep track of your progress. Advent of Code is structures so that you must successfully complete challenge n before taking on challenge (n+1).
  • While everyone has to solve the same problem, each user gets their own (presumably) unique data set.

Once you’ve signed in, you can start on the first challenge…

Spoiler alert!

Please be warned: If you want to try solving the challenge on your own and without any help, stop reading now! The remainder of this post will be all about my solution to both parts of the Day 1 challenge.

The Day 1 challenge, part one

Here’s the text from part one of the challenge:

Day 1: Report Repair

After saving Christmas five years in a row, you’ve decided to take a vacation at a nice resort on a tropical island. Surely, Christmas will go on without you.

The tropical island has its own currency and is entirely cash-only. The gold coins used there have a little picture of a starfish; the locals just call them stars. None of the currency exchanges seem to have heard of them, but somehow, you’ll need to find fifty of these coins by the time you arrive so you can pay the deposit on your room.

To save your vacation, you need to get all fifty stars by December 25th.

Collect stars by solving puzzles. Two puzzles will be made available on each day in the Advent calendar; the second puzzle is unlocked when you complete the first. Each puzzle grants one star. Good luck!

Before you leave, the Elves in accounting just need you to fix your expense report (your puzzle input); apparently, something isn’t quite adding up.

Specifically, they need you to find the two entries that sum to 2020 and then multiply those two numbers together.

For example, suppose your expense report contained the following:

1721
979
366
299
675
1456

In this list, the two entries that sum to 2020 are 1721 and 299. Multiplying them together produces 1721 * 299 = 514579, so the correct answer is 514579.

Of course, your expense report is much larger. Find the two entries that sum to 2020; what do you get if you multiply them together?

Here are the expense numbers that were provided for my account:

1140
1736
1711
1803
1825
1268
1651
2007
1923
1661
1788
1876
2003
1752
1988
1955
1568
1478
1699
1717
1828
1636
1387
1870
1658
1572
1703
1185
1569
1515
1142
1407
1587
1608
1827
1546
1808
1937
1815
1957
1401
1763
1970
1960
1853
1987
1865
1567
1664
1961
1771
1846
1971
1416
1897
633
1708
1606
515
1397
1873
1374
1969
1918
1170
1660
1494
1764
2002
1938
1396
1926
1714
1659
1805
1593
1899
1850
1644
1877
1561
1895
1985
1353
395
1919
1522
1745
1721
901
1765
1939
2009
1949
1852
1792
1749
1675
1883
1240
1868
1615
1693
1720
1388
1325
1337
867
1751
1408
1715
1942
1706
1894
1260
1945
1700
1148
1373
351
1790
1861
1755
1155
1622
1743
1872
1979
1262
1789
1305
1311
1729
1929
823
1623
2005
1932
1814
1909
1728
1592
1712
1363
1338
1804
1402
1198
264
1117
1791
1419
1229
1924
1838
1785
1982
1683
1950
1199
1984
1830
1921
1980
1834
1341
1282
1989
1854
1395
1847
1900
1913
1777
1779
1333
1800
1966
1543
1882
1375
1811
1673
1679
889
1670
1879
1312
1741
1772
1663
1776
1642
1674
1472
1580
1264
1738
1999
1637

I decided to use a Jupyter notebook running a Python kernel solve the problem.

Importing the data

My first step was to copy the numbers above, paste them into a triple-quoted string, and assign that string to the variable raw_input:

raw_input = """1140
1736
1711
1803
1825
1268
1651
2007
1923
1661
1788
1876
2003
1752
1988
1955
1568
1478
1699
1717
1828
1636
1387
1870
1658
1572
1703
1185
1569
1515
1142
1407
1587
1608
1827
1546
1808
1937
1815
1957
1401
1763
1970
1960
1853
1987
1865
1567
1664
1961
1771
1846
1971
1416
1897
633
1708
1606
515
1397
1873
1374
1969
1918
1170
1660
1494
1764
2002
1938
1396
1926
1714
1659
1805
1593
1899
1850
1644
1877
1561
1895
1985
1353
395
1919
1522
1745
1721
901
1765
1939
2009
1949
1852
1792
1749
1675
1883
1240
1868
1615
1693
1720
1388
1325
1337
867
1751
1408
1715
1942
1706
1894
1260
1945
1700
1148
1373
351
1790
1861
1755
1155
1622
1743
1872
1979
1262
1789
1305
1311
1729
1929
823
1623
2005
1932
1814
1909
1728
1592
1712
1363
1338
1804
1402
1198
264
1117
1791
1419
1229
1924
1838
1785
1982
1683
1950
1199
1984
1830
1921
1980
1834
1341
1282
1989
1854
1395
1847
1900
1913
1777
1779
1333
1800
1966
1543
1882
1375
1811
1673
1679
889
1670
1879
1312
1741
1772
1663
1776
1642
1674
1472
1580
1264
1738
1999
1637"""

Now that I had the data in a string, I could split the string into an array, using the newline character as the delimiter. I named the array split_input:

>>> split_input = raw_input.split("\n")
>>> split_input
['1140', '1736', '1711', '1803', '1825', '1268', '1651', '2007', '1923', '1661', '1788', '1876', '2003', '1752', '1988', '1955', '1568', '1478', '1699', '1717', '1828', '1636', '1387', '1870', '1658', '1572', '1703', '1185', '1569', '1515', '1142', '1407', '1587', '1608', '1827', '1546', '1808', '1937', '1815', '1957', '1401', '1763', '1970', '1960', '1853', '1987', '1865', '1567', '1664', '1961', '1771', '1846', '1971', '1416', '1897', '633', '1708', '1606', '515', '1397', '1873', '1374', '1969', '1918', '1170', '1660', '1494', '1764', '2002', '1938', '1396', '1926', '1714', '1659', '1805', '1593', '1899', '1850', '1644', '1877', '1561', '1895', '1985', '1353', '395', '1919', '1522', '1745', '1721', '901', '1765', '1939', '2009', '1949', '1852', '1792', '1749', '1675', '1883', '1240', '1868', '1615', '1693', '1720', '1388', '1325', '1337', '867', '1751', '1408', '1715', '1942', '1706', '1894', '1260', '1945', '1700', '1148', '1373', '351', '1790', '1861', '1755', '1155', '1622', '1743', '1872', '1979', '1262', '1789', '1305', '1311', '1729', '1929', '823', '1623', '2005', '1932', '1814', '1909', '1728', '1592', '1712', '1363', '1338', '1804', '1402', '1198', '264', '1117', '1791', '1419', '1229', '1924', '1838', '1785', '1982', '1683', '1950', '1199', '1984', '1830', '1921', '1980', '1834', '1341', '1282', '1989', '1854', '1395', '1847', '1900', '1913', '1777', '1779', '1333', '1800', '1966', '1543', '1882', '1375', '1811', '1673', '1679', '889', '1670', '1879', '1312', '1741', '1772', '1663', '1776', '1642', '1674', '1472', '1580', '1264', '1738', '1999', '1637']

split_input is an array of strings which needed to be converted into integer values.

In many other languages, I’d do this by using the map function to apply a “convert a string to its integer value” function to every item in the array, creating a resulting array called expenses. Here’s the Python version of that approach:

>>> expenses = list(map(int, split_input))
>>> expenses
[1140, 1736, 1711, 1803, 1825, 1268, 1651, 2007, 1923, 1661, 1788, 1876, 2003, 1752, 1988, 1955, 1568, 1478, 1699, 1717, 1828, 1636, 1387, 1870, 1658, 1572, 1703, 1185, 1569, 1515, 1142, 1407, 1587, 1608, 1827, 1546, 1808, 1937, 1815, 1957, 1401, 1763, 1970, 1960, 1853, 1987, 1865, 1567, 1664, 1961, 1771, 1846, 1971, 1416, 1897, 633, 1708, 1606, 515, 1397, 1873, 1374, 1969, 1918, 1170, 1660, 1494, 1764, 2002, 1938, 1396, 1926, 1714, 1659, 1805, 1593, 1899, 1850, 1644, 1877, 1561, 1895, 1985, 1353, 395, 1919, 1522, 1745, 1721, 901, 1765, 1939, 2009, 1949, 1852, 1792, 1749, 1675, 1883, 1240, 1868, 1615, 1693, 1720, 1388, 1325, 1337, 867, 1751, 1408, 1715, 1942, 1706, 1894, 1260, 1945, 1700, 1148, 1373, 351, 1790, 1861, 1755, 1155, 1622, 1743, 1872, 1979, 1262, 1789, 1305, 1311, 1729, 1929, 823, 1623, 2005, 1932, 1814, 1909, 1728, 1592, 1712, 1363, 1338, 1804, 1402, 1198, 264, 1117, 1791, 1419, 1229, 1924, 1838, 1785, 1982, 1683, 1950, 1199, 1984, 1830, 1921, 1980, 1834, 1341, 1282, 1989, 1854, 1395, 1847, 1900, 1913, 1777, 1779, 1333, 1800, 1966, 1543, 1882, 1375, 1811, 1673, 1679, 889, 1670, 1879, 1312, 1741, 1772, 1663, 1776, 1642, 1674, 1472, 1580, 1264, 1738, 1999, 1637]

It works, but from a Python programming point of view, it just doesn’t feel right.

The Pythonic approach would involve using a list comprehension instead of map (and then using the resulting iterator into a list). It just seems more readable:

>>> expenses = [int(string) for string in split_input]
>>> expenses
[1140, 1736, 1711, 1803, 1825, 1268, 1651, 2007, 1923, 1661, 1788, 1876, 2003, 1752, 1988, 1955, 1568, 1478, 1699, 1717, 1828, 1636, 1387, 1870, 1658, 1572, 1703, 1185, 1569, 1515, 1142, 1407, 1587, 1608, 1827, 1546, 1808, 1937, 1815, 1957, 1401, 1763, 1970, 1960, 1853, 1987, 1865, 1567, 1664, 1961, 1771, 1846, 1971, 1416, 1897, 633, 1708, 1606, 515, 1397, 1873, 1374, 1969, 1918, 1170, 1660, 1494, 1764, 2002, 1938, 1396, 1926, 1714, 1659, 1805, 1593, 1899, 1850, 1644, 1877, 1561, 1895, 1985, 1353, 395, 1919, 1522, 1745, 1721, 901, 1765, 1939, 2009, 1949, 1852, 1792, 1749, 1675, 1883, 1240, 1868, 1615, 1693, 1720, 1388, 1325, 1337, 867, 1751, 1408, 1715, 1942, 1706, 1894, 1260, 1945, 1700, 1148, 1373, 351, 1790, 1861, 1755, 1155, 1622, 1743, 1872, 1979, 1262, 1789, 1305, 1311, 1729, 1929, 823, 1623, 2005, 1932, 1814, 1909, 1728, 1592, 1712, 1363, 1338, 1804, 1402, 1198, 264, 1117, 1791, 1419, 1229, 1924, 1838, 1785, 1982, 1683, 1950, 1199, 1984, 1830, 1921, 1980, 1834, 1341, 1282, 1989, 1854, 1395, 1847, 1900, 1913, 1777, 1779, 1333, 1800, 1966, 1543, 1882, 1375, 1811, 1673, 1679, 889, 1670, 1879, 1312, 1741, 1772, 1663, 1776, 1642, 1674, 1472, 1580, 1264, 1738, 1999, 1637]

Now that I had the expenses in a Python list (that’s Pythonese for “array”), I could work with them.

Combinations to the rescue!

Once again, the goal of the challenge was to find the two numbers in the expense report whose sum was 2020.

To solve this problem, we need a way to generate all the possible combinations of two numbers taken from the list. I could write this code, but Python’s itertools module has a combinations() method that can do just that.

Here’s a quick demo of combinations() in action. Given a list containing a small number of integers, it generates a list of the possible 2-number combinations you can get from the list, without repetition (that is, a number can’t appear more than once in any combination):

>>> from itertools import *
>>> simple_list = [1, 3, 5, 7, 9]
>>> list(combinations(simple_list, 2))
[(1, 3), (1, 5), (1, 7), (1, 9), (3, 5), (3, 7), (3, 9), (5, 7), (5, 9), (7, 9)]

itertools also has a combinations_with_replacement() method. Rather than tell you what it does, let me show you:

>>> list(combinations_with_replacement(simple_list, 2))
[(1, 1), (1, 3), (1, 5), (1, 7), (1, 9), (3, 3), (3, 5), (3, 7), (3, 9), (5, 5), (5, 7), (5, 9), (7, 7), (7, 9), (9, 9)]

With that in mind, I used combinations() to generate a list of all the possible two-number combinations in expenses, which I assigned to a variable named all_expense_pairs:

>>> all_expense_pairs = list(combinations(expenses, 2))
>>> len(all_expense_pairs)
19900

Now that we have all the possible two-number combinations from the expense report, we can try to find the one(s) whose numbers add up to 2020.

Any time you’re in a situation where you need to find values in an array that match some criteria, you should think about applying a filter() function. I did just that: I used a filter() to extract a list of only those pairs summed to 2020…

def sums_to_2020(values):
    return sum(values) == 2020

>>> result = list(filter(sums_to_2020, all_expense_pairs))
>>> result
[(1387, 633)]

The resulting list had one tuple, (1387, 633), whose values sum to 2020. I entered the product of these two numbers — 877971 — and completed the first challenge.

The Day 1 challenge, part two

Here’s the text from part two:

The Elves in accounting are thankful for your help; one of them even offers you a starfish coin they had left over from a past vacation. They offer you a second one if you can find three numbers in your expense report that meet the same criteria.

Using the above example again, the three entries that sum to 2020 are 979366, and 675. Multiplying them together produces the answer, 241861950.

In your expense report, what is the product of the three entries that sum to 2020?

Had I solved the problem from first principles, the solution might have taken a lot of extra work. Thanks to the use of itertools.combinations(), the solution for part two took three lines of code:

>>> all_expense_triplets = list(combinations(expenses, 3))
>>> result2 = list(filter(sums_to_2020, all_expense_triplets))
>>> result2
[(867, 264, 889)]

Once again, the resulting list had one tuple, (867, 264, 889), and its values, added up, were 2020. I entered the product of these three numbers — 203481432 — and completed the second challenge.

Feeling simultaneously proud and soiled

Thanks to Python (and remembering that it had a library that could do combinations and permutations),  I made a personal best in solving the Day 1 puzzles. I’m pretty pleased, but at the same time, I did so little work that it feels as if I’ve cheated. I may have to try solving the problem from first principles — if I have the time.

Other days’ solutions:

Here are my solutions for other days in Advent of Code 2020:

Categories
Programming

The Advent of Code is coming in a few days!

We’re only a few days from December, which means it will soon be time for the great programming exercise known as the Advent of Code!

Think of it as an Advent calendar, but chocolates (or cheese, or wine), you’re presented with a new programming puzzle every day between the start of December and Christmas Day, in which you try to save Santa’s mission. You can use whatever programming language you want, and you don’t need to be an expert — as the site says, “just a little programming knowledge and some problem solving skills will get you pretty far.”

Advent of Code started in 2015, and has been taking place every year ever since. The 2020 edition begins on Tuesday, December 1st at 12:00 midnight Eastern time (UTC-5).

Not only do I plan on participating in this year’s Advent of Code, but I might even use a couple of the challenges in the Python class I’m currently teaching on behalf of Computer Coach.

Solving Advent of Code 2019’s day one challenge

Here’s the premise of the 2019 Advent of Code’s challenges: Santa is stuck at the edge of the solar system, and you have to rescue him. Each day’s challenge, which has two parts, gets you closer to bringing him home and saving Christmas.

Day one challenge, part one

Here’s the first part of day one’s challenge:

The Elves quickly load you into a spacecraft and prepare to launch.

At the first Go / No Go poll, every Elf is Go until the Fuel Counter-Upper. They haven’t determined the amount of fuel required yet.

Fuel required to launch a given module is based on its mass. Specifically, to find the fuel required for a module, take its mass, divide by three, round down, and subtract 2.

For example:

  • For a mass of 12, divide by 3 and round down to get 4, then subtract 2 to get 2.
  • For a mass of 14, dividing by 3 and rounding down still yields 4, so the fuel required is also 2.
  • For a mass of 1969, the fuel required is 654.
  • For a mass of 100756, the fuel required is 33583.

The Fuel Counter-Upper needs to know the total fuel requirement. To find it, individually calculate the fuel needed for the mass of each module (your puzzle input), then add together all the fuel values.

What is the sum of the fuel requirements for all of the modules on your spacecraft?

While the problems in the Advent of Code are the same for every participant, the data for each participant is different (there’s a sign-up process, which gives you an account, your own progress tracker, and your own data). This prevents participants from simply sharing the solution.

Here are the module masses that were provided for my account:

134492
88713
84405
148193
95951
63545
137840
65558
124836
95431
77622
91864
108677
116871
119496
97172
86115
105704
68613
77114
114013
52766
57048
80814
73888
58253
135934
97409
112439
98262
116047
57456
124261
83006
101495
133449
111372
56146
87818
92209
149259
124559
141838
147988
65703
125566
59650
139564
92430
126307
120406
147383
84362
51529
146366
131840
53270
71886
118767
104311
126181
76964
129430
95489
91098
54133
110057
107276
118226
96104
135382
85152
61697
143417
148879
126846
130205
111170
86687
113729
123330
56976
148470
66028
129715
75686
74964
148258
72669
88809
78173
92699
124806
67217
139066
136002
135730
145708
142054
135772

I decided to use the Python REPL to tackle this problem.

My first step was to copy the numbers above, paste them into a triple-quoted string, and assign that string to the variable raw_input:

raw_input = """134492
88713
84405
148193
95951
63545
137840
65558
124836
95431
77622
91864
108677
116871
119496
97172
86115
105704
68613
77114
114013
52766
57048
80814
73888
58253
135934
97409
112439
98262
116047
57456
124261
83006
101495
133449
111372
56146
87818
92209
149259
124559
141838
147988
65703
125566
59650
139564
92430
126307
120406
147383
84362
51529
146366
131840
53270
71886
118767
104311
126181
76964
129430
95489
91098
54133
110057
107276
118226
96104
135382
85152
61697
143417
148879
126846
130205
111170
86687
113729
123330
56976
148470
66028
129715
75686
74964
148258
72669
88809
78173
92699
124806
67217
139066
136002
135730
145708
142054
135772"""

Now that I had the data in a string, I could split the string into an array, using the newline character as the delimiter. I named the array split_input:

>>> split_input = raw_input.split("\n")
>>> split_input
['134492', '88713', '84405', '148193', '95951', '63545', '137840', '65558', '124836', '95431', '77622', '91864', '108677', '116871', '119496', '97172', '86115', '105704', '68613', '77114', '114013', '52766', '57048', '80814', '73888', '58253', '135934', '97409', '112439', '98262', '116047', '57456', '124261', '83006', '101495', '133449', '111372', '56146', '87818', '92209', '149259', '124559', '141838', '147988', '65703', '125566', '59650', '139564', '92430', '126307', '120406', '147383', '84362', '51529', '146366', '131840', '53270', '71886', '118767', '104311', '126181', '76964', '129430', '95489', '91098', '54133', '110057', '107276', '118226', '96104', '135382', '85152', '61697', '143417', '148879', '126846', '130205', '111170', '86687', '113729', '123330', '56976', '148470', '66028', '129715', '75686', '74964', '148258', '72669', '88809', '78173', '92699', '124806', '67217', '139066', '136002', '135730', '145708', '142054', '135772']

split_input is an array of strings which needed to be converted into integer values.

In many other languages, I’d do this by using the map function to apply a “convert a string to its integer value” function to every item in the array, creating a resulting array called masses. Here’s the Python version of that approach:

>>> masses = list(map(int, split_input))
>>> masses
[134492, 88713, 84405, 148193, 95951, 63545, 137840, 65558, 124836, 95431, 77622, 91864, 108677, 116871, 119496, 97172, 86115, 105704, 68613, 77114, 114013, 52766, 57048, 80814, 73888, 58253, 135934, 97409, 112439, 98262, 116047, 57456, 124261, 83006, 101495, 133449, 111372, 56146, 87818, 92209, 149259, 124559, 141838, 147988, 65703, 125566, 59650, 139564, 92430, 126307, 120406, 147383, 84362, 51529, 146366, 131840, 53270, 71886, 118767, 104311, 126181, 76964, 129430, 95489, 91098, 54133, 110057, 107276, 118226, 96104, 135382, 85152, 61697, 143417, 148879, 126846, 130205, 111170, 86687, 113729, 123330, 56976, 148470, 66028, 129715, 75686, 74964, 148258, 72669, 88809, 78173, 92699, 124806, 67217, 139066, 136002, 135730, 145708, 142054, 135772]

It works, but from a Python programming point of view, it just doesn’t feel right.

The Pythonic approach would involve using a list comprehension instead of map (and then using the resulting iterator into a list). It just seems more readable:

>>> masses = [int(string) for string in split_input]
>>> masses
[134492, 88713, 84405, 148193, 95951, 63545, 137840, 65558, 124836, 95431, 77622, 91864, 108677, 116871, 119496, 97172, 86115, 105704, 68613, 77114, 114013, 52766, 57048, 80814, 73888, 58253, 135934, 97409, 112439, 98262, 116047, 57456, 124261, 83006, 101495, 133449, 111372, 56146, 87818, 92209, 149259, 124559, 141838, 147988, 65703, 125566, 59650, 139564, 92430, 126307, 120406, 147383, 84362, 51529, 146366, 131840, 53270, 71886, 118767, 104311, 126181, 76964, 129430, 95489, 91098, 54133, 110057, 107276, 118226, 96104, 135382, 85152, 61697, 143417, 148879, 126846, 130205, 111170, 86687, 113729, 123330, 56976, 148470, 66028, 129715, 75686, 74964, 148258, 72669, 88809, 78173, 92699, 124806, 67217, 139066, 136002, 135730, 145708, 142054, 135772]

Once I had the masses, I could then calculate the fuel requirements for each mass. This may be the only time I’ve ever made use of Python’s floor division operator, which performs an integer division, rounding down:

>>> fuel_requirements = list(map(lambda mass: mass // 3 - 2, masses))
>>> fuel_requirements
[44828, 29569, 28133, 49395, 31981, 21179, 45944, 21850, 41610, 31808, 25872, 30619, 36223, 38955, 39830, 32388, 28703, 35232, 22869, 25702, 38002, 17586, 19014, 26936, 24627, 19415, 45309, 32467, 37477, 32752, 38680, 19150, 41418, 27666, 33829, 44481, 37122, 18713, 29270, 30734, 49751, 41517, 47277, 49327, 21899, 41853, 19881, 46519, 30808, 42100, 40133, 49125, 28118, 17174, 48786, 43944, 17754, 23960, 39587, 34768, 42058, 25652, 43141, 31827, 30364, 18042, 36683, 35756, 39406, 32032, 45125, 28382, 20563, 47803, 49624, 42280, 43399, 37054, 28893, 37907, 41108, 18990, 49488, 22007, 43236, 25226, 24986, 49417, 24221, 29601, 26055, 30897, 41600, 22403, 46353, 45332, 45241, 48567, 47349, 45255]

The map/list/lambda approach works just fine, but once again, a list comprehension just seems more elegant to my eye:

>>> fuel_requirements = [mass // 3 - 2 for mass in masses]
>>> fuel_requirements
[44828, 29569, 28133, 49395, 31981, 21179, 45944, 21850, 41610, 31808, 25872, 30619, 36223, 38955, 39830, 32388, 28703, 35232, 22869, 25702, 38002, 17586, 19014, 26936, 24627, 19415, 45309, 32467, 37477, 32752, 38680, 19150, 41418, 27666, 33829, 44481, 37122, 18713, 29270, 30734, 49751, 41517, 47277, 49327, 21899, 41853, 19881, 46519, 30808, 42100, 40133, 49125, 28118, 17174, 48786, 43944, 17754, 23960, 39587, 34768, 42058, 25652, 43141, 31827, 30364, 18042, 36683, 35756, 39406, 32032, 45125, 28382, 20563, 47803, 49624, 42280, 43399, 37054, 28893, 37907, 41108, 18990, 49488, 22007, 43236, 25226, 24986, 49417, 24221, 29601, 26055, 30897, 41600, 22403, 46353, 45332, 45241, 48567, 47349, 45255]

And now that I had the fuel requirements for each module, all I had to do was get their sum:

>>> total_fuel = sum(fuel_requirements)
>>> total_fuel
3454942

I entered the value for total_fuel into the solution text field, and Advent of Code immediately told me that I had solved part one of the day one challenge! So far, so good.

Day one challenge, part two

Christine Darden, one of the “Hidden Figures” mathematicians who helped land the astronauts on the Moon.

Part two of the challenge was a refinement of part one:

During the second Go / No Go poll, the Elf in charge of the Rocket Equation Double-Checker stops the launch sequence. Apparently, you forgot to include additional fuel for the fuel you just added.

Fuel itself requires fuel just like a module – take its mass, divide by three, round down, and subtract 2. However, that fuel also requires fuel, and that fuel requires fuel, and so on. Any mass that would require negative fuel should instead be treated as if it requires zero fuel; the remaining mass, if any, is instead handled by wishing really hard, which has no mass and is outside the scope of this calculation.

So, for each module mass, calculate its fuel and add it to the total. Then, treat the fuel amount you just calculated as the input mass and repeat the process, continuing until a fuel requirement is zero or negative. For example:

  • A module of mass 14 requires 2 fuel. This fuel requires no further fuel (2 divided by 3 and rounded down is 0, which would call for a negative fuel), so the total fuel required is still just 2.
  • At first, a module of mass 1969 requires 654 fuel. Then, this fuel requires 216 more fuel (654 / 3 - 2). 216 then requires 70 more fuel, which requires 21 fuel, which requires 5 fuel, which requires no further fuel. So, the total fuel required for a module of mass 1969 is 654 + 216 + 70 + 21 + 5 = 966.
  • The fuel required by a module of mass 100756 and its fuel is: 33583 + 11192 + 3728 + 1240 + 411 + 135 + 43 + 12 + 2 = 50346.

What is the sum of the fuel requirements for all of the modules on your spacecraft when also taking into account the mass of the added fuel? (Calculate the fuel requirements for each module separately, then add them all up at the end.)

Upon reading this, my first thought was:

The trick to writing recursive functions is to solve the “escape” case first — that is, the case where you stop the recursion and just return a value.

For this problem, the “escape” case is when the repeated fuel calculation gives a result of 0 or less:

if result <= 0:
  return 0

Otherwise, take the result, and apply the fuel calculation to it again. That’s what gives us the recursive part. Here’s the resulting if statement:

if result <= 0: 
  return 0
else: 
  return result + fuel_required(result)

And finally, we have to handle the initial calculation. The end result is the fuel_required function:

def fuel_required(mass):
  result = mass // 3 - 2
  if result <= 0:
    return 0
  else:
    return result + fuel_required(result)

Now that we have the fuel_required function, we can apply it to every item in the masses array from part one:

>>> updated_fuel_requirements = [fuel_required(mass) for mass in masses]
>>> updated_fuel_requirements
[67212, 44325, 42171, 74062, 47941, 31741, 68888, 32746, 62387, 47682, 38780, 45902, 54306, 58402, 59715, 48553, 43027, 52822, 34277, 38525, 56974, 26354, 28495, 40375, 36913, 29094, 67934, 48670, 56187, 49098, 57991, 28698, 62098, 41470, 50716, 66693, 55654, 28043, 43877, 46073, 74596, 62245, 70886, 73962, 32822, 62751, 29795, 69750, 46184, 63121, 60171, 73658, 42148, 25734, 73150, 65887, 26606, 35910, 59351, 52123, 63058, 38449, 64681, 47710, 45516, 27037, 54994, 53606, 59081, 48018, 67657, 42543, 30817, 71674, 74407, 63393, 65068, 55551, 43311, 56833, 61632, 28459, 74203, 32983, 64824, 37810, 37450, 74096, 36304, 44373, 39054, 46318, 62371, 33576, 69500, 67968, 67832, 72820, 70993, 67853]

This yields the updated fuel requirements for each module. The final step was to get their sum:

>>> updated_total_fuel = sum(updated_fuel_requirements)
>>> updated_total_fuel
5179544

Entering the value of updated_total_fuel into the solution text field completed the day one challenge.

Categories
Programming

“My code isn’t working” is a great problems-and-solutions flowchart for Python programmers

Tap to view at full size.

Dr. Martin Jones, the author behind the book and site Python for Biologists, has come up with a chart to help Python programmers when their code doesn’t work and they can’t figure out why.

He writes:

A few times a year, I have the job of teaching a bunch of people who have never written code before how to program from scratch. The nature of programming being what it is, the same error crop up every time in a very predictable pattern. I usually encourage my students to go through a step-by-step troubleshooting process when trying to fix misbehaving code, in which we go through these common errors one by one and see if they could be causing the problem. Today, I decided to finally write this troubleshooting process down and turn it into a flowchart in non-threatening colours.

Behold, the “my code isn’t working” step-by-step troubleshooting guide! Follow the arrows to find the likely cause of your problem – if the first thing you reach doesn’t work, then back up and try again.

It’s intended for programmers who are new to Python, but even experienced Pythonistas sometimes get distracted and stuck on simple things. I’m keeping a copy handy.

You can tap on the image above to view it at full size, and there’s also a printable PDF version as well.

[ Thanks to my UC Baseline classmate Daniel Jimenez for the find! ]

Categories
Programming

“I hope Ren’Py will notice me!”

What is Ren’Py?

Ren’Py is a tool for creating visual novels and an engine for running them.

There are a couple of ways to think of visual novels:

  1. As a “Choose Your Own Adventure”-style book, but in electronic form, and backed with visuals, sound effects, music, and interactivity, or
  2. As a story-driven, turn-based multimedia game, which can fit any number of genres, including adventures, simulations, or role-playing games.

Why is it called Ren’Py?

Ren’Py is a portmanteau of renai (恋愛), Japanese for “romantic love”…

…and Python, the programming language in which it’s implemented, and one of the languages you can use to create Ren’Py visual novels / games.

How much programming do I need to know to make visual novels or games in Ren’Py?

You’ve got options!

  • If you’re new to programming, Ren’py provides a scripting language that’s easy enough to let you get started writing visual novels after a couple of minutes’ worth of learning, but powerful enough to add a surprising amount of interactivity.
  • If you know Python or are an experienced programmer, you can harness the entire Python language and its libraries and geek out to your heart’s content.

And, yes, you can program using a mix of both Ren’Py’s programming language and Python.

What platforms can I develop Ren’Py visual novels and games on?

You can run the Ren’Py development tool on Windows, macOS, and Linux…

…and with a little work, you can even do Ren’Py development on a Raspberry Pi!

Aside from Ren’Py, do I need to install anything else?

You’ll need a text editor specifically made for coding — feel free to use your favorite one.

Of course, if you also want to create you own graphics, sound effects, and music for your visual novel or game, you’ll need the appropriate software for those tasks as well.

Once I’ve made a visual novel or game in Ren’Py, how can other people play it?

 

Ren’py games can be easily packaged for the following platforms:

  • Windows
  • macOS
  • Linux

With a little extra work, they can also be packaged for these platforms:

  • iOS
  • Android
  • Web (currently in beta)

And with even more work (and the right amount of luck), they can be deployed on these platforms:

  • Steam
  • Playstation
  • Xbox
  • Nintendo Switch

How do I get started?

You can get started by downloading the current version of Ren’py from the “Latest” page on renpy.org. At the time of writing, the latest version is 7.3.5, code-named “The world (wide web) is not enough”, released October 17, 2019.

Ren’Py comes with two projects:

There’s Tutorial, a Ren’Py tutorial in visual novel form, where “Professor Eileen” takes you through the basics. It’s well worth going through at least once.

There’s also The Question, which introduces you to the concept of visual novels, the interactivity, and that never-ending visual novel and anime trope, “Boy is too shy to ask the girl of his dreams on a date.”

Be sure to look at the code for both — you’ll pick up a lot just by reading it.

Watch this blog!

I’ll post some Ren’Py articles and links to the source code for a couple of games soon.

 

Categories
Programming What I’m Up To

Computer Coach’s “Intro to Python Coding” course (taught by Yours Truly) starts tonight!

The online Intro to Python Coding course that I’m teaching on behalf of Tampa Bay’s own Computer Coach Training Center starts tonight at 6:00 p.m.. For the next five weeks, on Monday and Wednesday evenings from 6:00 to 10:00, I’ll be leading a class of Python learners through “code along with me” exercises in the Python programming language.

Photo: Joey deVilla points at a projected screen of code with co-presented Angela Don.
Dropping code science at BarCamp.

The format of the course will be pretty much the same as the one I use at Tampa iOS Meetup, where I lead the group through a “code along with me” exercise. I project what’s on my computer on the big screen, and everyone follows along, entering the code as I explain what’s happening.

Since Python has a REPL (Read-Evaluate-Print Loop), I can also have the class go through some exercises and try little coding challenges. It will be a “learn by doing” kind of class.

The main textbooks for the course (which will be provided to students) are Python Crash Course, 2nd Edition…

Book cover: “Python Crash Course, 2nd edition: A Hands-On, Project-Based Introduction to Programming”

…and Automate the Boring Stuff with Python, 2nd edition (which is free to read online):

Book cover: “Automate the Boring Stuff with Python, 2nd edition: Practical Programming for Total Beginners”In order to minimize confusion, we’ll all use the same tools in the course, namely the Anaconda Individual Edition distribution of Python 3.7 and associated tools…Logo: Anaconda…and Visual Studio Code:

Logo: Visual Studio CodeBoth are available free of charge, and run on macOS, Windows, and Linux.

It’ll be fun! Watch this space; I’ll post some snippets from the course as it progresses.

Interested in signing up? Visit Computer Coach’s site and speak to them. Don’t dawdle — it starts tonight!

Categories
Current Events Programming What I’m Up To

I’m teaching an online Python programming course!

Photo: Man’s hand on Mac laptop, with Python book on the side. Caption: “Intro to Python course / Starts this Monday!”

Graohic: Computer Coach Training Center logoI’ll be teaching a live online course on Python programming on behalf of Computer Coach Training Center starting Monday. Here are the details:

  • What: Intro to Python Coding course
  • When: Monday and Wednesday evenings, 6:00 – 10:00 p.m., starting Monday, July 13 and ending Wednesday, August 12 (6 weeks, twice a week)
  • Where: Online.
  • How much: $900 — and Computer Coach has grants that can cover the cost if you’re unemployed and based in the Tampa Bay area (contact them to see if you qualify)
  • What you’ll need:
    • A computer that was made sometime in the last ten years. My main computer is a 2014-era MacBook Pro, but I’ll be doing demonstrations on a 2012-era Lenovo ThinkPad running Linux Mint, a 2009-era Compaq laptop running Peppermint Linux, and a $35 Raspberry Pi.
    • An internet connection. This is an online course, after all.

To register for this course, visit this page and tap the Attend Online button. Someone from Computer Coach will contact you.

Screenshot: The Meetup page for the Python course, with the “Attend online” button highlighted.

The course description

Photo: Woman’s hands typing on Mac laptop.

This is an introduction to the Python programming language. Now in the top 10 programming languages according to the TIOBE Programming Language Index, it is versatile enough to have a wide array of uses, from simple scripting to powering Instagram, Spotify, Netflix, Dropbox, and more. Its combination of simplicity and vast scientific and math libraries have made it the preferred programming language for data science and machine learning. If you’re looking for a first programming language, Python is an excellent choice.

 

This is not a passive course! This isn’t the kind of course where the instructor lectures over slides while you take notes (or pretend to take notes while surfing the web or checking your social media feeds). In this course, you’ll be actively taking part in the learning process, entering code, experimenting, making mistakes, correcting those mistakes, and producing working applications. You will learn by doing. At the end of each session, you’ll have a collection of little Python programs that you wrote, and which you can use as the basis for your own work.

The course will start at the most basic level by walking you through the process of downloading and installing the necessary tools to start Python programming. From there, you’ll learn the building blocks of the Python programming language:

  • Control structures that determine what your programs do,
  • Data structures to store the information that your programs act on,
  • Functions and objects to organize your code, and
  • Using libraries as building blocks for your applications.

You’ll write all sorts of programs…

  • You’ll use Python in “immediate mode” to perform quick calculations (and you’ll sharpen your command-line skills in the process).
  • You’ll write scripts to simplify or automate tedious tasks.
  • You’ll build web applications.
  • And since it’s a networked, data-driven world where no application is an island, you’ll learn how to use Python to interact with web services and databases.

Better still, you’ll learn how to think like a programmer. You’ll learn how to look at a goal and learn how you could write a program to meet it, and how that program could be improved or enhanced. You’ll learn skills that will serve you well as you take up other programming languages, and even learn a little bit about the inner workings of computers, operating systems, and the internet.

 

Categories
Programming

Converting a number into words, this time with Python and inflect.py

Teaching a person how to spell out numbers involves a lot of repetition. Tampa Bay’s own Jack Hartmann, whose children’s educational YouTube channel has over a million subscribers and 300 million views, knows this. He’s got a video that teaches kids the words for the numbers 0 through 10:

Don’t underestimate the power of videos for kids — Jack’s laughing all the way to the bank. This online estimator says that his YouTube channel should be earning about $70,000 every month, and keep in mind that his particular line of work has probably benefited from everyone being stuck at home. I may have to do something similar with the accordion when this software fad passes.

If you just wanted to be able to convert any number from 0 through 10 into word form in Python, you could use a list…

number_words = ['zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten']

…and if you wanted the number 3 in word form, you’d use this:

# This is in the Python REPL
>>> number_words[3]
'three'

You wouldn’t want to take this approach for a larger set of numbers, and you probably wouldn’t want to code it yourself. Luckily, you don’t have to do this in Python, thanks to the inflect.py module.

Using inflect.py

Pythoninflect.py is a module that does all sorts of processing to make your programs’ text output grammatically correct. If you hate seeing output like this…

You have 1 items in your cart.

…or this…

You have a egg in your inventory.

…you can use inflect.py to automatically use the correct singular or plural form, use “a” or “an” when appropriate, and so much more.

(I’ll cover inflect.py in greater detail in a future article.)

In addition to all these grammatical goodies, inflect.py can also be used to convert numbers to words.

To use inflect.py, you’ll need to install it first. The simplest way to do so is with pip:

pip install inflect

Once installed, you can use it in your Python programs. Here’s an example:

import inflect

inflector = inflect.engine()

words = inflector.number_to_words(54321)
print(words)

It produces this output:

fifty-four thousand, three hundred and twenty-one

The number_to_words() method has a number of optional parameters that are useful in certain circumstances. For instance, there’s the boolean wantlist parameter, which causes the word output to be broken into “chunks”:

words = inflector.number_to_words(54321, wantlist=True)

It produces this output:

[‘fifty-four thousand’, ‘three hundred and twenty-one’]

Suppose you want the number to be converted into its individual digits as words. You’d use the group parameter:

# This is in the Python REPL

>>> inflector.number_to_words(54321, group=1)
'five, four, three, two, one'

>>> inflector.number_to_words(54321, group=2)
'fifty-four, thirty-two, one'

>>> inflector.number_to_words(54321, group=3)
'five forty-three, twenty-one'

What if you’re using the group parameter set to 1, but want to get all UK English and have it use the word “naught” for zero? Or maybe you want your program to sound like a film noir gangster and say “zip” instead? Or you want it recite a phone number and say “oh”? That’s what the zero parameter is for:

# This is in the Python REPL

>>> inflector.number_to_words(13057, group=1, zero='naught')
'one, three, naught, five, seven'

>>> inflector.number_to_words(13057, group=1, zero='zip')
'one, three, zip, five, seven'

>>> inflector.number_to_words(8675309, group=1, zero='oh')
'eight, six, seven, five, three, oh, nine'

The one parameter does the same thing, but for the digit 1:

# This is in the Python REPL

>>> inflector.number_to_words(13057, group=1, one='unity')
'unity, three, zero, five, seven'

Want to get all Star Trek? Use the decimal parameter to change the default decimal word to “mark”.

# This is in the Python REPL

>>> coordinates = inflector.number_to_words(123.789, group=1, decimal='mark')
>>> print(f"Ensign Crusher, set course to {coordinates}. Engage.")
Ensign Crusher, set course to one, two, three, mark, seven, eight, nine. Engage.

A lot of style guides tell you to spell out the numbers zero through ten, and use the number form for numbers 11 and greater. The threshold parameter makes this easy:

# This is in the Python REPL

>>> inflector.number_to_words(9, threshold=10)
'nine'

>>> inflector.number_to_words(10, threshold=10)
'ten'

>>> inflector.number_to_words(11, threshold=10)
'11'

Go ahead — import inflect.py and play with it. There’s a lot of power in that module, and it goes way beyond just converting words to numbers!

Also worth checking out

If you’re an iOS/macOS programmer, you’ll want to look at the previous article, Converting a number from a numeric form into words in just four lines of Swift.