Of course, the proper comeback is: jeffy is not in the sudoers file. This incident will be reported...TO MOM!
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
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
are1721
and299
. Multiplying them together produces1721 * 299 = 514579
, so the correct answer is514579
.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
are979
,366
, and675
. 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:
Once again, here’s the weekly list of events for events for Tampa Bay techies, entrepreneurs, and nerds. Every week, on GlobalNerdy.com and on the mailing list, I scour the announcements for events that are interesting to or useful for those of you who are building the future here in “The Other Bay Area, on The Other West Coast”.
This list covers events from Monday, November 30 through Sunday, December 6, 2020. Yup, we’ll be in December by the end of the week!
Events — especially virtual, online ones — can pop up at the last minute. I add them to the list as I find out about them. Come back and check this article from time to time, as you might find a new listing that wasn’t there before!
Why this list doesn’t feature any in-person events
It’s because I’m trying to promote responsible behavior in the face of news stories like the ones below:
- CNN — US is ’rounding the corner into a calamity,’ expert says, with Covid-19 deaths projected to double soon
- New York Times — U.S. coronavirus cases pass 4 million for the month of November, doubling the record set in October and Daily Covid-19 deaths in the U.S. rise back toward the record level set in April
- WTSP Tampa Bay — Tampa Bay nears 1,000 hospitalizations as COVID-19 pandemic persists: As of Saturday afternoon, 995 people in the Bay area were hospitalized with COVID-19 as their primary diagnosis.
- ClickOrlando.com — Florida reports more than 6,000 new cases of coronavirus 2 days after Thanksgiving: State testing sties in Central Florida reopened this past Friday after being closed for holiday.
- FOX 13 Tampa Bay — 6,276 new Florida coronavirus cases reported Saturday; 79 new deaths
With a good number of people still having Thanksgiving-as-usual, we may see a spike just like the one Canada had (Canadian Thanksgiving is in October). You may want to take some extra precautions over the next couple of weeks.
This week’s events
Monday, November 30
- Tampa Bay Tech Career Advice Forum — Job Seeker Coffee Talk @ 9:00 AM to 10:00 AM
- Professional Business Networking with RGAnetwork.net — Tampa Bay Virtual Networking Lunch All Welcome JOIN us @ 11:30 AM to 1:00 PM
- Tampa Bay Investors & Traders — Stocktwits Meetups — Winning Wall Streets Money with Stocks, Futures, Options, Forex, Cryptocurrency @ 6:30 PM to 8:00 PM
- North Port Toastmasters Meets Online!! — 6:30 PM to 8:00 PM
- South Tampa Toastmasters — Get Outta UR Comfort Zone Every Monday night Zooming with Us FUN Toastmasters! @ 7:00 PM to 8:15 PM
- Thinkful Webinar | Intro to HTML & CSS: Build Your Own Website @ 9:00 PM to 11:00 PM
- cYbor Security Meetup — Hacker Movie Monday: “OUR BRAND IS CRISIS” @ 9:00 PM to 11:00 PM
Tuesday, December 1
- Tampa Bay Networking Breakfast @ 7:30 AM to 9:00 AM
- Venice Area Toastmasters Club #5486 @ 7:30 AM to 9:00 AM
- Tampa Bay Tech Career Advice Forum — Successful Interviewing @ 10:00 AM to 11:30 AM
- Clearwater Renaissance Speakers Toastmasters @ 12:00 PM to 1:00 PM
- Network After Work Tampa – Networking Events — Virtual Cocktails And Connections @ 6:00 PM to 8:00 PM
- WordPress St. Petersburg — Ask Us Anything: WordPress Support Crowdsourcing Meetup @ 6:30 PM to 8:30 PM
- Tampa Hackerspace — THS Member Meeting Online @ 7:00 PM to 8:00 PM
- Naples Toastmasters Club #2835 @ 7:00 PM to 8:30 PM
- Thinkful Webinar | Intro To Data Analytics: Excel Basics @ 9:00 PM to 11:00 PM
Wednesday, December 2
- North Tampa Networking Group — Business networking @ 9:00 AM to 10:00 AM
- Grow Financial Virtual Agile Tour — 11:00 AM to 12:00 PM
- Tampa Bay Professional Business Networking @ 11:30 AM to 1:00 PM
- Heart of Agile St. Pete – Tampa – Orlando — Weekly Coffee Corner @ 12:30 PM to 1:45 PM
- Tampa Bay Business Intelligence and Data Analytics — Monthly Meeting @ 6:00 PM to 8:00 PM
- Tampa Hackerspace — Girls Who Code Interest Session AND Enrollment @ 6:00 PM to 8:00 PM
- Scrum Master Guild- The 2020 Scrum Guide is Here @ 6:00 PM to 8:00 PM
- Women in Linux — Understanding Linux @ 7:00 PM to 9:00 PM
- Carrollwood Toastmasters Meetings now conducted Online using Zoom @ 7:00 PM to 8:30 PM
- Thinkful Webinar | Intro to Data Science: Python Fundamentals @ 8:00 PM to 10:00 PM
Thursday, December 3
- CRUSH Networking Clearwater, Palm Harbor, Dunedin, Oldsmar — Next Level Networking – Crush Seat Specific Morning Group @ 7:30 AM to 8:30 AM
- Business over Breakfast Tampa Bay @ 7:30 AM to 9:00 AM
- Sarasota Speakers Exchange Toastmasters @ 12:00 PM to 1:00 PM
- Telecom Park Toastmasters #6745 @ 12:00 PM to 1:00 PM
- Tampa Bay Tech Career Advice Forum — The Job Seekers Lunch Hour @ 12:00 PM to 1:30 PM
- Tampa Bay Speed Networking, Virtual Style~ You’ll Still get to meet in small groups! @ 5:00 PM to 7:00 PM
- Tampa Bay Azure User Group — Zero to Hero with Azure Functions By Thiago Custodio – Microsoft Azure MVP @ 6:00 PM to 7:30 PM
- Pinellas Tech Network — How to Rock Your Next Live Stream Event @ 6:00 PM to 7:00 PM
- Tampa Bay Coalition of Reason — A Series of Fortunate Events: Chance & the Making of the Planet, Life, & You @ 7:00 PM to 9:00 PM
- Tampa Hackerspace — Online Electronics Guild Meetup @ 7:00 PM to 9:00 PM
Friday, December 4
- Tampa Bay Tech Career Advice Forum — Career Success Magic: Insider Secrets @ 10:00 AM to 2:00 PM
- Tampa International Networking Lunch Virtual @ 11:30 AM to 1:00 PM
- IIBA Tampa Bay — Lunch & Learn Series #17: Earned Value EZ @ 12:00 PM to 1:00 PM
- USA Windows Virtual Desktop User Group — December Meetup @ 12:00 PM to 2:00 PM
Saturday, December 5
Sunday, December 6
Do you have any events or announcements that you’d like to see on this list?
Let me know at joey@joeydevilla.com!
Join the mailing list!
If you’d like to get this list in your email inbox every week, enter your email address below. You’ll only be emailed once a week, and the email will contain this list, plus links to any interesting news, upcoming events, and tech articles.
Join the Tampa Bay Tech Events list and always be informed of what’s coming up in Tampa Bay!
Native mobile development skills are rare. Being able to point to apps in the store and say “I made those” can really help you stand out. And you can learn how — at half price, if you hurry!
raywenderlich.com is the premier mobile development tutorial subscription site, and right now, everything in their store is 50% off!
They’ve got a number of subscription plans that are a great deal, especially when you factor in over 3000 videos, dozens of books, and access to the forums:
- Ultimate Beginner plan ($12.42/month on sale, normally 29.99/month):
- Access beginner courses
- Access beginner books
- iOS & Android learning paths
- Stream via iOS/Android app
- Hands-on challenges
- Download source code
- Forum membership
- Ultimate Professional plan ($24.92/month on sale, normally 49.99/month):
- Access every book in our library
- Access advanced ‘Pro’ courses
- Download & watch courses offline
- Access beginner courses
- iOS & Android learning paths
- Stream via iOS/Android app
- Hands-on challenges
- Download source code
- Forum membership
These deals last only until Monday, November 30th, so if you want to save some money and pick up or sharpen your mobile dev skills, you should act quickly!
I learned iOS development through RayWenderlich.com’s book, iOS Apprentice, back when Objective-C was the only option, and the iPhone 4S was the hot new device. I became a regular reader of the site, and over time, got to know the team and even joined, going on to write articles, a video, and even co-author a book! I can’t think of a better place or a better group of people to learn from.
I’m not the only one who thinks so…
If you want to get into mobile development, this deal’s for you. Sign up before Monday, November 30th, and get mobile coding!
As I wrote in the previous post, the Advent of Code is happening soon — it start on Tuesday, December 1st and runs all the way to December 25th. If you want to give your programming skills a good workout or test, you’ll want to try out Advent of Code’s challenges!
The previous post featured Python solutions to the day one challenges from the 2019 edition of Advent of Code. In this post, I’ll present solutions written in Swift.
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 get4
, then subtract 2 to get2
.- For a mass of
14
, dividing by 3 and rounding down still yields4
, so the fuel required is also2
.- For a mass of
1969
, the fuel required is654
.- For a mass of
100756
, the fuel required is33583
.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 used the Python REPL for my Python-based solution. For my Swift-based solution, I used the closest analogue: an Xcode Playground.
Swift, like Python uses the three quotes to denote multiline strings. I used them to define a multiline string constant, rawInput
, into which I pasted the data:
let rawInput = """ 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 """
With rawInput
defined, it’s time to convert it from a multiline string into an array of strings, with each line getting turned into its own array element. The String
class’ split
method does this quite easily, and the result was the splitInput
string array:
let splitInput = rawInput.split(separator: "\n")
The next step was to convert splitInput
’s numbers-in-string-form into actual numbers. This process would involve applying the same function — the Int
struct’s “init
from string” method — to all the elements in an array, which is exactly what the map
method is for:
let masses = splitInput.map {Int($0)!}
Swift’s map
method takes a closure containing a function as its argument and applies that function to every item in the given array, creating a new array as its result.
In this case, the function in question is:
Int($0)!
Parameters passed into the closure begin with the $
character, which is then followed by a number specifying which parameter it is. The first parameter is $0
, followed by the second parameter, $1
, followed by the third parameter, $2
, and so on.
Only one parameter is passed to the closure: $0
, which represents the current element of the splitInput
array. It’s fed into the init
method of Int
that takes a string and attempt to produce an integer. Since it’s possible that this method will be given a string that can’t be converted into an integer, the method’s return type is the optional type Int?
.
Since I’m quite certain that all the strings in the splitInput
array convert to integers, I used the ! operator to force unwrap the resulting Int?
values.
The end result is masses
, an array of integers. Each element in the array represents the mass of a component in the ship, and we need to calculate the fuel necessary to propel each component to the final destination.
This calculation involves applying a function to every element in masses
, and that function is:
- Divide the mass by 3, rounding down.
- Subtracting 2 from the result above.
Once again, I used map
:
let fuelRequirements = masses.map { mass in mass / 3 - 2 }
In the function above, mass
and 3
are both integers, so mass / 3
is an integer division, which automatically rounds down.
The result of this map
ping is fuelRequirements
, an array of integers containing the fuel requirements for each module.
The result is the sum of all the values in fuelRequirements
. Unfortunately, Swift doesn’t have a built in method for getting the sum of an array, so we’ll need to roll our own:
let totalFuel = fuelRequirements.reduce(0, +)
For my data, the result was 3454942. This turned out to be correct, so it was time to tackle part two.
Day one challenge, part two
Part two involved recalculating the fuel requirements when also taking into account the mass of the added fuel:
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
requires2
fuel. This fuel requires no further fuel (2 divided by 3 and rounded down is0
, which would call for a negative fuel), so the total fuel required is still just2
.- At first, a module of mass
1969
requires654
fuel. Then, this fuel requires216
more fuel (654 / 3 - 2
).216
then requires70
more fuel, which requires21
fuel, which requires5
fuel, which requires no further fuel. So, the total fuel required for a module of mass1969
is654 + 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.)
This called for a recursive function, the Swift code for which is below:
func fuelRequired(mass: Int) -> Int { let result = mass / 3 - 2 if result <= 0 { return 0 } else { return result + fuelRequired(mass: result) } }
I used this function to map
the values in the masses
array from part one onto a new array, updatedFuelRequirements
…
let updatedFuelRequirements = masses.map { fuelRequired(mass: $0) }
…and the sum of its the elements was the answer for part two:
let updatedTotalFuel = updatedFuelRequirements.reduce(0, +)
For my data, the answer was 5179544.
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 get4
, then subtract 2 to get2
.- For a mass of
14
, dividing by 3 and rounding down still yields4
, so the fuel required is also2
.- For a mass of
1969
, the fuel required is654
.- For a mass of
100756
, the fuel required is33583
.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
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
requires2
fuel. This fuel requires no further fuel (2 divided by 3 and rounded down is0
, which would call for a negative fuel), so the total fuel required is still just2
.- At first, a module of mass
1969
requires654
fuel. Then, this fuel requires216
more fuel (654 / 3 - 2
).216
then requires70
more fuel, which requires21
fuel, which requires5
fuel, which requires no further fuel. So, the total fuel required for a module of mass1969
is654 + 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.
Once again, here’s the weekly list of events for events for Tampa Bay techies, entrepreneurs, and nerds. Every week, on GlobalNerdy.com and on the mailing list, I scour the announcements for events that are interesting to or useful for those of you who are building the future here in “The Other Bay Area, on The Other West Coast”.
This list covers events from Monday, November 23 through Sunday, November 29, 2020.
I’ve opted to list only those events that I can confirm are happening online. I’m not yet listing in-person events, as we’re still in the middle of a pandemic in one of the hardest-hit states (875,000 cases, which is an increase of 31,000 since last week, and 17,488 deaths, which is up 1,572 from last week) in one of the hardest-hit countries in the world (11 million cases, which is an increase of 1 million from last week, and 245,000 deaths, which is up 7,000 from last week).
Events — especially virtual, online ones — can pop up at the last minute. I add them to the list as I find out about them. Come back and check this article from time to time, as you might find a new listing that wasn’t there before!
It’s the week of Thanksgiving, which is one of the least productive weeks of the year. You might want to double-check with event organizers that events that appear on the schedule are actually happening this week!
This week’s events
Monday, November 23
- Tampa Bay Tech Career Advice Forum — Job Seeker Coffee Talk @ 9:00 AM to 10:00 AM
- Professional Business Networking with RGAnetwork.net — Tampa Bay Virtual Networking Lunch All Welcome JOIN us @ 11:30 AM to 1:00 PM
- Young Professionals of Tampa Bay Networking Group — South Tampa Referrals @ 11:30 AM to 1:00 PM
- North Port Toastmasters Meets Online!! @ 6:30 PM to 8:00 PM
- Get Outta UR Comfort Zone Every Monday night Zooming with Us FUN Toastmasters! @ 7:00 PM to 8:15 PM
Tuesday, November 24
- Tampa Bay Agile: v-Lean Coffee @ 7:30 AM to 8:30 AM
- Professional Business Networking with RGAnetwork.net — Power Partner Breakfast Virtually! @ 7:30 AM to 9:00 AM
- Pinellas/Pasco/Hillsborough/Polk/Manatee Business Meetups — Business over Breakfast Largo/Seminole Virtually @ 7:30 AM to 9:00 AM
- Professional Business Networking with RGAnetwork.net — Downtown St Pete Networking Virtually JOIN us @ 7:30 AM to 9:00 AM
- Venice Area Toastmasters Club #5486 @ 7:30 AM to 9:00 AM
- Clearwater Renaissance Speakers Toastmasters @ 12:00 PM to 1:00 PM
- Tampa Hackerspace — Online Weekly Open Make Night @ 6:00 PM to 10:00 PM
- Tampa Bay Coalition of Reason — CFI Insider: Secular Guide to Surviving the Holidays @ 7:00 PM to 8:30 PM
- Shut Up & Write Tampa — Online Event: Shut Up & Write on Zoom @ 7:45 PM to 9:15 PM
- Thinkful Webinar | Intro To Data Analytics: Tableau Basics @ 9:00 PM to 11:00 PM
Wednesday, November 25
- Heart of Agile Weekly Coffee Corner @ 12:30 PM to 1:45 PM
- Let’s Talk Business- Tampa Bay Virtual Meetings @ 2:20 PM to 3:50 PM
- ACES: Architects, Contractors, Engineers, & Site Developers — State of the Market @ 4:00 PM to 5:00 PM
- Tampa Bay Inventors Council — A Special TBIC Thanks-for-being-a-member treat this Wednesday night! @ 6:15 PM to 7:45 PM
- Carrollwood Toastmasters Meetings now conducted Online using Zoom @ 7:00 PM to 8:30 PM
Thursday, November 26
It’s Thanksgiving day! You might want to double-check with the event organizers that these events — which were listed on Meetup and EventBrite — are actually happening on this day.
- Professional Business Networking with RGAnetwork.net — Downtown St Pete Networking Virtually JOIN us @ 7:30 AM to 9:00 AM
- Tampa Bay Tech Career Advice Forum — The Job Seekers Lunch Hour @ 12:00 PM to 1:30 PM
- Sarasota Speakers Exchange Toastmasters @ 12:00 PM to 1:00 PM
- Young Professionals of Tampa Bay Networking Group — Speed Networking, Virtual Style~ You’ll Still get to meet in small groups! @ 5:00 PM to 7:00 PM
- St Pete Beach Toastmasters Zoom Group – Improve Your Public Speaking! @ 6:30 PM to 8:00 PM
Friday, November 27
It’s Black Friday! You might want to double-check with the event organizers that these events — which were listed on Meetup and EventBrite — are actually happening on this day.
- Wesley Chapel Young Entrepreneurs Meetup (40 and Under) — Business Networking Lunch Every FridayVirtually @ 11:30 AM to 1:00 PM
- Young Professionals of Tampa Bay Networking Group — Friday Business Introductions JOIN us Virtually All Welcome @ 11:30 AM to 1:00 PM
- South Fl Public Relations and Marketing — Friday Business Introductions @ 11:30 AM to 1:00 PM
- Professional Business Networking with RGAnetwork.net — RGA Top Professional Business Networking Tampa Bay & surrounding areas~ @ 11:30 AM to 1:00 PM
- Wesley Chapel, Trinity, New Tampa Business Professionals — Odessa Networking Lunch Virtually @ 11:30 AM to 1:00 PM
Saturday, November 28
There aren’t any scheduled tech, entrepreneur, or nerd online events…YET!
Sunday, November 29
Do you have any events or announcements that you’d like to see on this list?
Let me know at joey@joeydevilla.com!
Join the mailing list!
If you’d like to get this list in your email inbox every week, enter your email address below. You’ll only be emailed once a week, and the email will contain this list, plus links to any interesting news, upcoming events, and tech articles.
Join the Tampa Bay Tech Events list and always be informed of what’s coming up in Tampa Bay!