Wed 25 June 2025

Procedurally Generating a Tic-Tac-Toe Zine with Python

Posted by Al Sweigart in misc   

At PyCon 2025, I handed out a pocket-sized zine that lets you play a procedurally generated choose-your-own-adventure version of tic-tac-toe. The zine itself is available as a PDF for viewing on your computer and a PDF for double-sided printing. Here's how I made it using Python.

What are Zines?

I never really liked zines.

Zines (pronounced "zeens", short for "magazines") are small, self-published print booklets made by individuals or small groups, often about indie or non-mainstream topics. In the 80s and 90s when photocopying was accessible but the internet was not, so zines made sense as a way to do custom small-scale publishing. But once social media and smartphones took off, I thought of zines as an anachronism at best. Why not just have a website?

In tech, Julia Evans' Wizard Zines do a great job of explaining various technical subjects. But I kept thinking, "Couldn't this just be a website?" What changed my mind and made me see the value in zines was:

  1. Zines can be a physical gift or souvenir that don't take up much space.
  2. A zine will persist to be picked up later, unlike a forgotten browser tab or bookmark.
  3. There are so many possibilities for zines with the advent of laser printers, QR codes, and procedural-generated content.
  4. They're a fun, crafty little thing you can make for cheap.

In the future I'll write a blog post about designing zines in the 2020s, but for now I'd like to show you how I made this zine.

Choose Your Own Adventure Books

If you're like me and... of a certain age... you may remember Choose Your Own Adventure books. These books would tell a story, then prompt the reader to choose what happens next based on what page they turn to. "If you want to explore the old cave, turn to page 37. If you want to go back to the motel, turn to page 45."

You could think of a tic-tac-toe game as a Choose Your Own Adventure book with 362,880 (or 9 x 8 x 7 x 6 x 5 x 4 x 3 x 2 x 1) pages. You start on page 1 with a blank tic-tac-toe board and there are nine possible places the first player can move. Each of those nine boards in turn have eight possible moves for the second player to go to.

362,880 is a lot, but much smaller than other games like chess. And the true number is much smaller: some boards are duplicates, games can end before the entire board is filled, and the human reader's move and the zine's responding move can be collapsed into one board. And you can fit multiple tables on each page.

My zine was going to be folded such that each zine page was one-eighth of a US letter-sized page. (This is perfect for fitting into a pants pocket.) Printing duplex (i.e., on both sides of the sheet), I could get 32 zine pages for two sheets of paper. Experimenting with my Brother HL-l2460DW laser printer, I could fit 24 tic-tac-toe tables per zine page and still have it mostly readable. (Your phone's camera can be a magnifying lens if the tables are too small.)

My zines could have 768 tic-tac-toe boards, but I'd want to use some of the pages for the front & back cover, instructions, and the centerfold. Really, I would have 25 pages available for 600 (24 x 25) tic-tac-toe boards.

I wrote Python code to create the tic-tac-toe boards and then do the layout for the PDFs.

Procedurally Generated Tic-Tac-Toe

The code that generates the zine is make_ttt_zine.py in my tictactoe_zine repo.

I've created simple tic-tac-toe programs for The Big Book of Small Python Projects and Invent Your Own Computer Games with Python before. I also used tic-tac-toe as a way to explain object-oriented programming in Beyond the Basic Stuff with Python.

The make_ttt_zine.py first creates the data structures for the tic-tac-toe boards (Full code in the git repo):

The program creates a list in boards with a single blank starting board. Then for each board in the list, the main part of the program adds additional boards for each possible reader move followed by the zine's move. These new boards are appended to the end of the list. The index of the new board is added to the previous board that leads to it. Eventually, no new boards are appended as they are all finished with either a win, loss, or tie.

In the get_zine_move() function that picks the zine's move, the ZINE_BLUNDER_RATE value causes the zine to sometimes make a random move instead of the best move. Perfection is boring; it's more amusing for the zine to sometimes make mistakes.

Next the boards are shuffled around randomly, updating their indexes to stay consistent. Then, duplicate boards are removed (Full code in the git repo):

For random seed 44, these steps reduced the number of boards from 1,062 to 580. This fits within the number of pages I wanted for the zine.

Finally, we need code that generates PNG images of each zine page, which then fits them into one US letter-sized image for each of the four printable pages. These are then put into a single PDF. We also create a PDF that is viewable on a computer instead of meant to be printed. (Full code in the git repo)

Zine Design and Layout

There was a lot of trial and error involved when designing the layout of the zine. I settled on two double-sided sheets, with eight zine pages per sheet side. This would make the zine a total of 32 pages (including the front and back cover.)

I also chose to spend four zine pages for a centerfold that acts as an advertisement for the zine's git repo. This requires taking care to not cut the four zine pages of the centerfold or accidentally staple them down.

In the future, I plan on making a Python app for creating these PDFs based on the number of pages and zine page size.

Full details are available from in the git repo's README.

I forgot to bring business cards with me to PyCon, but the zines turned out to be a great substitute since they had my website and contact info on them. I ended up giving away all 150 of them by the third day of the conference. People liked them; they made for a nice souvenir that was different from the usual sticker or flyer (the centerfold was a neat kick, especially).

This was a great project that ended up taking about four days. It's given me a new interest in making zines and software tools to help others make zines. Keep an eye out for a future blog post on the topic.


Check out other books by Al Sweigart, free online or available for purchase:

...and other books as well! Or register for the online video course. You can also donate to or support the author directly.