/Making a Fortune Telling Machine

Making a Fortune Telling Machine

Some time ago, Chris asked me to make some sort of game or party activity for her wedding to Rob.

I knew I wanted to continue to explore the domain of low-key/opt-in ongoing party games that help guests strike up conversations, as I did with Secret Agent Party, but the specific theme and mechanic eluded me for some time. A wedding is, of course, not a neutral venue; the project needed to be relevant to both the overall meaning of a wedding and to Chris and Rob’s relationship in particular.

I found myself lingering on the importance of the future, and more specifically the idea of choosing your own future by being deliberate about the values that would guide it, which is central to wedding vows and also what I see as one of Rob and Chris’s great strengths as a team. Two other influences were Chris’s interest in Tarot as a means of generating stories, and the wedding’s visual theme of stars and constellations.

I half-formed many ideas, squandering quite a bit of time, before hitting upon one that I was enthusiastic about: a deck of cards and a fortune-telling device that would take readings from hands of cards that were dealt to it. Attendees would be given two cards and a few coins; they would be required to show three cards to the device and spend a coin to receive a fortune.
The Object.
Photo by Vincent Zeng

Most of the rest of this post talks about implementation details. Skip to the bottom if you just want to see a couple of pictures of the project in action.

I decided on the concept just about two months before the wedding, and I was traveling for two of those weekends (one was !!Con!), so the whole project was on a somewhat compressed timeline. Fortunately, David was able to take on the computer vision aspect of the project, and Trevor paired with me on some of the remaining code.

I envisioned this as an entirely screen-free interaction. Input would be the cards and coins; output would be a paper printout.

Getting to put real coins in a real coin slot felt important, since it lent literal and figurative weight to the interaction: handling the coins and hearing them clink in the slot is cooler than just pushing a button, and their finite nature would rate-limit readings and thus encourage players to make deliberate decisions about the hands they played. I also envisioned the potential for other mini games or activities that players could undertake to earn more coins.

The printouts would serve as being something between a party favor and a possible guestbook entry. Many of my earlier ideas had centered around the collaborative creation of a permanent artifact, with guestbooks being a clearly suitable medium for a wedding. San Tilapian Studies was a strong precedent. With fortunes in particular, ideas like “advice jars” were relevant as well — for example, there could be a “fortune donation” box where guests could give Chris and Rob whichever fortunes they thought were good ones. I ended up not doing this. Thermal prints are not exactly archival quality, and I think it would have been difficult to signal how fortune donation was supposed to work. Also, it seemed presumptuous to have guests base their advice on fortunes that were ultimately all curated by me.

Since the hardware was integral to the vision of the project, the first thing I did when I settled on a concept was to price out parts and order them.

Because of the tight deadline, I chose hardware that would be as straightforward to work with as possible. I sourced as much as possible from Adafruit, since I knew I’d be in good hands if I did run into any trouble. The thermal printer was what Adafruit calls their “mini thermal printer”, that is, the one with the biggest enclosure; I didn’t want to worry about running out of paper nor did I want to build my own enclosure. The project was powered by a Raspberry Pi3 and used the official RasPi camera module to take pictures for card recognition.

Adafruit’s coin slot was unfortunately out of stock at the time, but I got the very similar CH-923 elsewhere. (The CH-923 is a six-coin slot, which is overkill, but it was the same price as the single-coin slots and I figured it would give me flexibility if I wanted it.)

I also bought approximately a lifetime supply of thermal paper from Amazon.

For the coins themselves, I didn’t want to use legal US currency. This was partly because weird coins make for a better magical artifact, but mostly because I wanted to control the coin supply. I was happy to find used “pachislo” coins with a silly little ship and stars design on them, a perfect match for a constellations wedding.

Coins.

The cards were the second timeline priority. They were arguably the most important standalone piece because they could be interesting even if I failed to put together the fortune machine itself. They also had to be designed with sufficient time for printing, so I worked on them as soon as I had the hardware ordered.

Structurally, I wanted a deck with suits and ranks that hinted at broader connections between the cards, but with different proportions than a traditional Tarot deck. The card subjects should be grand, universally applicable concepts while also being specifically about Rob, Chris, and their relationship.

