Review: The Poppy War

The Poppy War by R.F. Kuang
My rating: 4 of 5 stars

I recieved this book as part of the Hugo 2021 nominations packet

This book was GREAT! I can definitely see why its series was up for a Hugo nom this year. I wasn’t able to get past the first book before it was time for Hugo voting, but I looked forward to reading this book every day. There was never a time where I felt I had to push through the book. It even had such a great start:

“Take your clothes off.”

Actually, the first paragraph:

Rin blinked. “What?”
The proctor glanced up from his booklet. “Cheating prevention
protocol.”

WTF kind of test is taking cheating so seriously that the students have to strip to make sure they aren’t cheating?”

And from there we were off to the races. I was reading this mostly at the same time as The Ruin of Kings. I also enjoyed that book, but I felt this one was more exciting from beginning to end – probably because (other than the first chapter) it’s being told chronologically so nothing has to be held back for suspense or surprise. There was also the difference factor of this book’s setting. While most fantasy is either Europe or Europe by another name, here we have a world that straddles primary and secondary world fantasy. Clearly the Nikara are China and the Mugan are Japan. I was torn on whether Hesperia was meant to be the Roman Empire or a lumping of all of Europe. The Hinterlands are clearly the Mongols. Spearly – I’m not sure if it’s meant to be Okinawa or Taiwan. But we have a correlation to Sun Tzu and even a journey to the west type Buddhism story. That was all fun and having the food and cultural references be a little different was also informative when it’s so often taken from Greek or Norse mythology. (Or Tolkienesque) I think many fantasy readers of Asian descent will be happy to see themselves in the story, finally! If my kids (who are hapas) continue to be into fantasy, I’ll definitely be sharing it with them.

As to the story itself, in another interesting contrast to The Ruin of Kings, the fantasy elements take a LONG time to arrive. This is neither good nor bad, it simply is. It made me wonder for a long time how and when RF Kuang was going to incorporate fantasy into it. In the meanwhile it read like a Confucian/wuxia tale and that was fun in and of itself. I think Kuang takes the well-worn Harry Potter/Cinderella trope of being raised by uncaring relatives and rising WAY above that to some good places. Because it’s meant for an older audience it’s not simply a “prove myself and I won’t be a misfit anymore” story. It’s also a wartime story and I think a lot of the aspects of the aftermath of war hit me a little harder in 2021 after years of things like the Syrian Refugee crisis and understanding how quickly things get turned around. I think Kuang does an excellent job of providing consequences to the actions. This isn’t one of those fantasy books where murder just happens willy-nilly. It leaves people screwed up.

I’m definitely adding the next book to my to-read list and can’t wait to jump on it. (Although since Hugo voting is over, it’s going to the back of the list) If you want your fantasy to have a different perspective or if you want to see yourself as the hero (because of gender identity or racial identity) read it. If you want a good story, no matter your background – read it!

Definitely a book for adults or for a very mature teen – profanity, drug use, rape, war, death, violence.

View all my reviews

Review: Men at Arms

Men at Arms by Terry Pratchett
My rating: 4 of 5 stars

This is my second time reading this book. I left the rating the same

When it comes to the Anhk-Morpork based books, Pratchett lays in the final brick in the foundation for all that will come. The Night Watch is elevated to full Watch. We get more interactions and elaborations on how the Guilds work and how Vetinari pulls all the strings. Carrot grows a little, even as he retains his essential “Carrot-ness”. Pratchett builds on adding in all the fantasy and horror characters that he started back with Reaper Man and introduces Angua, our werewolf watch-person. We also have the return of Gaspode and Detritus from Moving Pictures. Reading all of these in a row and thinking about them critically for these reviews has made me realize that Pratchett has moved towards really making Anhk-Morpork (and greater Discworld) seem like more real, lived-in places as he settled into it as a long series. It feels as alive as Gotham, especially as written by Scott Snyder.

This particular storyline is in the form of a detective novel (which I believe most of the City Watch novels become from here on out), but it’s almost incidental to the story. I think that’s the weakest thing about it and, despite having given it 4 stars before, I didn’t like the main plot too much last time. Pratchett is more interested in exploring ideas of diversity in the workplace (another trope that will continue in each of the City Watch novels going forward), the right of kings, and, to some extent, the feeling of power that comes from using a gun. That last one comes through a bit anviliciously and I don’t know if it’s a slight weakness in Pratchett’s writing on this book or if it comes from not being American. (That is to say, I’m lead to believe guns are much, much rarer in England)

It’s not a horrible place to jump in although you’ll be missing some of the details about the way others react to Carrot and the fact that he’s a human adopted by dwarves. Pratchett does seed the Koom Valley battle between Trolls and Dwarves that he pays off way later in Discworld 34, Thud!

Overall, by the time you’ve either arrived at Discworld #15 if reading in order or City Watch #2, I think you’ll have an idea of whether you like Discworld and should read this book. It works very well and I enjoyed it a lot, but it’s also not some must-read book even if you didn’t like other Discworld books you’d tried before.

View all my reviews

Review: Learn You a Haskell for Great Good!

Learn You a Haskell for Great Good! by Miran Lipova?a
My rating: 3 of 5 stars

Learn You a Haskell (or LYAH as it’s known on Stack Exchange and other parts of the internet) is the most often recommended resource for learning Haskell. I think it has a lot of things going for it.

1. It’s available on the net for free if you don’t want to (or can’t) buy it
2. It’s got a conversational tone that reminds me of what I love most about the Perl O’Reilly books
3. The author does a good job comparing and contrasting with imperative programming languages (almost all the ones you’ve heard of, if you’re heard of any programming languages).

The bummer for me and the way that I learn is that the author shows lots of small examples and almost no full program examples. There’s an O’Reilly book (also available for free online) that’s a little more traditional in showing some more full programs. However, the reviews on here for that book seem to indicate that it’s due for a revision because it’s a bit out of date.

Should you use this book to learn Haskell? I think it depends a lot on the way you learn. This book and a little help on reddit were enough to get me started on solving Advent of Code problems in Haskell. I don’t need it for work or school, so I can’t say how well it would prepare you for that. Starting with functors and going through Monads, it went over my head on this first read. I’m pretty sure I will need to read it again at some point after writing a little more Haskell.

View all my reviews

Review: The Ruin of Kings

