In 1995, the PlayStation game Wipeout was released, and its influence was massive. It had great playability, music and visuals. The clean and futuristic visuals were designed by The Designers Republic and they were incorporated into all parts of the game, advertising copy and at least one music video.
The music of Wipeout included works by the Chemical Brothers, Fluke, and The Prodigy. The official music video for Fluke's "Atom Bomb" revolves around Ariel Tetsuo, one of the characters in Wipeout. it's a combination of in-game video, animation, effects, and live video suitable for a screening at a rave party.
For years, when returning from San Francisco on an empty 280 freeway with racetrack curves, I'd play Atom Bomb (or just remember the tune) and be reminded of flying by on a Wipeout course looking for imaginary power-up pads.
The Chemical Brothers
Wipeout got me further interested in electronic and techno music from artists like The Chemical Brothers. The Chemical Brothers have a history of innovative music video from creative directors like the legendary Michel Gondry. Their video for "Wide Open" ft. Beck stars Sonoya Mizuno dancing alone in a warehouse as parts of her body become 3D-printed lattice constructions – in one amazing four and a half minute shot. It was directed by Dom & Nic.
The video's worth watching. The music and the dance are haunting on their own, and her transformation to the printed lattice self is on another level. It's amazing.
Fx Guide has an article on the making of that music video, So, just how was that Chemical Brothers video made?. Dom & Nic posted a "Behind the Scenes: The Chemical Brothers ‘Wide Open’" video on Vimeo here:
FX's miniseries Devs
I've just started Devs and it looks to be really good. The main character is played by Sonoya Mizuno, the dancer from "Wide Open"! Oh man, there's nothing but positive associations there for me going all the way back to 1995. Everything from FX, Sonoya Mizuno, to the Chemical Brothers all the way back to Wipeout.
I really hope I like the rest of Devs. ^_^;
PSNation was the source for the Wipeout image.
What is this?
This is a fun card game that’s easy to learn. Each player tries to discard all the cards in their hand. When someone discards their last card, the others are penalized points for the cards left in their hands.
Before playing, the players agree to play to a certain score maximum. (Usually 100.) Once any player reaches the maximum the game is over. The player with the lowest score is the winner.
Chinese Poker is played with a standard deck of 52 playing cards. Three 2s and one ace are removed, leaving a deck with 48 cards. Suits and color do not matter. Only the values of the cards matter. Aces beat kings. When played individually, the two is the high card. (It will beat an ace.) When played in straights, the two is the low card.
Cutting for the First Deal
Each player selects a single card from the deck. Aces are high during the cut. The owner of the high card gets the deal. The dealer also gets to make the first play. (“Plays” are described below.) The play the rotates clockwise around the table.
- 2 Players: The dealer deals out 3 hands of 16 cards each. Then the dealer gets to choose any one of the three hands. The other player then gets to select one of the remaining two hands.
- 3 Players: The dealer deals out 3 hands of 16 cards each. Each player plays the hand they are dealt.
- 4 Players: The dealer deals out 4 hands of 12 cards each. Each player plays the hand they are dealt.
First Play: The current player may discard a valid “hand” from his whole hand, or choose to pass. To discard a hand, place the selected cards face up on the table to show the other players. They will have to beat this hand or pass. Valid hands are described below.
Subsequent Plays: Each subsequent player may discard a better hand of the same type as the one shown on the table (if they have such a hand) or pass. For example, if the current play on the table is a pair of 8s, the only play that may be discarded to beat that hand is another pair of a higher value (9s and up). (Four of a kind is a wild hand, an exception, and is described below.)
If everyone passes, then the player who discarded the most recent hand may discard a new hand of any type. (As in the first play.) Once a player discards his last card, he has won the round and each player has to tally his own score. The winner of the round gets to deal the next round. (This can be very frustrating if you’re not the winner!)
Valid Hands to Discard
- Single Card: A single card may be discarded as a hand. 3 is the lowest and 2 is the highest.
- Pairs: A single pair, or sequential pairs, may be discarded as a hand. Any number of pairs may be discarded in a hand, as long as they are sequential. (E.g., 445566 is a valid hand, while 445577 is not because 7 does not come immediately after 5.)
- Three of a Kinds: As pairs, three of a kinds may be played individually, or in groups of sequential values. (E.g., 333444.)
- Full House: A pair accompanied by a three of a kind. One the value of the three of a kind needs to be beaten in the subsequent play. (99944 beats 777KK because 9s are higher than 7s.)
- Straight: Straights must be at least 5 cards long. They may be as long as 13 cards. The 2 is the low card in straights. To beat a straight, you must play a higher straight of exactly the same number of cards. (E.g., 456789 beats 345678.)
- Four of a Kind: Four of a kinds must be played with a fifth card. The value of the fifth card is the value to be beat by another four of a kind. (E.g., 84444 beats 6JJJJ because 8s are higher than 6s.) Four of a kinds are wild hands and may be played on any other kind of hand!
Each time somebody has won a round, the other players have to count the number of their remaining cards, and add points to their current score according to the following rules:
- All cards caught in the hand: 50 points.
- All cards but one caught in the hand: 40 points.
- All cards but two caught in the hand: 30 points.
- Otherwise: Add 1 point for each card caught in the hand.
The game was taught to us by Derek and Pei Fluker. We know this is not the same as another gambling game called Chinese Poker.
In 1993, I created what may be the world’s first online card game with a GUI interface. Here’s the “About” window for the classic Macintosh version.
Every few years, disparate web services seem to conspire to shutdown or break at the same time. It happened again last month. Some of them you may have heard of, but the others broke or disappeared without any prior announcement. I've been through this sort of thing before, so I knew it'd eventually take a little work to keep my services humming along again.
Google Plus announced that they'd shut down their service ten months before doing so. That gave me a few months to avoid addressing the issue, and then eventually getting around to backup up my posts. I love my backup, it's so much faster to load and navigate than Google's service.
Google Image Charts
Google Image Charts gave us seven years to prepare for their shutdown. They even staged one short and one long outage to draw our attention to the upcoming closure. I ignored it the whole time, and then scrambled to find a replacement after the permanent closure.
FourSquare Activity Feeds
FourSquare shut down their user activity feeds without any notice. They were in a sort of limbo as they tracked Swarm activity, but the URLs were still FourSquare. The feed was very important to me because I maintain a lifestream. I became a FourSquare developer to access my content, and discovered that I could make a more informative feed on my own than the feed they'd just killed. Yay! That turned out to be an unexpected win.
Internal Server Errors
My shared web hosting service started suffering intermittent "internal server errors" (HTTP code 500) at the same time as the above changes. I turned off FastCGI, but that didn't help. Then I upgraded PHP from 5.6 to 7.2, which broke my blog engine (Habari), so I fixed that, but the "500"s kept happening. As a last resort, DreamHost moved my site from Virginia to some new servers in Oregon. That fixed the issue, and even better, upgraded my account to a Linux distro that uses Python 3.6! Yay, that means I finally get to use f-strings at DreamHost! DreamHost was the last place where I couldn't use f-strings, so I'm really happy that they've caught up.
Despite the handful of shutdowns and failures, I ended up with a couple of unexpected wins over the previous situation, so I'm pretty happy about that.
I've changed this blog's protocol to HTTPS. If you use a feed reader, please update your feed reader's URL for this site. The new one is mostly the same, but uses HTTPS: https://david.dlma.com/blog/atom/1.
Whenever I have to change a feed URL, I either redirect from the old one, or when I can't, I leave a message like this as the last message on the old feel URL.
I had to do this once before, in 2012. Here's a mention of practicing good feed hygiene back then, too.
Here's a puzzle my son put in my Father's Day card. He wrote, "In the figure below, fill in each of the sixteen numbers from 1 to 16 such that the four rows and three columns (use the lines as a guide) add up to 29."
You can tell from the pencil markings that I tried to solve it by hand. But it wasn't long before I thought about trying to solve it with a Python script. There's a naïve script that you could write:
for permutation in itertools.permutations(range(1, 17)): if rows_and_cols_add_up(permutation): print permutation
That one just tries every single ordering of the numbers and then presumably checks things like sum(p, p, p) == 29 in the testing function. The only problem with that approach is that the potato I'm using for a computer might not finish going through the permutations before the next Father's Day. (No, really. There are over 20 trillion permutations to check.)
Fast but Not Exactly Correct
So I decided to attack it from another angle, just look at the rows first. There are four rows, let's call them rows a, b, c, and d from top to bottom. Row a has a 3-tuple in it, b has a 4-tuple, c has a 4-tuple, and d has a 3-tuple. I wrote an algorithm that first finds all the 3-tuples and 4-tuples that add up to 29.
From those sets of valid 3-tuples and 4 tuples, the algorithm chooses a 3-tuple, a 4-tuple, another 4-tuple, and finally a 3-tuple. It assures that there are no duplicate numbers across any of the tuples. Then if that's true, it checks the three columns, and if they add up to 29, it prints the winning results.
And voilà, it worked, and demonstrated that there are multiple solutions, and found them in mere seconds! I saved that initial implementation as a gist. I thought I was done, but the code was bugging me. I knew that while it reflected the way I approached the problem, it was inefficient (it did too many comparisons), it included duplicate solutions (that's the "wrong" part of it), and it wasn't Pythonic.
Correct but Slower
So, a few hours later, I refactored the code, and realized I could replace four nested "for" loops (one for each row) with two loops that each choose two items from their containers (one container of 3-tuples, and one of 4-tuples). And I could check for no-duplicates on one line using a set() instead of explicitly checking for duplicates with "any(this in that)" calls.
The refactor replaced 22 lines of deeply nested code with four lines of simpler code. Not only were there fewer lines, I hoped the program became more efficient because it's iterating fewer times. That's the best kind of change! It's smaller, easier to reason about, and should be faster when executed. It's very satisfying to commit a change like that! The changes to the gist can be seen here:
The red lines are being replaced by the green lines. It's nice to see lots of code being replace by fewer lines of code.
Correct and Fast
I was wrong about it being faster!
Profiling showed that while the change above was more correct because it didn't include duplicate results, it was inefficient because it lacked the earlier pruning between the for loops from the first try. Once I restored an "if" call between the two new for loops, the program became faster than the first run.
for b, c in itertools.combinations(t4, 2): if len(set(b + c)) == 8: for a, d in itertools.combinations(t3, 2): if len(set(a + b + c + d)) == 14: test_rows(a, b, c, d)
It's very rewarding to bend your way of thinking to different types of solutions and to be able to verify those results. This feeling is one of the reasons I still code.
Here's the final version of the solution generating code.