Several themes emerged. From the wedding setting: excitement and possibilities; the passage of time; communication and interpersonal relationships. From the specifics of Chris’s and Rob’s interests: research and learning/teaching, Pittsburgh, math, logic, and language. I re-discovered Mrs. Miniver’s Problem and it became a guiding theme for structural ideas I wanted to communicate: unbalanced triads, confluence, cycles, and liminality.

With some refinement help from Trevor and David, I settled into a 33-card deck:

Three triads of progression:
    mystery, spontaneity, exploration
    past, present, future
    nostalgia, contentment, anticipation

Three triads of overlapping/merging:
    limerence, romance, partnership
    allegheny, ohio, monongahela
    melody, music, rhythm

Three triads of cyclic processes or periodic growth:
    symbol, language, logic
    novice, pupil, expert
    conjecture, research, proof

Plus an additional “major arcana”:

    chris, &, rob

…bringing the total number of cards up to 30, a somewhat significant number for myself and Chris this year. (As a side note, the number of guests at the wedding was 81; since each guest received two cards and the major arcana was reserved for Chris & Rob, there were a satisfyingly exact six decks in play.)

The visual design of the cards matched the style that Rob and I put together for the invitations, programs, and other paper goods for the wedding: clean geometric stars, dashed lines, and text in Raleway in white on a full-bleed navy blue background.
Paper goods.
Photo by Rob
The design on the card backs (and later, on the front of the fortune machine) was inspired by the overlapping circle motif of Mrs. Miniver’s Problem, which coincidentally becomes the “wedding rings” quilt pattern when repeated.

The constellations on the cards were computationally generated, sort of. I wrote a quick Processing sketch to generate a deck with a quantity of randomly placed stars according to the suit and rank of each card, and then I chose the connection lines between the given stars to make a constellation that “felt correct” for the given concept. Rob helped with this stage, which was like stargazing in tiny, computer-generated skies. (The constellations were subject to one artistic constraint: cards in the first three triads had a linear graph structure, cards in the second three triads had branching, and cards in the third three had cycles.)

For ease of sending the files to the printer, I generated the entire deck as a multi-page pdf. Also, since Processing is mostly intended for RGB displays, I did have to convert my pdf to CMYK to make sure the deck would print with the right blue.

While I did price out the option of getting real card decks printed, I decided that at the low quantity and short timeline I required, it made more sense to just get them printed as individual cards — essentially full-bleed business cards with rounded corners. I had them printed by catprint.com, who had also handled the wedding invitations. I used their “plastic” stock, which ended up being thinner than I expected, but quite nice for cards. The cards seem to have held up well, without getting bent or picking up fingerprints.
Final cards, with a few coins.
Photo by Vincent Zeng

Once the cards were off to the printers, I had no excuse but to build the fortune telling machine itself, which I had started referring to as “the Object.”

The physical design had some pretty tight constraints. It needed to have an obvious place for the cards to go, such that the user’s hands were unlikely to be in the picture and there would be plenty of light on the cards, with the camera at an appropriate distance from the cards. It needed to incorporate the coin slot and the thermal printer, with a lockable compartment for the coins to land in. It needed to have instructions on it, and it would ideally be aesthetically pleasing and portable.

The camera position drove the design, and I ended up going with a design where the cards would fit neatly into slots so we could be pretty sure of their position. Some draped fabric would obscure the camera itself.

Everything was designed in Illustrator, since I no longer have AutoCAD, and the facings were cut on a laser cutter. I framed it up out of 1×2 lumber, using laser etched lines on the plywood to help me line up the framing.
Some of the vector cut lines.
Framing.
The back and sides of the Object were faced with 1/8” plywood, and the front was faced with 1/8” acrylic, with a stack of three sheets for the card slot. The arched top is barely load-bearing and was simply made out of chipboard.
Framing.
I decorated the front and added instructions with my favorite acrylic treatment, which is to laser etch the acrylic, rub paint into the grooves, and wipe off the extra paint with a damp rag.
Etched acrylic.
Photo by Vincent Zeng