The Ruin of Kings (A Chorus of Dragons #1)The Ruin of Kings by Jenn Lyons
My rating: 4 of 5 stars

I received this book for free as part of the 2021 Hugo Voting Packet

This was a great, fun book. The only point I really had against it is that eventually with all the fantasy names and locations and history, things eventually got a little convoluted to where I couldn’t remember who was related to whom and it really starts to matter in the last quarter of the book. The book has an interesting framing device – one of the book’s characters, Thurvishar, has written a report to the Emperor to document what happened. The introduction even includes a bit of lampshade hanging about the fact that he’s going to have to tell the real-world reader some things that the in-world reader (the Emperor) would already know.

The report is supposed to be in three parts, but that sets your expectations slightly wrong because the first part of the book is the first 3/4 (actually more because there’s also an appendix at the end). Part 1 reminds me a bit of the way that Christopher Nolan’s Mememto plays out. We have two narratives going on about our main character, Khirin. One narrative starts a few years in the past and makes its way to the time point of the first chapter. The other narrative starts from that first chapter and takes us to just shy of the “present”. That was fun to read because you’re able to look for foreshadowing and other little story-telling easter eggs because you know where the narrative is headed.

At its most basic level this is a Chosen One narrative, but the author plays with lots of subtropes, often subverting what you might expect while on a Chosen One journey. To go deeper than that would risk giving too much of the plot away, but at least it’s more creative than a basic Chosen One narrative. Lyons has populated the world with quite a few interesting magic systems, gods, and goddesses. Some of the background made me think of Sanderson’s Cosmere which makes me wonder if the author was inspired by Sanderson or if she’s just playing with very similar tropes.

Overall, I’d recommend it to anyone looking for some fun fantasy that plays with many different parts of the form. Lyons also is a speed demon since this book came out in 2019 and she’s already at book #4. So you’re not going to get left behind as you might with other fantasy books unless she gets hit by a bus or something.

I was able to read everything in here without issue, but if you need some TWs: there is child abuse, non-consensual sex, profanity. I’d recommend (depending on your values/the kid’s maturity level) for at least high school/secondary school or older.

View all my reviews

Review: Hackers: Heroes of the Computer Revolution

Hackers: Heroes of the Computer RevolutionHackers: Heroes of the Computer Revolution by Steven Levy
My rating: 4 of 5 stars

This book is the story of the beginning of computers, written in the 1980s. I’d already read about many of the events portrayed in the book via other books or magazine articles. But this was nice and detailed. I like Steven Levy’s style. He really brings the people profiled to life. Knowing where computers have ended up – which companies and movements have won – makes it an especially interesting read compared to when it was first published and people weren’t sure where the industry was going or if it would crash like the Atari crash of the 1980s.

It was weird that I was alive for a good portion of the last part of the book, but hadn’t experienced it firsthand both from being too young and from neither myself nor my parents having geeky peers.

The book has 2 afterwards to update the reader on what’s happened since then. The second one is from 2010. It’s funny, even 11 years later it the Zuckerberg section read SO differently. Frankly, I’d really love if Levy would just make a sequel to the original book and really cover the 90s to today. Go deep on Google, Red Hat, Linus Torvalds, Facebook, and Twitter. Show the 3rd (or 4th?) wave hacker and how they are similar and different from those previous generations.

Good as a history book that reads like a long-form magazine article. Also easy to read in chunks as you have interest. Highly recommend.

View all my reviews

2021 in Music (Last.FM and Spotify Listening Trends)

This year I was able to attend the Paul and Storm / JoCo concert that COVID stole from me last year. While there I bought the entire Paul and Storm discography, but I think because I listened to it so much on Spotify in the past, I didn’t listen to it as much as I thought I would. 

This was not one of those years where I came out ahead by not paying for Spotify. I bought a lot of albums, including starting on my quest to get the entire MxPx back catalog from the time I stopped listening in high school until now. (This is reflected in this year’s numbers)

New albums

  • Dj Cutman – Free to Stream
  • Five Iron Frenzy – Until this Shakes Apart (Kickstarter)
  • Icon For Hire – Amorphous (Kickstarter)
  • Hamilton Soundtrack
  • I Fight Dragons – Side Quest: B-Sides and Rarities (Patreon)
  • Nikki Lynette – Roses N’ Guns, Roses N’ Guns 2
  • DJ Format – Stealin’ James Brown, Holy Shit
  • MxPx – Let it Ride, Before Everything & After, MxPx, Panic, Secret Weapon, On the Cover II, Punk Rawk Christmas
  • Entire discography of My Sister’s Fugazi Sweater
  • Entire discography of Ponies at Dawn
  • Entire Discography of Paul and Storm
  • Spindash 3
  • Dicey Dungeons Soundtrack
  • Packy Lundholm – Track Sabbath Vol 2
  • Slay the Spire Soundtrack
  • Janelle Monae – The Archandroid (gift)
  • Slick Shoes – frequency and rotation (gift)
  • Cimafunk – El Alimento (gift)
  • Dizzy Gillespie – 12 albums (gift)
  • Lana Del Rey – Chemtrails Over the Country Club

Of all the albums I bought this year, I think my favorite was Track Sabbath Volume 2 by Packy Lundholm. This isn’t really reflected in the numbers as I got it near the end of the year. That said, I really enjoyed the Hamilton Soundtrack, especially now that I could listen to it without Spotify ads. I also had a lot of fun listening to Nikki Lynette’s albums and Amorphous by Icon for Hire.

I wasn’t particularly attached to Ponies at Dawn (which turns out – in what perhaps shouldn’t be surprising – to be a collective of My Little Pony fan bands), one of the albums was recommended to me by Bandcamp and the entire discography was just a couple bucks. Same situation as with My Sister’s Fugazi Sweater. Of course, now they’re each pretty likely to come up on a purely random listen of my library since they constitute such a large chunk of it.

A few other things to note before we look at the stats:

  • The kids wanted to listen to the Audio Adrenaline cover of The Hairbrush Song and The PDX Broadsides’ Tiny Little Octopus every night during tooth brushing for a few months.
  • My use of the “recently favorited” feature on Ffunkwhale has definitely led to a lot of repeated listens of songs that I’ve marked as favorite. A lot of them are pretty energizing and great to put on in the morning at work. Eg: RVA All Day by the No BS Brass Band, I Am Jack’s Smirking Revenge by Five Iron Frenzy, and Freestyle by Bassnectar
  • I also created more artist-centric “radios” on Funkwhale, once again leading to artist concentration over a purely random mix.
  • I continued to use Spotify to preview artists and albums I would then put on my to-buy list
  • Throughout December I listened to lots of Christmas music on repeat. In fact I almost exclusively listened to Christmas music.

Onto the 2021 last.fm music stats:

Top Artists for 2021

1. MxPx (2139) – As I said above, I got pretty obsessed with MxPx and then with collecting the entire back catalog. I also spent a lot of time listening to a Funkwhale radio station with MxPx, Anberlin, and Five Iron Frenzy. They earned the top spot this year!

2. Bee Gees (873) – The Bee Gees jumped up quite a bit compared to last year. I listened to them 4 times as much as I did last year. I don’t remember listening to them so much this year, but I’m not surprised given what I’ve said about them in the past – they remind me of my childhood with my mom putting on the records as well as the fact that they wrote some absolute bangers. 

3. Five Iron Frenzy (830) – This shouldn’t be a surprise since their kickstarter album finally came out this year. It took me a few listens, but it grew on me to the point where I think Wildcat is about the only song I don’t like on the album.

4. I Fight Dragons (804) – Another crowd-funded album that released this year. This time it was b-sides and rarities. I actually had a fair chunk of these already, but the ones they cleaned up and/or re-recorded sound really nice.

5. Relient K (517) – I have not yet caught up with their latest album. I think I’ve mentioned in the past that I fell off of enjoyment of the band when they got a bit more serious. I still enjoy the old stuff, though. I remember spending one weekend playing their older stuff and reminiscing with the wife and sharing with the kids.

6. Anberlin (417) – A perennial group on this list, during COVID they started doing virtual concerts and I think it awoke something in them because they recently had a new single that appeared on my Spotify New Releases playlist. I hope they’re working on a new album.

7. Icon for Hire (404) – I had a few singles from their time on Tooth and Nail, but somehow I found out about their Kickstarter in 2020. The album finally released in 2021 and I really enjoyed the music. Sure, it’s a little emo for me in the sense that emo is mostly a young person’s genre (I think we either grow out of the sadness or “graduate” into depression), but the group writes catchy songs with fun and/or piercing lyrics. 

8. The Beatles (371) – As usual, I had a couple times in 2021 where I decided to just listen to all their albums. 

9. Lana Del Rey (352) – Chemtrails finally came out. I have to say that, as of the beginning of 2022, it’s not my favorite LDR album. I keep wanting her to put together another trip hop album. Sometimes I go back and revisit albums or songs I didn’t like and form new opinions, so we’ll see. Mostly I listened to her discography a few times as well as a Female Singer radio I have on Funkwhale.

10. YUNG BAE (328) – I don’t remember listening to this much YUNG BAE, but it’s always fun for something I can slip on in the background and just enjoy.

11. The PDX Broadsides (302) – As I mentioned above, the kids got addicted to Tiny Little Octopus for a while. I used to play it 3 times a night for a few months.

12. Marketplace (271) – As I mentioned last year, the scrobbler on Android was changed so that it also scrobbles podcasts. I guess I listened to a lot of Marketplace this year.

13. Paul and Storm (269) – I went through the entire discography once after buying it. After that I revisited my favorites a few times.

14. Dj CUTMAN (239) – There were a few times at work where I REALLY needed to concentrate on my work without worrying about lyrics distracting me at all. There are lots of artists I can put on, but Dj CUTMAN usually is the first to come to mind when I need to do that.

15. Run the Jewels (238) – I have continued to enjoy the RTJ songs I have, but a LOT of these came from listening to my Favorites list on Funkwhale, where I had a few RTJ songs.

16. The Indicator from Planet Money (233) – Another podcast. They have a LOT of episodes, so I’m not surprised they appeared on here.

17. The Doubleclicks (213) – They had a free day where all their albums were available for free on Bandcamp so I went and grabbed the albums I didn’t already have. Most of these listens come from going over the discography.

18. Nikki Lynette (176) – I first got introduced to Nikki Lynette via the songs she did with I Fight Dragons. When I discovered her two mixtapes, I listened to them almost literally on repeat for a month or two. I’d probably listen more often if Danielle didn’t hate them.

19. Anamanaguchi (161) – I don’t remember listening to this much Anamanaguchi this year, but they’re another great band for music without lyrics. 

20. Elaine Li (153) – I have one album of video game music that she did, but I had no idea I listened to it this much in 2021.

Top Albums of 2021

As usual, the albums can mix things up a bit compared to the artists because I might listen to lots of music from an artist, but over many albums or I might listen to one artist by listening to the same album over and over – particularly if I just got the album. 

1. Bee Gees – The Ultimate Bee Gees (873) – No real surprise here as it’s almost my only source of Bee Gees music, so if they appeared as high as they did above, it would have to be from this album.

2. Icon for Hire – Amorphous (399) – As with the Bee Gees, this is almost the only album I have for Icon For Hire, so no surprise here. 

3. MxPx – MxPx (384) – The album that kicked off my MxPx buying spree, it’s a great album that showcases their growth (in age) while still showing how there’s room for punk rock when you’re older.

4. Five Iron Frenzy – Until This Shakes Apart (361) – no surprise since it just came out this year

5. I Fight Dragons – Canon Eyes (339) – I’m slightly surprised I listened to this much of this album specifically. I remember listening to a lot of IFD, but not this particular one.

6. Marketplace – Marketplace (271) – podcast

7. MxPx – Let It Happen (236) – This B-side compilation of MxPx music has a LOT of tracks so I don’t have to listen to it too often for it to appear high on the list.

8. The Indicator from Planet Money – NPR (232) – podcast

9. MxPx – Before Everything & After (197) – I liked this one a lot and also needed to listen for the review I wrote this year, so I’m not surprised.

10. MxPx – At the Show (171) – an oldie, but a goodie

11. MxPx – Punk Rawk Christmas (157) – I’m pretty surprised this one made the list as I bought it at the end of November to be able to listen to during the Christmas season. Despite that it was able to do better than a lot of other albums that I had all year. 

12. Elaine Li – Geshi Matsuri (153) – As I said in the previous section, I’m surprised I listened to this album this much.

13. MxPx – Secret Weapon (150) – another new album

14. Lin-Manuel Miranda – Hamilton: An American Musical (149) – I’m slightly surprised that I listened to this album this much in 2021, but it’s such a great album.

15. MxPx – The Ever Passing Moment (148) – one of my older MxPx albums – surprised that it’s on here.

16. Jeremih & Chance the Rapper – Merry Christmas Lil’ Mama Re-Wrapped (146) – on here because of my Christmas music spree.

17. MxPx – Slowly Going the Way of the Buffalo (139) – yet another old MxPx album

18. Danny Elfman – Tim Burton’s The Nightmare Before Christmas (137) – also on here because of my Christmas music

19. Run the Jewels – Run the Jewels 3 (130) – In the end, I prefer more songs from RTJ3 than RTJ4

20. I Fight Dragons – Side Quest: B-Sides And Rarities (129) – I think the fact that I already had a bunch of these songs kept it lower on the list this year.

Top Songs of 2021

Not too many surprises here given what we’ve seen so far. I’ll just comment on anything that’s odd or surprising.

1. I Fight Dragons – Artifact (266)

2. Elaine Li – Beginners (Ragnarok Online II) (152) – Well, here we go. This song must have gotten stuck on repeat or something without me noticing. There’s no way I listened to this song on purpose so much. It also explains the appearances above.

3. Ape School – Cocaine & Guns ASAP (87)

4. Idina Menzel, AURORA – Into the Unknown (65)

5. The PDX Broadsides – Tiny Little Octopus (58)

6. The Beach Boys – Do You Wanna Dance? (Mono) (1999 Digital Remaster) (54) – A surprise appearance since The Beach Boys don’t appear anywhere else on the list. Also, I usually listen to Pet Sounds over the older stuff I used to listen to on the oldies station when I was a kid.

7. Audio Adrenaline – the hairbrush song (live at the gardens) (48)

8. I Fight Dragons – Talking (35)

9. Bee Gees – How Deep Is Your Love (34)

10. Bee Gees – To Love Somebody (33)

11. Five Iron Frenzy – Lonesome For Her Heroes (33)

12. Five Iron Frenzy – So We Sing (33)

13. Icon for Hire – Curse Or Cure (33)

14. MxPx – Let’s Ride (33)

15. Bee Gees – How Can You Mend a Broken Heart (32)

16. NUTRISYSTEM – NS62367 It’sTime DDTV YT 169 30 YoYo (32) – an ad from Spotify

17. Bee Gees – For Whom The Bell Tolls (31)

18. Bee Gees – Guilty (live) (31)

19. Bee Gees – Heartbreaker (live) (31)

20. Icon for Hire – Brittle (Prelude) (31)

Top All-Time Artists

1. Five Iron Frenzy (4807) (no change) – Almost a thousand more listens keeps Five Iron Frenzy securely in the lead. 

2. Anberlin (3797) (up from #3) – As I predicted last year, it has jumped up past Fantastic Plastic Machine. 

3. Fantastic Plastic Machine (3777) (drop from #2) – And in danger of being overtaken by The Beatles this year or next year.

4. The Beatles (3467) (no change)

5. Relient K (3045) (no change) – But funnily enough another year of about 500 listens. Very consistent with them, I am.

6. MxPx (2986) (not previously on the list) – jumping from out of nowhere is MxPx! They weren’t even on my list of potential additions to the list. 

7. I Fight Dragons (2955) (drop from #6) – while it lost a spot due to MxPx’s surge, it’s very, very close to overtaking Relient K. I predict that both MxPx and IFD will overtake them next year. We’ll have to see what my listening trends do in 2021.

8. “Weird Al” Yankovic (1920) (drop from #7) – It’s a long way to #7. I think “Weird Al” may continue to fall unless a new album is released or my kids get into him.

9. The PDX Broadsides (1733) (up from #10) – they continue to rise and would have been even higher had it not been for MxPx. Tough to say if they’ll pass “Weird Al” this year, but if they finally release the naughty album from their last kickstarter, that could be the boost they need. 

10. Fall Out Boy (1658) (drop from #8) – I really didn’t have them front of mind in 2021. If I add them to my rock out in the morning list, they may make a comeback.

11. Jonathan Coulton (1615) (drop from #9) – I think without a new album he may continue to fall.

12. Lana Del Rey (1393) (up from #14) – new to the list last year and up 2 spots in a year where everyone was dropping in the face of MxPx. LDR’s got some potential staying power. It’s going to depend on her next album and whether I find it any good.

13. Gnarls Barkley (1214) (drop from #11) – they’re stubbornly holding on for a group I rarely listen to. 

14. Chance the Rapper (1203) (drop from #12) – if I end up with the next album(s) this year, he may rise or remain on the list. We’ll see. 

15. Bee Gees (1174) (new to list) – They were not on my predicted list for this year either. So perhaps my predictions don’t mean much? 

Off the list this year:

  • Tom Lehrer (from #13 to #16) – slightly surprising after last year’s rally. But he’s RIGHT there. Just like “Weird Al” – if I decided to share the songs with my kids, he could maybe eke back onto #15
  • LostProphets (from #15 to #18)

Potentials for next year:

  • Anamanaguchi (currently #17) – Tricky because they’re only 100 scrobbles short of the Bee Gees, but I also don’t listen to them too much nowadays
  • YUNG BAE (current #19) – I almost feel more likely to select one of his songs over Anamanaguchi if I’m in the mood for something without lyrics
  • Childish Gambino (currently #21) – tough to know how realistic this is. I don’t listen to it as often as I did years ago, but I do have a couple songs I like to listen to on Spotify if I’m looking for something a little different
  • Dj CUTMAN (current #23) – is probably the most likely to jump into the top 15. I’m more likely to select his music on purpose compared to Gwen Stefani (#20) or DCTalk (#22)

All-Time Top Songs

After many years reflecting the scrobbles I made with a smaller collection of music, the all-time songs are slowly starting to better reflect the artists I listen to a lot. Of course, we’re talking about individual songs, so there will be some anomalies compared to the stats above. I’m surprised I don’t see any RTJ on here or some of the other songs I listened to a lot via the Favorites radio on Funkwhale.

1. I Fight Dragons – Artifact (309) (not previously on the list) – Jumps out of nowhere with double the scrobbles to take the top spot.

2. Elaine Li – Beginners (Ragnarok Online II) (156) – (not previously on list) – so this is where all the listens for the album came from!

3. Jonathan Coulton – The Princess Who Saved Herself (124) (dropped from #1) – first position change in a couple years

4. Clap Your Hands Say Yeah – Upon This Tidal Wave of Young Blood (123) – (dropped from #2) – Just like FPM, a relic from back when I first started using last.fm

5. Fantastic Plastic Machine – Steppin’ Out (110) (dropped from #3) 

6. The PDX Broadsides – Tiny Little Octopus (106) (not previously on the list) – completely expected as I noted at the beginning of the blog post. The kids’ obsession brought it to the list. We’ll see if they can rise a few more spots in 2022.

7. Gnarls Barkley – Who Cares (105) (dropped from #5)

8. Fantastic Plastic Machine – Take Me To The Disco [Malibu Mix] (104) (dropped from #4)

9. Gnarls Barkley – Just a Thought (103) (dropped from #6)

10. Anberlin – Audrey, Start The Revolution! (99) (dropped from #7) – A slight surprise. I listened to a lot of Anberlin. On the other hand, I have a LOT of Anberlin songs so the chance of any one particular song playing is slim. 

11. Five Iron Frenzy – Handbook For The Sellout (96) (up from #12) – First song that previously appeared that rose in position.

12. Five Iron Frenzy – Blue Comb ’78 (94) (dropped from #11)

13. Ape School – Cocaine & Guns ASAP (93) (new to list)

14. 4minute – Cut it Out (92) (dropped from #8)

15. Anberlin – The Undeveloped Story (92) (new to list)

A few stats from my last.fm year in review email:

  • An average of 73 daily scrobbles vs 59 in 2020
  • 139 days, 20 hours of listening time vs 81 days, 20 hours in 2020
  • 365 day streak vs 131 streak in 2020
  • 28% artists new to my scrobbles vs 12% in 2020
  • 48% new albums compared to 18% in 2020 (no surprise if you look back to the top of my post)
  • 44% new tracks vs 22% in 2020
  • Top tags
    • Rock
    • Pop
    • Pop punk
    • Electronic
    • Punk Rock
  • Most active hour was 1100 and I was most active during the work day (a reversal from early in my scrobbling times when we couldn’t scrobble from work)
  • Most active day of the week is Thursday
  • Most active day in the whole year was 8 July: 854 scrobbles
  • Total Scrobbles EOY: 184,819
  • 26,557 for year

Spotify

As I mentioned above, Spotify was mostly for listening to songs or albums I was thinking about buying. I did also occasionally use it to listen to something a little different. 

I didn’t get an email from Spotify this yaer. Maybe they only do that for paying customers? But I was able to search and find my Top Songs 2021 Playlist. Below the embedded playlist you’ll find a text version for accessibility and as a preservation step in case Spotify deletes my 2021 playlist and the embed stops working.

  1. Chief Takinawa & GameChops – Just Peachy – Over time I’ve grown a little tired of straightforward video game covers, but this one makes it into its own bit of art and is a joy to listen to. Loved throwing this on whenever I’d start up Spotify
  2. Slick Shoes – Whispers – This, and all the Slick Shoes songs on here were me seeing if I wanted to buy the latest album.
  3. Slick Shoes – Waiting
  4. Slick Shoes – Carry This
  5. MxPx & Rivals – Say Yes – A fun single that MxPx promoted in their newsletter. Will it end up on a future album? I’m not sure! I’m also not sure how much that is a thing bands are thinking about in the streaming era
  6. Childish Gambino – Sober 
  7. Rivals – Heathens – I started checking out Rivals after enjoying their collaboration with MxPx
  8. Slick Shoes – The Worlds Were Mine
  9. MxPx – Rolling Strong
  10. Ra Ra Riot – Too Dramatic
  11. Childish Gambino – 3005
  12. Jokabi & GameChops – Dire Dire Docks
  13. MxPx – Let’s Ride
  14. Chief Takinawa, Sage, & The 64th Wonder – Bounty Hunter – Beboppin’ On Heaven’s Door
  15. Slick Shoes – Keep It Secret, Keep It Safe
  16. Jokabi & Gamechops – Lost Woods
  17. MxPx – Uptown Streets
  18. Renee Elise Goldsberry & Original Broadway Cast of Hamilton – Satisfied
  19. Ra Ra Riot – Foolish
  20. Curly, GameChops, & GlitchxCity – Bubblegum K.K.
  21. MxPx – Friday Tonight
  22. Rivals – Moonlit
  23. Jonathan Groff, Original Broadway Cast of Hamilton – You’ll Be Back – I think this continues to be one of my favorite songs from the soundtrack
  24. Jakobi & Gamechops – Song of Storms
  25. MxPx – All of It
  26. Slick Shoes – Always Have (Enough is More)
  27. Renee Elise Goldsberry, Phillipa Soo, Jasmine Cephas-Jones, Leslie Odom Jr., & The Original Broadway Cast of Hamilton – The Schuyler Sisters
  28. Jokabi & GameChops – Professor Sycamore’s Theme
  29. MxPx – The Way We Do
  30. Ninjoi. – Misty
  31. Slick Shoes – Candy
  32. Jokabi & Gamechops – Ordon Village
  33. MxPx – Life Goals
  34. Chief Takinawa – Nogarap II
  35. Slick Shoes – Angel
  36. Jokabi & GameChops – Newbark Town
  37. MxPx – 20-20 Hindsight
  38. Jonathan Groff – I know Him
  39. Prince – I Wanna Be Your Lover
  40. Jokabi, GameChops, & Mikel – Smash Bros.
  41. Rx Bandits – In Her Drawer
  42. No BS! Brass Band – 3am Bounce
  43. Phillipa Soo & Original Broadway Cast of Hamilton – Helpless
  44. Jokabi & GameChops – Refugee Camp
  45. Rx Bandits – Ruby Cumulous
  46. Prince and The Revolution – Raspberry Beret
  47. Daveed Diggs, Leslie Odom Jr, Okieriete Onaodowan, & Original Broadway Cast of Hamilton – What’d I Miss
  48. Jokabi & GameChops – Kaepora Gaebora
  49. Rx Bandits – Only For the Night
  50. Ra Ra Riot – The Orchard
  51. Christopher Jackson, Lin-Manuel Miranda, & Original Broadway Cast of Hamilton – One Last TIme
  52. Jokabi & Gamechops – Agniratha
  53. Rivals – Lavenders
  54. Ra Ra Riot – Boy
  55. Prince – Little Red Corvette
  56. Jokabi & Gamechops – Kakariko Is Saved
  57. Leslie Odom Jr, Daveed Diggs, Okieriete Onaodowan, & Original Broadway Cast of Hamilton – Washington On Your Side
  58. Chief Takinawa – Astral Delusions
  59. MxPx – Pipe Dreams
  60. Coffee Date & Game Chops – Aria of the Soul
  61. Rx Bandits – Stargazer
  62. Chief Takinawa – Baeo
  63. MxPx – Disaster
  64. Coffee Date & GameChops – National Park
  65. Rx Bandits – Wide Open
  66. Slick Shoes – Held by Hope
  67. BKNAPP – Lake
  68. Jokabi & GameChops – Surf
  69. Chief Takinawa – REPAIRS [Bump] – Instrumental
  70. Slick Shoes – For Better For Worse
  71. Prince – Nothing Compares 2 U
  72. The Icarus Kid, 88bit, & GameChops – Gusty Garden Galaxy
  73. MxPx – Friday Tonight
  74. Face to Face – Farewell Song
  75. Nathaniel Rateliff & The Night Seats – What If I
  76. Mikel & GameChops – Zelda’s Meledy
  77. Strawberry Girls, Ben Rosett, & Zachary Garren – COMMANDER
  78. Sincere Engineer – Coming In Last
  79. Louser & Reel Big Fish – No Hope
  80. Curly & GameChop – 7 PM
  81. Crowcover – Alleycat (From Persona 5)
  82. Chief Takinawa – Hannya the 2nd: Blossoms – Megamix
  83. Leslie Odom Jr, Lin-Manuel Miranda, Rnee Elise Goldsberry, Phillipa Soo, Christopher Jackson, & Original Broadway Cast of Hamilton – Non-Stop
  84. Coffee Date & GameChops – Ocarina of Time
  85. Wizard of Loneliness – Cookin’ in Hateno Village
  86. Ra Ra Riot – Dying is Fine (Live at Pianos)
  87. Childish Gambino – All the Shine
  88. Coffee Date & GameChops – Lost Girl
  89. No BS! Brass Band – Hoodie
  90. Ra Ra Riot – Oh, La
  91. Marcus D & Emancipator – Kindred Spirit
  92. Coffee Date & GameChops – Yoshi’s Obstacle Course
  93. Childish Gambino – Redbone
  94. Prince – Welcome 2 America
  95. Mana – Ironia
  96. Juanes – Y Nos Dieron Las Diez
  97. Chief Takinawa – ShinobiWAV
  98. Doni – Try Hard (Donkey Kong Country 2 – Stickerbrush Symphony)
  99. Ra Ra Riot – Flowers
  100. Juanes – Todo Hombre Es Una Historia

Programming Update: November/December 2021

In these last two months of the year I only worked on Advent of Code

November

In November I worked through part of the 2016 problem set. I didn’t get too far because of how many languages I was doing at this point. Eventually I decided to allow myself to get a bit further in Python and then catch up with the other languages. Whenever I’d get stuck I’d go back to the other languages. Overall, once I’d figured out Python – Ruby, Perl, and Golang would be pretty easy. Haskell would still be hard, but I started getting the hang of it near the end of the month. 

For Python I did Days 4 and 5. The hardest part for day 4 (across any language) was the fact that we had to do two levels of sorting. 

import string
from collections import Counter
import re


def input_per_line(file: str):
    """This is for when each line is an input to the puzzle. The newline character is stripped."""
    with open(file, 'r') as input_file:
        return [line.rstrip() for line in input_file.readlines()]


def create_checksum_check(the_counter):
    """Takes in a counter and sorts by amount and then alphabetical in case of a tie."""
    sorted_counter = sorted(the_counter.most_common(), key=lambda x: (-x[1], x[0]))
    return [letter_pair[0] for letter_pair in sorted_counter]


def decipher_and_discover(encrypted_room: list, sector_id: str) -> bool:
    """Return true if this is the room we want."""
    shift = int(sector_id) % 26
    alphabet = string.ascii_lowercase
    decrypted_room = ""
    for character_string in encrypted_room:
        for character in character_string:
            # where are we in the alphabet?
            character_index = alphabet.index(character)
            new_character = character_index + shift
            if new_character > 25:
                new_character = new_character - 26
            decrypted_room += alphabet[new_character]
        decrypted_room += " "
    return decrypted_room.__contains__("object")


def is_real_room(room_data: str) -> tuple[int, int]:
    """Determine if a room is real and return sector ID. Else return 0."""
    encryption_counter = Counter()
    sector_and_checksum = re.compile(r'(\d+)\[(\w+)]')
    sector_and_checksum_results = re.findall(sector_and_checksum, room_data)
    encrypted = re.compile(r'(\w+)-')
    encrypted_results = re.findall(encrypted, room_data)
    # fill out counter to figure out if the checksum is right
    for encrypted_result in encrypted_results:
        for letter in encrypted_result:
            encryption_counter[letter] += 1
    # create checksum list
    checksum = [letter for letter in sector_and_checksum_results[0][1]]
    encrypted_letters_in_order = create_checksum_check(encryption_counter)
    # time to check if the checksum is right
    is_it_valid = [
        checksum[index] == encrypted_letters_in_order[index]
        for index in range(5)
    ]
    if all(is_it_valid):
        if decipher_and_discover(encrypted_results, sector_and_checksum_results[0][0]):
            return int(sector_and_checksum_results[0][0]), int(sector_and_checksum_results[0][0])
        else:
            return int(sector_and_checksum_results[0][0]), 0
    else:
        return 0, 0


if __name__ == "__main__":
    part_1sector_ids = []
    part_two_sector_id = []
    rooms_to_check = input_per_line("../input.txt")
    for room in rooms_to_check:
        part_1, part_2 = is_real_room(room)
        part_1sector_ids.append(part_1)
        part_two_sector_id.append(part_2)
    sector_id_sum = sum(part_1sector_ids)
    part_two_answer = sum(part_two_sector_id)
    print(f"The sum of sector ids for real rooms is {sector_id_sum}")
    print(f"The sector ID of the decrypted room is {part_two_answer}")
require "../../input_parsing/parse_input"

def count_and_sort_letters(room_string)
  room_string.scan(/[a-z]/).tally.to_a.sort_by!{|character| [-character[1], character[0]]}[0..4]
end

def valid_sector_id(character_count, checksum)
  character_count.map.with_index { |character, index| character[0] == checksum[index]}.all?
end

def letter_mover(letter, number)
  (0...number).each do
    if letter == "z"
      letter = "a"
    else
      letter = letter.next
    end
  end
  letter
end

def decryptor(encrypted_room, sector_id)
  shift_amount = sector_id.to_i % 26
  decrypted = encrypted_room.chars.map{|letter| letter_mover(letter, shift_amount)}.join
  puts decrypted
  if decrypted.include? "object"
    true
  else
    false
  end
end

encrypted_rooms = input_per_line("../input.txt")
sector_id_sum = 0
part_two_sector_id = 0
encrypted_rooms.each do |room|
  encrypted_part = room.scan(/(\w+-)/).join
  character_counts = count_and_sort_letters(encrypted_part)
  sector_and_checksum = room.scan(/(\d+)\[(\w+)\]/)
  if valid_sector_id(character_counts, sector_and_checksum[0][1])
    sector_id_sum += sector_and_checksum[0][0].to_i
    if decryptor(encrypted_part, sector_and_checksum[0][0])
      part_two_sector_id = sector_and_checksum[0][0]
    end
  end
end

puts "The sum of the sector IDs of the real rooms: #{sector_id_sum}"
puts "The decrypted room has a sector ID of #{part_two_sector_id}."
//Solution to 2016 Day 04 -- Security Through Obscurity
package main

import (
	"adventofcode/2016/aocinputs"
	"fmt"
	"regexp"
	"sort"
	"strconv"
	"strings"
)

type characterFrequency struct {
	character string
	frequency int
}

func decryptCharacter(character string, shift int) string {
	for i := -0; i < shift; i++ {
		if character == "z" {
			character = "a"
		} else if character == "-" {
			character = " "
		} else if character == " " {
			character = " "
		} else {
			runeValue := []rune(character)[0]
			character = string(runeValue + 1)
		}
	}
	return character
}

func findNorthPoleRoom(encryptedRoom string, sectorID int) bool {
	cipherShift := sectorID % 26
	var decryptedRoomName string
	encryptedRoomChars := strings.Split(encryptedRoom, "")
	for _, encryptedChar := range encryptedRoomChars {
		decryptedRoomName += decryptCharacter(encryptedChar, cipherShift)
	}
	//roomMustHave := regexp.MustCompile(`northpoleobjectstorage`)
	fmt.Printf("decrypted string: %s\n", decryptedRoomName)
	roomMustHave, _ := regexp.MatchString(`northpole object storage`, decryptedRoomName)
	return roomMustHave
}

func main() {
	roomList, err := aocinputs.MultipleLines("/home/ermesa/Programming Projects/adventofcode/2016/Day_04/input.txt")
	if err != nil {
		print(err)
	}
	sectorSum := 0
	var partTwoAnswer int
	for _, room := range roomList {
		sectorAndChecksumRegExp := regexp.MustCompile(`(\d+)\[(\w+)]`)
		encryptedRegExp := regexp.MustCompile(`(\w+)-`)
		sectorAndChecksum := sectorAndChecksumRegExp.FindStringSubmatch(room)
		sectorString := sectorAndChecksum[1]
		sector, _ := strconv.Atoi(sectorString)
		checksum := sectorAndChecksum[2]
		encryptedRoomBits := encryptedRegExp.FindAllString(room, -1)
		encryptedRoom := strings.Join(encryptedRoomBits, "")
		characterCounter := make(map[string]int)
		for _, character := range encryptedRoom {
			if string(character) != "-" {
				characterCounter[string(character)]++
			}
		}
		characterFrequencySlice := make([]characterFrequency, 29)
		for key, value := range characterCounter {
			characterFrequencySlice = append(characterFrequencySlice, characterFrequency{character: key, frequency: value})
		}
		// sorts the slice in place
		sort.SliceStable(characterFrequencySlice, func(i, j int) bool {
			if characterFrequencySlice[i].frequency != characterFrequencySlice[j].frequency {
				return characterFrequencySlice[i].frequency > characterFrequencySlice[j].frequency
			}
			return characterFrequencySlice[i].character < characterFrequencySlice[j].character
		})
		// final eval for part 1
		var checksumEval int
		checksumCharacters := strings.Split(checksum, "")
		for i := 0; i < 5; i++ {
			if characterFrequencySlice[i].character == checksumCharacters[i] {
				checksumEval++
			}
		}
		if checksumEval == 5 {
			sectorSum += sector
			if findNorthPoleRoom(encryptedRoom, sector) {
				print("true!!!!!\n")
				partTwoAnswer = sector
				print(partTwoAnswer)
			}
		}
	}
	fmt.Printf("The sum of all valid sectors is %d\n", sectorSum)
	fmt.Printf("The North Pole Objects are stored in sector %d", partTwoAnswer)
}

For Haskell I was able to do days 2 and 3.

import Data.List

--read from a file and put array where each line is an element. Point free.
readLines :: FilePath -> IO [String]
readLines = fmap lines . readFile 

-- given a number and a direction, give back the new number
findNextNumber :: (Eq p, Num p) => p -> Char -> p
findNextNumber number direction
    | (number == 1) && (direction == 'D') = 4
    | (number == 1) && (direction == 'R') = 2
    | (number == 2) && (direction == 'D') = 5
    | (number == 2) && (direction == 'L') = 1
    | (number == 2) && (direction == 'R') = 3
    | (number == 3) && (direction == 'D') = 6
    | (number == 3) && (direction == 'L') = 2
    | (number == 4) && (direction == 'U') = 1
    | (number == 4) && (direction == 'D') = 7
    | (number == 4) && (direction == 'R') = 5
    | (number == 5) && (direction == 'U') = 2
    | (number == 5) && (direction == 'D') = 8
    | (number == 5) && (direction == 'L') = 4
    | (number == 5) && (direction == 'R') = 6
    | (number == 6) && (direction == 'U') = 3
    | (number == 6) && (direction == 'D') = 9
    | (number == 6) && (direction == 'L') = 5
    | (number == 7) && (direction == 'U') = 4
    | (number == 7) && (direction == 'R') = 8
    | (number == 8) && (direction == 'U') = 5
    | (number == 8) && (direction == 'L') = 7
    | (number == 8) && (direction == 'R') = 9
    | (number == 9) && (direction == 'U') = 6
    | (number == 9) && (direction == 'L') = 8
    | otherwise = number


-- takes in a character for a keypad button and returns the next one based on direction
findNextKeypadPartTwo :: Char -> Char -> Char
findNextKeypadPartTwo keypad direction
    | (keypad == '1') && (direction == 'D') = '3'
    | (keypad == '2') && (direction == 'D') = '6'
    | (keypad == '2') && (direction == 'R') = '3'
    | (keypad == '3') && (direction == 'U') = '1'
    | (keypad == '3') && (direction == 'D') = '7'
    | (keypad == '3') && (direction == 'L') = '2'
    | (keypad == '3') && (direction == 'R') = '4'
    | (keypad == '4') && (direction == 'D') = '8'
    | (keypad == '4') && (direction == 'L') = '3'
    | (keypad == '5') && (direction == 'R') = '6'
    | (keypad == '6') && (direction == 'U') = '2'
    | (keypad == '6') && (direction == 'D') = 'A'
    | (keypad == '6') && (direction == 'L') = '5'
    | (keypad == '6') && (direction == 'R') = '7'
    | (keypad == '7') && (direction == 'U') = '3'
    | (keypad == '7') && (direction == 'D') = 'B'
    | (keypad == '7') && (direction == 'L') = '6'
    | (keypad == '7') && (direction == 'R') = '8'
    | (keypad == '8') && (direction == 'U') = '4'
    | (keypad == '8') && (direction == 'D') = 'C'
    | (keypad == '8') && (direction == 'L') = '7'
    | (keypad == '8') && (direction == 'R') = '9'
    | (keypad == '9') && (direction == 'L') = '8'
    | (keypad == 'A') && (direction == 'U') = '6'
    | (keypad == 'A') && (direction == 'R') = 'B'
    | (keypad == 'B') && (direction == 'U') = '7'
    | (keypad == 'B') && (direction == 'D') = 'D'
    | (keypad == 'B') && (direction == 'L') = 'A'
    | (keypad == 'B') && (direction == 'R') = 'C'
    | (keypad == 'C') && (direction == 'U') = '8'
    | (keypad == 'C') && (direction == 'L') = 'B'
    | (keypad == 'D') && (direction == 'U') = 'B'
    | otherwise = keypad
    
    
-- take an array of instructions and a starting number and find the final number
findNumberRow :: (Foldable t, Eq a, Num a) => a -> t Char -> a
findNumberRow number directionList = foldl findNextNumber number directionList

--puting it all together
almostFinalAnswer :: (Foldable t, Eq b, Num b) => [t Char] -> [b]
almostFinalAnswer aocinput = scanl findNumberRow 5 aocinput

-- get rid of that extra first number
finalAnswer :: (Foldable t, Eq a, Num a) => [t Char] -> [a]
finalAnswer aocinput = tail (almostFinalAnswer aocinput)

--make it one string
stringFinalAnswer :: Foldable t => [t Char] -> [Char]
stringFinalAnswer aocinput = concat (map show (finalAnswer aocinput))

-- same as findNumberRow but for part 2
partTwoFindKeyPadRow :: Foldable t => Char -> t Char -> Char
partTwoFindKeyPadRow keypadButton directionList = foldl findNextKeypadPartTwo keypadButton directionList

partTwoFinalAnswer :: Foldable t => [t Char] -> [Char]
partTwoFinalAnswer aocInput = tail (scanl partTwoFindKeyPadRow '5' aocInput)

main = do
    ourInput <- readLines "../input.txt"
    print "The answer to part 1 is:"
    print (stringFinalAnswer ourInput)
    print "The answer to part 2 is:"
    print (partTwoFinalAnswer ourInput)
import Data.List

--read from a file and put array where each line is an element. Point free.
readLines :: FilePath -> IO [String]
readLines = fmap lines . readFile 

-- Check 3 values to see if they make a valid triangle
validateTriangle :: (Ord a, Num a, Num p) => [a] -> p
validateTriangle [side1, side2, side3]
    | (side1 + side2 > side3) && (side1 + side3 > side2) && (side2 + side3 > side1) = 1
    | otherwise = 0

-- Take in a string of 3 numbers and run validateTriangle on it
evaluateTriple :: Num p => String -> p
evaluateTriple triple = validateTriangle (map (read::String->Int) (words triple))

checkAllPart1Triples :: Num b => [String] -> b
checkAllPart1Triples triples = sum (map evaluateTriple triples)

main = do
    ourInput <- readLines "../input.txt"
    print "The answer to part 1 is:"
    print (checkAllPart1Triples ourInput)
    --print (map words ourInput)

December

Of course, for December I worked on the Advent of Code 2021 live. Once again, it was awesome to work on it along with thousands of others and participate on the subreddit. The only bummer was that between WorldCon, work, and Christmas – I couldn’t spend as much time on it this year as I did last year. Funnily enough, I ended up with the same amount of stars as last year even though it’s a lot more contiguous this time around. I also worked with some buddies at work and that made it special this year as well.

Before I get on to what I thought of this year’s problems, I wanted to share some videos of others solving this year’s problems. First off, the Kotlin devs solving the first few days in Kotlin:

Second, one of my favorite programming YouTube channels is Code_Report. He likes to solve problems in APL (as well as various other languages) Here are his solutions of 2021 AoC in APL:

The first day itself was interesting because while it was nice and easy to get the right answer, there was also the ability to use a little trick to make things easier. When I found out about it I was tickled. Particularly, for part 2:

x + y + z < y + z + a

is the same as

x < a

Day 4 was a ton of fun designing a Bingo simulator.

"""Solution to Advent of Code 2021 Day 04: Giant Squid"""
from copy import deepcopy


def input_per_line(file: str):
    """This is for when each line is an input to the puzzle. The newline character is stripped."""
    with open(file, 'r') as input_file:
        return [line.rstrip() for line in input_file.readlines()]


def find_numbers_and_bingo_cards(our_input: list) -> (str, dict):
    """Take in our input and separate it out into a string of numbers to call and a dict of boards."""
    called_numbers = our_input[0]
    bingo_card_list = []
    our_input.pop(0)  # remove called numbers
    our_input.pop(0)  # remove initial blank line
    temp_card_list = []
    for row in our_input:
        if row == "":
            bingo_card_list.append(deepcopy(temp_card_list))
            temp_card_list.clear()
        else:
            temp_card_list.append(row)
    bingo_card_dict = {}
    for card_number, card in enumerate(bingo_card_list):
        bingo_card_dict[card_number] = {}
        for row_number, row in enumerate(card):
            split_row = row.split()
            for column_number, number in enumerate(split_row):
                bingo_card_dict[card_number][(row_number, column_number)] = [number, 0]
    return called_numbers, bingo_card_dict


def what_is_winning_board(boards: dict) -> [bool, int]:
    """Take in a list of boards and return whether there's a winner and the board number."""
    for bingo_board, value in boards.items():
        if value[(0, 0)][1] == 1 and value[(0, 1)][1] == 1 and value[(0, 2)][1] == 1 and value[(0, 3)][1] == 1 and \
                value[(0, 4)][1] == 1:
            return True, bingo_board
        elif value[(1, 0)][1] == 1 and value[(1, 1)][1] == 1 and value[(1, 2)][1] == 1 and value[(1, 3)][1] == 1 and \
                value[(1, 4)][1] == 1:
            return True, bingo_board
        elif value[(2, 0)][1] == 1 and value[(2, 1)][1] == 1 and value[(2, 2)][1] == 1 and value[(2, 3)][1] == 1 and \
                value[(2, 4)][1] == 1:
            return True, bingo_board
        elif value[(3, 0)][1] == 1 and value[(3, 1)][1] == 1 and value[(3, 2)][1] == 1 and value[(3, 3)][1] == 1 and \
                value[(3, 4)][1] == 1:
            return True, bingo_board
        elif value[(4, 0)][1] == 1 and value[(4, 1)][1] == 1 and value[(4, 2)][1] == 1 and value[(4, 3)][1] == 1 and \
                value[(4, 4)][1] == 1:
            return True, bingo_board
        elif value[(0, 0)][1] == 1 and value[(1, 0)][1] == 1 and value[(2, 0)][1] == 1 and value[(3, 0)][1] == 1 and \
                value[(4, 0)][1] == 1:
            return True, bingo_board
        elif value[(0, 1)][1] == 1 and value[(1, 1)][1] == 1 and value[(2, 1)][1] == 1 and value[(3, 1)][1] == 1 and \
                value[(4, 1)][1] == 1:
            return True, bingo_board
        elif value[(0, 2)][1] == 1 and value[(1, 2)][1] == 1 and value[(2, 2)][1] == 1 and value[(3, 2)][1] == 1 and \
                value[(4, 2)][1] == 1:
            return True, bingo_board
        elif value[(0, 3)][1] == 1 and value[(1, 3)][1] == 1 and value[(2, 3)][1] == 1 and value[(3, 3)][1] == 1 and \
                value[(4, 3)][1] == 1:
            return True, bingo_board
        elif value[(0, 4)][1] == 1 and value[(1, 4)][1] == 1 and value[(2, 4)][1] == 1 and value[(3, 4)][1] == 1 and \
                value[(4, 4)][1] == 1:
            return True, bingo_board
    return [False, 0]


def bingo_game(called_numbers: str, boards: dict) -> (int, int, dict):
    """Take in the numbers to call and the boards and return the board that wins, winning number, and dict."""
    called_numbers_list = called_numbers.split(",")
    final_number = 0
    win_and_number = [False, 0]
    boards_that_have_won = set()
    for number in called_numbers_list:
        for bingo_board, value in boards.items():
            for row in range(5):
                for column in range(5):
                    if value[(row, column)][0] == number:
                        boards[bingo_board][(row, column)][1] = 1
        win_and_number = what_is_winning_board(boards)
        if win_and_number[0]:
            final_number = number
            break
    return win_and_number[1], final_number, boards


def final_score(winning_number: str, winning_board: dict) -> int:
    """Take in the winning number and board and calculate the final score.
    Have to sum up all the numbers that were NOT called and multiply that by the winning number.
    """
    winning_number_int = int(winning_number)
    sum_of_unmarked = sum(int(value[0])
                          for value in winning_board.values()
                          if value[1] == 0)
    return winning_number_int * sum_of_unmarked


if __name__ == "__main__":
    called_numbers_and_boards = input_per_line("../input.txt")
    called_numbers, game_boards = find_numbers_and_bingo_cards(called_numbers_and_boards)
    winning_board, winning_number, modified_game_boards = bingo_game(called_numbers, game_boards)
    part_one_final_score = final_score(winning_number, modified_game_boards[winning_board])
    print(f"The winning score is from board {winning_board} and the score is {part_one_final_score}")
    # time for part 2
    winning_boards = set()
    total_game_boards = len(game_boards.keys())
    print(f"{total_game_boards=}")
    for _ in range(total_game_boards):
        winning_board, winning_number, modified_game_boards_part2 = bingo_game(called_numbers, game_boards)
        # print(f"{len(winning_boards)=}")
        # print(f"{len(game_boards.keys())=}")
        if len(winning_boards) < total_game_boards - 1:
            modified_game_boards_part2.pop(winning_board)
            winning_boards.add(winning_board)
        else:
            break
    part_two_final_score = final_score(winning_number, modified_game_boards_part2[winning_board])
    print(f"If you decide to let the wookie...I mean, the giant squid win, the winning score is from board "
          f"{winning_board} and the score is {part_two_final_score}")

Day 6 with the Lanternfish was the first hard part 2 day – the first one where a naive algorithm would never finish. I was very proud of myself at figuring out a solution based only on someone making a joke that Eric Wastl should have had us confront Grouper. 

"""Solution for Advent of Code 2021 Day 06: Lanternfish"""
from collections import Counter


def input_only_one_line(file: str):
    """Puzzle input is just one line."""
    with open(file, 'r') as input_file:
        return input_file.readline()


def fish_birth(fish_population: list) -> list:
    """Take in a list of fish ages and return the new list.

    Rules:
    - Decrement each fish by one
    - If a fish reaches 0, on the next day it becomes 6 and we add a fish with a timer of 8 to the end.
    """
    new_fish_to_add = 0
    new_fish_population = []
    for fish in fish_population:
        if fish == 0:
            new_fish_population.append(6)
            new_fish_to_add += 1
        else:
            new_fish_population.append(fish - 1)
    new_baby_fish_list = [8] * new_fish_to_add
    return new_fish_population + new_baby_fish_list


def fish_birth_part_2(fish_population: dict) -> dict:
    """Take in a dictionary of fish ages and return the new list.
    (because a list takes infinity to calculate and all the RAM)

    Rules:
    - Decrement each fish by one
    - If a fish reaches 0, on the next day it becomes 6 and we add a fish with a timer of 8 to the end."""
    new_fish_population = {}
    if 0 in fish_population:
        new_fish_population[8] = fish_population[0]
        new_fish_population[6] = fish_population[0]
    for fish_age in range(1, 9,):
        if fish_age in fish_population:
            if fish_age == 7:
                if 6 in new_fish_population:
                    new_fish_population[6] += fish_population[7]
                else:
                    new_fish_population[6] = fish_population[7]
            else:
                new_fish_population[fish_age-1] = fish_population[fish_age]
    return new_fish_population


if __name__ == "__main__":
    initial_fish_population = input_only_one_line("../input.txt")
    initial_fish_population = initial_fish_population.split(",")
    fish_population = [int(fish) for fish in initial_fish_population]
    part_one_fish_population = fish_population
    for day in range(80):
        # print(f"{day=}")
        part_one_fish_population = fish_birth(part_one_fish_population)
    print(f"After 80 days there are {len(part_one_fish_population)} lanternfish.")
    # Part 2 (although it should also work for part 1)
    fish_population_counter = Counter(fish_population)
    for day in range(256):
        # print(fish_population_counter)
        fish_population_counter = fish_birth_part_2(fish_population_counter)
    print(f"After 256 days there are {sum(fish_population_counter.values())} lanternfish.")

Day 8 with the seven segment search was my first very frustrating day. I worked with a friend on it for nearly 5 hours and couldn’t figure it out. We ended up adapting someone else’s answer to figure it out. It will help me with other similar problems in the future. 

Day 11 was my first use of a class. Usually I’ve found that classes lead to an over-engineered solution in AoC, but this time it really helped me out.

"""Solution for Advent of Code Day 11: Dumbo Octopus"""
import logging
logger = logging.getLogger(__name__)
logger.setLevel("INFO")


def input_per_line(file: str):
    """This is for when each line is an input to the puzzle. The newline character is stripped."""
    with open(file, 'r') as input_file:
        return [line.rstrip() for line in input_file.readlines()]


class DumboOctopus:
    """Class to hold octopus energy level and whether it has already flashed"""
    def __init__(self, initial_energy_level: int):
        self.energy_level: int = initial_energy_level
        self.flashed: bool = False


def create_octopus_grid(octopus_input: list) -> dict:
    """Take in a list of initial octopus arrangements and energy and turn into a dictionary of DumboOctopus."""
    octo_dict = {}
    for y, octopus_line in enumerate(octopus_input):
        for x, octopus in enumerate(octopus_line):
            octo_dict[(x, y)] = DumboOctopus(int(octopus))
    return octo_dict


def flash_octopuses(octopus_dictionary: dict) -> dict:
    """Flash the octopuses (recursively if necessary) and return the final octopus dictionary."""
    go_again = False
    for location, octopus in octopus_dictionary.items():
        if octopus.energy_level > 9 and octopus.flashed is False:
            logger.debug(f"I'm at {location=} and my flash status is {octopus.flashed}")
            octopus.flashed = True
            location_x = location[0]
            location_y = location[1]
            top_left = (location_x - 1, location_y - 1)
            top = (location_x, location_y - 1)
            top_right = (location_x + 1, location_y - 1)
            right = (location_x + 1, location_y)
            bottom_right = (location_x + 1, location_y + 1)
            bottom = (location_x, location_y + 1)
            bottom_left = (location_x - 1, location_y + 1)
            left = (location_x - 1, location_y)
            logger.debug(f"My neighbors are: {top_left}, {top}, {top_right}, {right}, {bottom_right}, {bottom},"
                         f" {bottom_left}, {left}")
            for neighbor in [top_left, top, top_right, right, bottom_right, bottom, bottom_left, left]:

                if neighbor in octopus_dictionary:
                    octopus_dictionary[neighbor].energy_level += 1
                    if octopus_dictionary[neighbor].flashed is False:
                        go_again = True
    if go_again:
        logger.debug("-----------------")
        logger.debug("About to do another loop")
        logger.debug("-----------------")
        return flash_octopuses(octopus_dictionary)
    return octopus_dictionary


def octopus_step(octopus_dictionary: dict) -> (dict, int):
    """Take in a dictionary of DumboOctopus and then run through a step.

    Step includes:
    - First, the energy level of each octopus increases by 1.
    - Then, any octopus with an energy level greater than 9 flashes.
    This increases the energy level of all adjacent octopuses by 1,
    including octopuses that are diagonally adjacent. If this causes an octopus
    to have an energy level greater than 9, it also flashes. This process continues as
    long as new octopuses keep having their energy level increased beyond 9.
    (An octopus can only flash at most once per step.)
    - Finally, any octopus that flashed during this step has its energy level set to 0,
    as it used all of its energy to flash.

    Return new status and number of flashes.
    """
    logger.debug("--------------------------------")
    logger.debug("Step 1: Raise Octopus energy levels")
    flash_count = 0
    # Step 1: Raise all octopus energy levels
    for octopus in octopus_dictionary.values():
        octopus.energy_level += 1
    logger.debug(f"{octopus_dictionary[(0, 0)].energy_level=}")
    logger.debug(f"{octopus_dictionary[(1, 0)].energy_level=}")
    logger.debug(f"{octopus_dictionary[(2, 0)].energy_level=}")
    # Step 2
    logger.debug("--------------------------------")
    logger.debug("Step 2: Flash the Octopuses")
    octopus_dictionary = flash_octopuses(octopus_dictionary)
    logger.debug(f"{octopus_dictionary[(0, 0)].energy_level=}")
    logger.debug(f"{octopus_dictionary[(1, 0)].energy_level=}")
    logger.debug(f"{octopus_dictionary[(2, 0)].energy_level=}")
    # Step 3
    logger.debug("--------------------------------")
    logger.debug("Step 3: Reset Flashed Octopus Energy levels to 0")
    for octopus in octopus_dictionary.values():
        if octopus.energy_level > 9:
            octopus.energy_level = 0
        if octopus.flashed:
            flash_count += 1
            octopus.flashed = False
    return octopus_dictionary, flash_count


if __name__ == "__main__":
    initial_octopus_energy = input_per_line("../input.txt")
    octopus_grid = create_octopus_grid(initial_octopus_energy)
    part_one_flashes = 0
    everyone_flash_step = 0
    for number in range(1000):
        octopus_grid, this_time_flashes = octopus_step(octopus_grid)
        if number < 100:
            part_one_flashes += this_time_flashes
        if this_time_flashes == 100:
            everyone_flash_step = number + 1
            break
    print(f"After 100 steps there have been {part_one_flashes} ? flashes")
    print(f"The first time all the dumbo ? (???) flash at once is {everyone_flash_step}")

That said, for Day 21, I decided to have a bit of fun and make it object oriented and treat it as though I was creating a real game. It ended up making things a bit easier to keep track of. 

"""Solution for Advent of Code 2021 Day 21: Dirac Dice """


def input_per_line(file: str):
    """This is for when each line is an input to the puzzle. The newline character is stripped."""
    with open(file, 'r') as input_file:
        return [line.rstrip() for line in input_file.readlines()]


class Player:
    def __init__(self, name, initial_location):
        self.player_name = name
        self.player_score = 0
        self.location = initial_location

    def move_player(self, die_roll: int):
        """Move the player from their current location to the new location."""
        # now take into account that the board is circular
        location = self.location + die_roll
        while location > 10:
            location = location - 10
        self.location = location

    def update_score(self):
        self.player_score += self.location

    def complete_move(self, die_roll: int):
        """Move player and update score"""
        self.move_player(die_roll)
        self.update_score()

    def __repr__(self):
        return f"{self.player_name} at {self.location} with score: {self.player_score}"


class Die:
    def __init__(self):
        self.number_rolled = 0
        self.times_rolled = 0

    def next_roll(self, practice_mode=True):
        """Calculate the next roll of the die. In practice mode it's just a one-up"""
        if practice_mode:
            self.number_rolled += 1
            self.times_rolled += 1
        return self.number_rolled

    def __repr__(self):
        return f"Die rolled {self.times_rolled} times. Most recent number rolled {self.number_rolled}"


if __name__ == "__main__":
    player_starting_positions = input_per_line("../input.txt")
    # create the players
    _, player_one_starting_position = player_starting_positions[0].split(":")
    player_one_starting_position = int(player_one_starting_position)
    _, player_two_starting_position = player_starting_positions[1].split(":")
    player_two_starting_position = int(player_two_starting_position)
    player_one = Player("Player 1", player_one_starting_position)
    player_two = Player("Player 2", player_two_starting_position)
    max_player_score = 0
    # create die
    part_one_die = Die()
    # while loop that ends when max_player_score > 999
    print("About to start game")
    print(player_one)
    print(player_two)
    while max_player_score < 1000:
        print("-------")
        print("New Round")
        # roll die 3 times
        rolls = [part_one_die.next_roll(practice_mode=True), part_one_die.next_roll(practice_mode=True),
                 part_one_die.next_roll(practice_mode=True)]
        player_one_moves = sum(rolls)
        print(f"{player_one_moves=}")
        player_one.complete_move(player_one_moves)
        if player_one.player_score > 999:
            break
        rolls.clear()
        rolls = [part_one_die.next_roll(practice_mode=True), part_one_die.next_roll(practice_mode=True),
                 part_one_die.next_roll(practice_mode=True)]
        player_two_moves = sum(rolls)
        player_two.complete_move(player_two_moves)
        rolls.clear()
        print(player_one)
        print(player_two)
        max_player_score = max(player_one.player_score, player_two.player_score)
    # someone has scored 1000
    print("Game Over.")
    print(player_one)
    print(player_two)
    print(part_one_die)
    loser_score = min(player_one.player_score, player_two.player_score)
    die_rolls = part_one_die.times_rolled
    print(f"Loser score times number of die rolls is {loser_score * die_rolls}")

I think I will be taking a break from programming at least for the month of January. I’d like to focus on a few other things – end of the year blog posts, cooking, catching up on 2 years worth of cookbooks, and some gaming. Afterward, I think I may work on a few programming projects before coming back to Advent of Code, but I’m not 100% sure what I’ll stick to, these are just my current plans.

Thoughts on Worldcon 2021 (Discon 3)

This strange, COVID-filled year was the year WorldCon was local to me, so I figured it was the best time to check it out. I didn’t need to pay for a hotel or flight, just a few days of parking and metro line fees. Overall, I thought it was fine. I enjoyed the panels I attended, especially when Scalzi read from his upcoming book, Kaiju Preservation Society. But I didn’t become a convert like the folks at the First Time Attendees panel who have been attending for decades. Outside of that, I had a few thoughts about my experiences this year:

Kaffeeklatsches

One of the best parts of the convention were the Koffeeklatches. Basically about 10 folks around the table with an author or other famous person. More or less everyone gets the chance to ask at least one question (as long as they’re assertive enough to ask – definitely some gender issues at some of the ones I attended where the men spoke up a lot more than the women, including pushing past the woman, who usually yielded). 

It was fascinating to attend Jo Walton’s event. I loved her Thessaly trilogy and so I was curious to see what kind of person she was. The highlight for me was when she blew my mind while responding to someone’s question – Ms Walton mentioned that having European fae (or other mythological creatures) in North America is a kind of literary colonialism. Because the fae (and friends) are supposed to be creatures from the deep past. So shouldn’t America be full of the Native American mythological creatures? (Neal Gaiman deals with this in the best way in American Gods) I’d never thought of it before! It was also neat to learn that The Just City was based on a story she started while she was a teenager.

I also attended Kaffeeklatsches for magazine editors Neil Clarke of Clarkesworld and Jason Sizemore of Apex Magazine. The magazines have slightly different focuses, even if they’re both SFF magazines. Since they’re both smaller magazines, they’re very much reflections of their editors. (I know that this is true to a certain extent of any magazine, but the larger ones that have been around for decades have editorial inertia as well) Both editors spoke about how they work with submissions and how many times they have to go back and forth with contributors. Both gave horror stories about people who couldn’t take rejection well and, in some cases, threatened the editors. It was fascinating to see the behind-the-scenes aspects. I also got the impression that, generally speaking, SFF editors are a genial bunch who help each other out even though they are ostensibly competition. I think they tend to see themselves as part of a pie that just grows with more publishers. From the outside, the short story/novella market does seem to be growing. 

The Future of WorldCon

I’m nearly 40 and, based on appearances, there were not too many folks at the convention younger than me. It seemed everyone was either within a decade of me or older. Why is this? I would wager that it may have something to do with the mainstreaming of SFF and the growth of the internet. Until very recently SFF has been the domain of us nerds and geeks. Even being a  gamer was considered a bit of a badge of shame until a decade or so ago. (I certainly remember in middle school that it was considered a more nerdy pursuit) And now with the MCU, HBO’s adaptation of A Game of Thrones, The Wheel of TIme, and a million other SFF books being adapted for the masses into TV shows and movies – it’s no longer so geeky to be into SFF. Notice how both the west and east coast Comic Cons now require a lottery for attendance. So perhaps there is a less of a need for WoldCon to meet with others who share your genre likes? And with the internet and massive forums like Reddit, you can find others to talk to, even if you can’t meet in person. And it even allows you to go so deep like the subreddit dedicated only to Brandon Sanderson’s Cosmere. 

Perhaps there is still some hope for growth of the WorldCon family, though. During the “Is Genre too big for the Hugos” panel (which turned out not to be exactly what I expected) there was a large focus on growing the fandom in diverse areas. I think the WorldCon/Discon committee did the best they could to make Discon III an inviting place. There were gender neutral bathrooms. Lots of admonitions to be nice to others, despite differences. Lots of signs to remind folks to give up seats to others because “not all disabilities are visible”. So if we can grow the fandom, maybe that helps keep things going?

Tying into all of this – this was the second year that WorldCon was virtual. To be most specific, it was a hybrid con this year. I attended both in person and virtually. The first few days I attended in person and I watched the Hugo Awards and the Sunday panels virtually. I think the virtual programming is important because it helps folks attend who can’t spend a thousand or so on travel, hotels, and food. This helps with diversity as well because we can also consider rich vs poor as a diversity dimension. And since membership in WorldCon is required to vote in the Hugos, it makes the Hugos more representative as well. I think between the Discord chat and the (I believe) Vimeo video, things worked well. Things that could be improved in the future if WorldCon moves to a hybrid model include better audio and making sure someone is monitoring the chats for questions. I also think that YouTube might be better than Vimeo for casting to a larger screen. 

Last Random Thoughts

Based on the questions asked during the panels, it seems there were more writers vs non-writers in attendance. (Maybe only writers were likely to ask questions? Or maybe it’s just the panels I happened to attend)

The genre has definitely become huge for quite a few niches to have formed. I attended panels where I had no idea who the authors were and yet there were fans who were extremely excited to see them. 

The convention did a great job dealing with COVID. For the next week or so afterwards they kept us informed of people who reported positive cases and the panels they attended.

The Bigfoot Endurance Trail Race

Today I ran my first trail run, the Bigfoot Endurance Trail. I will definitely say that Ripit Events did a great job running the race. There were lots of good reminder emails leading up to the race. They had folks managing parking. Everything went smoothly and, more or less, on time. 

The race was at Rockburn State Park, making this the first race for which I didn’t have to get up at an ungodly hour to attend. It’s just a 10 minute-ish drive from the house. I’d never done any of the trails – usually we just take the kids to the playground. Luckily, one of my friends at work warned me to pick up my feet. I don’t think I necessarily run at a shuffle, but it did make me run with more awareness of the trail. This was a good thing because Mother Nature was out to get me! The 10 mile race consisted of two 5-mile loops. On the first loop I mildly rolled both ankles either in sections that were nothing but tree roots or where the gravel or sand weren’t as tightly packed as they could be. However, tragedy struck (at least race-level tragedy) with half a mile to go. I rolled my left ankle HARD. Like, I’m definitely going to need some Ibuprofen and an ice pack when I get home hard. That threw off my gait and so with literally only a quarter of a mile to go, I couldn’t lift my foot high enough and tripped over a root. I took a spill, but was luckily wearing gloves. I only scratched up my knee. Very nicely – perhaps because this is more of a fun-run community thing than the Boston Marathon or something like that – a couple of runners both in front and behind of me stopped to help me up and make sure I was fine. One of them also checked up on me after the race. This tumble cost me for my age group to fall from 2nd place to 3rd place (literally just 20 seconds difference).

So how did I do? Really darned well! I’m really happy with where my training got me.

all my stats together
All my stats in one nice little widget

My only disappointment was my average pace. I was going for something closer to 7:30. But, considering I had to constantly dodge roots (thanks, Mother Nature!) and that the trail was, at some points, so narrow that I couldn’t pass anyone going slowly in front of me, I was happy. After all, look at the deltas here for my age group:

Me against my age group

If I had only one complaint it’s that the whole park wasn’t closed and so on the second loop I had to dodge around cyclists and regular, non-racing folks who were just going for a hike on the trail. 

So, will I do it again? I’m not sure. Road races aren’t perfect. You have to watch out for pot holes or places where the pavement or asphalt isn’t level. However, there were sections of this race where I had literally no choice but to step on roots. There wasn’t enough space between the roots for me to place my feet between them. It makes me wonder about the potentially more treacherous trail races. As I get older I’m also going to recover less easily from rolled ankles, falls, etc. I could just take it as an easier race and go faster on the more stable parts and slower on the parts more full of roots. I don’t know. Right now I’m undecided. But I think as a “genre” I’m not dying to do more trail races. All that said, the #1 winner is 49! So nothing says you can’t do it as you get older. And I think as long as your knees and hips are OK, long distance running at the non-Olympics level seems be be a sport that you can do in middle age (unlike, say, basketball)

I am glad I did it at least once, though.

Finished the race!!
Finished the race!!

PS – screw COVID and supply chain issues because we didn’t get to get our medals today. And, yeah, it’s not like I’m getting a first-place Olympic medal, but there’s something about that medal signifying accomplishment of training that’s slightly missing and that’s a real bummer!

GPS of my run
GPS of my run – may have some inconsistencies due to loss of signal under the trees

MxPx – Panic and Secret Weapon (Special Edition)

I continued to catch up with the MxPx back catalog and purchased both Panic and Secret Weapon. My initial feelings upon listening were that I liked a lot more songs on Secret Weapon. But maybe, as with Before Everything & After this would turn out to be just a first impression where when looking at each song one at time would leave me feeling differently. 

cover of Panic by MxPx
cover of Panic by MxPx

Panic

The album definitely sets you up to think it may be a dark departure when comparing the cover to those that came before it. It comes out 2 years after the previous album, but there’s nothing unusual about that. Going back to Teenage Politics and not counting live albums, B-sides, or covers, they were averaging about 1 album every two years. The album is their only album from the label SideOneDummy. It’s interesting that (at least according to the wording on Wikipedia) they were dropped from A&M. They seem to be going from success to success with that label and their previous album was their highest charting album. Maybe it was MxPx initiated after being made to make a more “commercial” sound? But this is just speculation on my part after having heard the previous album as the band trying to sound more like Good Charlotte. Taking a look at running times, the album is a return to sub-three minute songs with two big outliers – “Heard that Sound” and “Waiting for the World To End” – both clocking in at 3:40. Let’s take this album track-by-track:

  1. The Darkest Places – This is a VERY classic MxPx sounding song. Very fast. It’s about facing the world vs just giving up. I don’t know what Herrerra’s political leanings are, but since album came out in 2005, it’s probably written in 2003 or 2004 – in the shadow of the 11 Sept attacks and the start of the War on Terrorism. The lyrics mention flipping the channels on the TV and getting depressed. Seems to be a call to the youth to not be depressed and to fight the situation. Could also be a reference to shining a light into dark places as a Christian metaphor. It’s still 10 years before Herrera releases the statement distancing himself from the religion.
  2. Young and Depressed – Just like some of the songs on Teenage Politics, I’m rapidly aging away from this song, which seems to be targeted from late teens to mid-20s fans. It seems to really target that feeling of ennui that can strike around that age. However, it’s pretty clear that MxPx is also viewing it a bit tongue-in-cheek with the chorus line: You’re young and depressed/but you’re pretty well dressed . I think it’s meant to acknowledge that it’s not all as bad as you think. It also always makes me think of the Fall Out Boy lyric: …And perfect boys with their perfect lives/Nobody wants to hear you sing about tragedy…
  3. Heard that Sound – This song seems to flow from the previous one with Herrera starting off feeling depressed before the “sound” he heard seems to lift his spirits. Works quite well thematically in order after the previous song. So far the songs seem to match the promise of the darker cover – previous covers were usually pictures of PxPx or the band members. (Note from after I wrote this section, but before I published it: When I was listening to MxPx’s new live album Southbound to San Antonio Herrera mentions as an intro to this song that it’s about a sold out show he couldn’t get into)
  4. Cold Streets – This one is the first one on this album that I’m less of a fan of. It’s a much harder, distorted sound than the usual MxPx. It does also continue an overall depressing streak in the songs. The title of “Panic” is starting to make more sense. It’s not a horrible song, or even a bad song. It’s just not what I tend to like out of most of my MxPx songs. 
  5. The Story – The story continues this heavier rock sound, but this one sounds more like a Good Charlotte-style song. So I like it a bit more than the previous song. Wonder if it’s from a time period of song writing closer to the previous album. Lyrically, it’s kind of wondering where life goes from this point forward. 
  6. Wrecking Hotel Rooms – The first “slow song” on this album. Funny, for the title and the way the song starts, it’s a love song. It’s asking if the other person is pining for the narrator. Although, strangely, the second verse has the narrator admitting they’re singing to a person they’ve never even met. It’s oddly poetic in a way that MxPx songs usually aren’t. I like this one a lot. Definitely in the top three on this album for me. I wonder if the institutions line is a reference to the need for pills in the previous album. 
  7. Late Again – This song has a VERY weirdly country sound to it. It’s quite an outlier for MxPx and yet the band seems to be having a ton of fun with it. I wonder if it’s at all a reference to “My Life Story” on The Ever Passing Moment where he also mentions often being late. 
  8. Kicking and Screaming – Another Good Charlotte-sounding song. I would almost call it a punk version of “I did it my way”.
  9. Grey Skies Turn Blue – Back to a more modern MxPx sound for this song. The lyrics point to the singer saying he’ll rejoin someone (seems to be a significant other) after he gets over his depression. Sonically, I like this song.
  10. Emotional Anarchist – Lyrically, this one left me a bit puzzled, like the first few Fall Out Boy albums. Each line makes sense on its own, but I’m not sure how it all comes together. Sonically, the song is fun. It still remains tinged with some sadder elements.
  11. Call In Sick – 100% classic MxPx sound for this song. The lyrics are pretty fun, the narrator is just exhorting their significant other to call in sick to work so they can go have a vacation together, waking up together. Well, literally, it’s calling for an abandonment of societal responsibilities and eloping forever. But I think the sentiment is still fun and we’re finally into a bit of a happier and fun song. Also, this is a recurring theme with MxPx songs. The previous album has the singer telling the person to quit their job and stay with him, etc. 
  12. Get Me Out – A return to that hard punk sound (almost screamo) of Cold Streets. MxPx usually has at least one song like this per album or every other album. It’s not really my favorite. At least, it’s not what I come to MxPx for. Lyrically, just wants to be out of his current situation.
  13. Waiting for the World to End – Back to the usual MxPx sound. Seems to be sort of a companion to The Story (track 5). 
  14. This Weekend – A slower song about hanging out with your significant other and friends over the weekend. 

After going back over each of the tracks, it looks like I enjoyed about half the songs on the album. That’s not a horrible ratio and it’s a rare album where I like more than three quarters of it. I think it’s generally well put together and the song order is well-considered. There are a few odd placements. Get Me Out seems to sit out of place from the songs around it, for example. I think the biggest thing that makes me like this one a little less is the overall depressing feel over the album. I understand the sentiment – both from an age perspective and as someone who suffers bouts of melancholy. But as a whole album, it’s a bit much.

cover of Secret Weapon by MxPx
cover of Secret Weapon by MxPx

Secret Weapon

For this album MxPx returned to their original label, Tooth and Nail. There are some songs on the album that,in hindsight, seem to foreshadow MxPx moving on to their own record label, Rock City Recording Company, after this. They seem (speculation on my part) to have followed in the path of fellow Tooth and Nail former peer Icon for Hire in desiring the ability to make their own music without studio interference. After some cursory research, it appears to be a truly independent label, not an imprint of a larger label.

  1. Secret Weapon – A faster-paced MxPx song, but a real banger. Great song to put on in the morning to wake up and pump yourself up. It’s about embracing your own talents and strengths and pushing forward. A great reversal to the tone of the previous album. 
  2. Shut It Down – Love the first line “This is a public service announcement….with GUITARS!” Another fast-paced MxPx song. More than ten years later, the exhortation to not let a cell phone or TV become reality seems quite quaint. I mean, I do think that connected folks are, on average, spending too much time in a virtual existence that is toxic, but this seems to be a battle that those complaining about it in the early 2000s have definitely lost. I think at this point it’s about teaching people how to find balance and fun/peace out of these interactions rather than doom-scrolling. Sonically the song is right on the border between a faster MxPx song I can enjoy and the hardcore stuff I complained about in Panic.
  3. Here’s to the Life – I’m not 100% sure of the meaning of the lyrics here. I even looked them up to make sure I was hearing them correctly. It’s another one of those more poetic ones. But it seems to me to be a slight acknowledgement that the life of a musician can be less than ideal. I do like it, musically.
  4. Top of the Charts – This one is DEFINITELY about the record labels messing with MxPx’s song-writing. Are they talking about A&M, SideOneDummy, or Tooth & Nail? Again, foreshadowing that they’d soon be leaving for their own label. They do reference a lyric from Before and Everything After so maybe that’s a clue. I like it both sonically and lyrically.
  5. Punk Rawk Celebrity – A song both about about how celebrity is fleeting and also about how celebrity is the antithesis of the punk rock ethos. A very interesting song, especially when the trumpets jump in. Reminds me of some of the Panic at the Disco songs of the era that also would sometimes bring in various non-traditional-rock instruments.
  6. Contention – About the same speed as “Shut It Down”. This one is right on the other side of the border for me. A little too hard punk compared to my favorite MxPx songs. 
  7. Angels – Melodically MxPx has been here before although it’s a bit more pop than punk in an album that’s mostly a return to the skate punk sound. I do like the sound and lyrics a lot. Basically a song about guardian angels.
  8. Drowning – Sonically, it’s a typical MxPx song, if a little long 3:51. Lyrically, it’s about the feelings after a breakup
  9. Chop Shop – Another really fast one. This one’s a bit gruesome for me. 
  10. You’re On Fire – I see this one as a companion to Secret Weapon. Basically someone who’s got things going well for them (after a bad time) and now they’re unstoppable. 
  11. Bass So Low – A fun chorus that really rumbles your bass. Always have a good time listening to this song.
  12. Sad Sad Song – A funny Beach Boys via Skate Punk breakup song. Unlike some of MxPx’s other breakup or post-breakup songs where the singer is feeling blue, this one is more about how the other person’s a bit nuts and he’s glad to be broken up. Also a good listen.
  13. Never Better Than Now – A song about getting back up and trying again when things go awry. Another good song.
  14. Biting the Bullet (Is Bad for Business) – About a girl (maybe autistic?) who’s having a hard time. 
  15. Not Nothing – The back half of this album seems to be about breakups. Another breakup song. Still, a banging song that isn’t a chore to listen to.
  16. Tightly Wound – A really fun song about hoping you win soon. 
  17. The Hoo-Ha Jangle – Not 100% sure what the lyrics are about, but another fun song to listen to.
  18. Madcap Scheme – Another one of those songs that appears on nearly every album – although this could be friends or significant others – let’s just throw caution to the wind and have fun tonight type of song.
  19. Throw Your Body in the Air – Song about moshing/jumping around at a concert. 

After going through both albums track-by-track it’s definitely the case that I like Secret Weapon a lot more, it’s not just a bad first impression of Panic. Next up is Punk Rawk Christmas and since it’s not just covers of the usual Christmas songs, I think I’ll give it a review.

Adding Art to the Mundane

Nathan's Famous Hot Dogs in Coney Island with a painting on the doors
I love the idea of having art on the gates that come down when the the restaurant is closed. Rather than the ugly grey, possibly tagged with grafitti, we get this amazing art.

I saw this when I was doing a boardwalk run this summer. I’d been visiting Coney Island for nearly 20 years at this point, but the recent revitalization has been impressive to see. That includes ideas like this art that bring happiness and joy even when Nathan’s is closed.