I am not super happy with how the fabric draping turned out — I wanted to evoke theatre curtains, but I think it looks uncomfortably close to a turban (“uncomfortably” because fortune telling devices already often have gross, Orientalist overtones). I ended up ripping it out and replacing it at 3:00 AM the night before the wedding; it got a little better, but not as much as I would have liked. With more time, I would have bought fabric with a limper drape and more sheen.

I made an adjustable bracket to hold the camera (more laser cutting and a couple of small bolts), and I hot-glued it into position once I was convinced it was positioned well to see the cards.

I also threw some sponges in the coin drawer to keep the coins from thumping against the bottom.
Back of the Object.
Photo by Vincent Zeng

The coin slot and thermal printer basically worked out of the box. This youTube tutorial shows the quick process for training the coin slot on a particular coin. I also made a very minor hardware modification to the coin slot: I cut away a tiny piece of plastic that blocked a bit of the coin return slot and prevented my fairly large coins from being retrievable.

There were a couple of considerations for using the slot and printer with the Pi:

With those things in place, I used RasPi’s GPIO functionality to attach an interrupt to pulses from the coin slot. Since I was only recognizing a single kind of coin, there was only one kind of pulse event — a single pulse — to detect. The basic skeleton of the control code looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#!/usr/bin/env python2.7  
# useful info on hardware interrupts from: 
# http://RasPi.tv/how-to-use-interrupts-with-python-on-the-raspberry-pi-and-rpi-gpio-part-3 

from time import sleep
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BOARD)

import printer
p = printer.ThermalPrinter()

# Set coin slot's pin for input
GPIO.setup(32, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)

#[lots of stuff about retrieving fortunes and formatting text for the printer here]
      
# Here's what happens when we see a coin 
def vend(channel):
  try:
      hand = vision.getHand()
      hand.sort(key=lambda x: cardPositions[x.name])
      cardnames = map(lambda x: x.name, hand)
      print_fortune(",".join(cardnames))
  except:
      tb = traceback.format_exc()
      print tb
      print_fortune("error", None)


# Register an event:
# when a falling edge is detected on port 17, regardless of whatever   
# else is happening in the program, vend(channel) will be run  
GPIO.add_event_detect(32, GPIO.RISING, callback=vend, bouncetime=300)

# send a little job to the printer when we first start up
p.print_text("Hello.")
p.linefeed(5)

# Run around in a loop, waiting for an event
try:
  while True:
      sleep(1)
finally:
  GPIO.cleanup()

As I said above, David wrote the card image recognition. He initially considered using Tesseract’s optical character recognition, but as this was only somewhat accurate and quite slow on the Pi, he went with the following approach, in his own words:

“First, we find the cards and rectify them, using the methods outlined in http://arnab.org/blog/so-i-suck-24-automating-card-games-using-opencv-and-python.
Rectified card
Then, we locate the bright blobs:
Blobs detected
and we classify each as either a pointy star or a ring star. Finally, we match this list of star locations and classifications against our list of known cards.”

David also handled configuring the project as a systemd unit so that the code would run automatically when the Pi was powered on, and together we thought of a way of signalling shutdown to the machine via a secret series of cards.

Okay, all that remains is, oh, just the hardest part: what should the fortunes say?

My original idea was that the “fortunes” would be more like small stories, or fables. Two of Emily Short’s works were particularly inspiring: the Annals of Parrigues and the “tale”-generation from Counterfeit Monkey. I spun my wheels for a while looking at predecents. I read a fair number of wedding-related tales from the Multi-Lingual Folk Tale Database, most of which were predictably either horrifying or baffling. (I did like this one.) Before settling on my card deck, I had looked at Plotto with Rogelio and found it… not entirely appropriate for this wedding. After I had my card themes sorted, I also considered trying to use a planner-based approach, and I even considered doing so with Chris’s own system, Ceptre.

I also looked through Darius Kazemi’s delightful Corpora repo for inspiration. The proverbs document almost seemed like the right thing, but most of the actual content was entirely the wrong tone.

I had decided that I was going to hand-author content for the obviously cohesive card combinations, such as all three cards from a single suit, and I had quite a few notes for those. So I just needed “filler fortunes” for the less-interesting hands; I rationalized this with a hope that the somewhat boring nature of these would ideally lead players to seek out better combinations. From a code standpoint, the fortunes for all possible hands would be baked into a JSON file — at play time, the fortunes were entirely deterministic — and the fallback content would supply solid coverage that I could then overwrite with more interesting content as I found/generated it.

At this point, it was Thursday night before the Saturday wedding. (With my own dress still not finished…) And so I decided to try a plan that sounded like it just might work, and which Trevor courageously agreed to help implement. I had noticed that many of the “light” meanings in the Tarot interpretations corpus would work well as standalone fortunes, such as possible endings for sentences like “In the upcoming week, be sure to make time for…” or “Do not underestimate the importance of…” If we could just pick whichever Tarot meaning “made the most sense” for a given hand, and not worry too much about whether “the most sense” was really any sense at all, that would probably be good enough for filler. And there weren’t too many meanings to just annotate them by hand. And once they were annotated, a computer could easily sum up their score.

So I made a spreadsheet full of checkboxes, with the Tarot meanings as the rows and a column for each card. For each meaning, I checked off all of the cards that could be considered relevant. With 124 meanings (the “light” Tarot meanings, curated a bit for wedding-appropriateness) and 30 cards, this was tedious but not unbearably so.
Screenshot of spreadsheet with card rankings

Meanwhile, Trevor did some heroic coding to import the spreadsheet data, generate a list of all possible hands of cards, rank the appropriateness of each meaning to each hand, and assign a random highest-ranked meaning to each hand. After messing about with a few measures of appropriateness, we decided on: for a given meaning, add one point for each appropriate card that is in the current hand, and subtract a fraction of a point for each appropriate card that is not in the current hand. The idea was that the penalty would help more specific meanings get chosen instead of generic ones: for example, “taking satisfaction in knowing how your efforts will aid others” was marked with “contentment,” “anticipation,” and “partnership,” whereas “showing your love to others” was marked with those as well as “nostalgia,” “limerence,” “romance,” and “&.” Without the score penalty, both meanings would be marked as equally appropriate for the “contentment/anticipation/partnership” hand and the choice would be random between them. To refine our results, Trevor actually hacked up a stat tracker so we could see how well the fortunes were being spread around the possible hands. We ended up with a penalty of 1/10 point.

We were thrilled that the output was, well, surprisingly acceptable. Some were quite good! Of course, as with any well-crafted fortunes, the vagueness and positivity mask the possible inappropriateness. Here’s a sample from the beginning of the output file:

'romance,proof,rob': '"Sharing your knowledge"', 'present,allegheny,rhythm': '"Preparing for a party"', 'allegheny,monongahela,proof': '"Doing what you set out to do"', 'partnership,ohio,expert': '"Seeing the value of another person's approach"', 'future,nostalgia,logic': '"Moving forward with confidence and authority"', 'present,ohio,expert': '"Putting a plan into motion"', 'exploration,contentment,expert': '"Knowing and being honest about your own limits"', 'symbol,research,rob': '"Pursuing a course of study"', 'spontaneity,symbol,novice': '"Using your imagination"', 'past,nostalgia,anticipation': '"Allowing events to unfold"', 'past,expert,rob': '"Stepping back to gain perspective"', 'present,nostalgia,contentment': '"Embracing the aid that comes your way"', 'anticipation,romance,allegheny': '"Seeing events in the best possible light"', 'anticipation,music,expert': '"Appreciating the luxuries that life has to offer"', 'allegheny,ohio,language': '"Directing the flow of work"', 'exploration,past,novice': '"Putting old things together in new and exciting ways"', 'mystery,past,ohio': '"Bringing opposites together"', 'future,nostalgia,melody': '"Giving in to the need for rest and renewal"', 'allegheny,ohio,melody': '"Breaking through barriers"', 'pupil,expert,&': '"Nurturing yourself and others"', 'mystery,logic,pupil': '"Asking for assistance"', 'past,future,contentment': '"Using the old to make something new"'

I also added some “tiny star field”-inspired headers and footers to each fortune to add style and make it clearer when the fortune was done printing. (The thermal printer only supports ASCII, which somewhat limits the star options.)

1
2
3
4
5
6
7
8
9
10
11
possibleStars = ["*", "*", "*", "*", "*", "*", ".",".",".",".", ":", "~", ",", "`", "'", "-"]
def starfield():
  stars = ""
  for i in range(4):
      for j in range(40):
          if (random.randint(0,20) <=1):
              stars = stars + random.sample(possibleStars, 1)[0]
          else:
              stars = stars + " "
      stars = stars + "n"
  return stars

In the last four hours before I delivered the Object to the venue, I hurriedly pulled together fortune content from ideas and notes I’d accumulated over the previous two months, such as historical quotes about Pittsburgh, Oblique Strategies, etc. (I don’t want to give all the content away in this post, of course.) David helped with finalizing the quotes for the full suits, including this Hofstadter quote we used for the symbol/language/logic triad:

“People enjoy inventing slogans which violate basic arithmetic but which illustrate “deeper” truths, such as “1 and 1 make 1” (for lovers), or “1 plus 1 plus 1 equals 1” (the Trinity). You can easily pick holes in those slogans, showing why, for instance, using the plus-sign is inappropriate in both cases. But such cases proliferate. Two raindrops running down a window-pane merge; does one plus one make one? A cloud breaks up into two clouds — more evidence of the same? It is not at all easy to draw a sharp line between cases where what is happening could be called “addition”, and where some other word is wanted. If you think about the question, you will probably come up with some criterion involving separation of the objects in space, and making sure each one is clearly distinguishable from all the others. But then how could one count ideas? Or the number of gases comprising the atmosphere? Somewhere, if you try to look it up, you can probably find a statement such as, “There are 17 languages in India, and 462 dialects.” There is something strange about the precise statements like that, when the concepts “language” and “dialect” are themselves fuzzy.”

We declared it done enough and threw everything into boxes to take to the venue.
The Object.
Photo by Vincent Zeng

Chris and Rob got married, and it was the best.

With respect to the fortune teller project, the evening progressed approximately as I hoped it would: I seeded some knowledge that the coins and cards on the table were intended for use with the mysterious Object on the back table, and folks started retrieving fortunes soon after the beginning of the reception.
The Object, in action.Photo by Erica Dilcer

I also told a few people that “you make your own fortune,” in an attempt to get players to seek out meaningful combinations. A hand-authored fortune was stumbled upon fairly quickly, which spurred speculation about how to get better fortunes. (The particular discovered fortune was one of the longer ones — a recipe for pound cake — which had the interesting effect that people could tell from across the room that something interesting was being printed.)
More guests interacting with the Object.
Photo by Erica Dilcer

Sometime during dinner, two independent groups discovered the method of counting the stars to determine a card’s suit and rank. By midway through the reception, there was an effort underway to assemble a full deck. After the deck was assembled, I was curious to see if my next expected step would occur, but I was too interested in it happening to leave it to chance, so I hinted around that I thought it would be fabulous if someone invented a card game to play with the cards. Several friends rose to the challenge and invented a game they called Constellermo. As far as I could tell, gameplay largely hinged on shouting “Constellermo!”

Friends examining cards at a table.
Photo by Erica Dilcer

Over the course of the evening, several friends remarked that they were pleased with the initial random hand of cards that they’d been dealt.

Predictably, I didn’t end up with any production time to work on coin earning/finding minigames. I did have a helper hide some coins at the beginning of the reception, and seeking those out provided entertainment for the 5-year-old in attendance. After that, though, I just went around periodically and handed out coins to anyone who wanted them. So it may have been better to start people off with more coins so they could be a bit more adventurous in their explorations.

The Object did get a little flakier as the lighting grew dimmer in the venue — the lamp we’d brought had some glare issues — but it never became totally unusable. There were also a few issues with paper jams. These seemed to mostly be caused by the difficulty of tearing the paper cleanly, so they could possibly be avoided by using a printer with its own cutting mechanism. The small rolls of thermal paper lasted longer than I was expecting, and we only went through two or three rolls over the course of the evening.

Overall: this was a fun project that I’m pretty satisfied with. It was ambitiously, but not impossibly, scoped for my own skill set and the allotted time, and it worked basically as planned at the event itself.

But really: Chris and Rob got married, and it was the best.
Chris and Rob.
Photo by Erica Dilcer

Original Source