Generating a Book Bingo Chart in Hugo

Another r/Fantasy 2021 Book Bingo post! How in the world am I generating this (updating) chart in Hugo?

2021 Book Bingo

Attack on Titan, Vol. 1

by Hajime Isayama

Hard Mode ✓

5 SFF Short Stories (Hard: An entire anthology or collection)

The Poppy War

by R.F. Kuang

Hard Mode ✓

Set in Asia (Hard: by an Asian author)

The Changeling

by Victor LaValle

Hard Mode ✓

r/Fantasy A to Z Genre Guide (Hard: by a BIPOC author)

The House in the Cerulean Sea

by T.J. Klune

Hard Mode ✓

Found Family (Hard: featuring an LGBTQ+ character)

The Scorpio Races

by Maggie Stiefvater

Hard Mode ✓

First person POV (Hard: Multiple)

The Wyrmling Horde

by David Farland

r/Fantasy Book Club (Hard: with participation)

Replaced with: Sequel: Not the First Book in the Series (2017)

The Borrowers Afield

by Mary Norton

Hard Mode ✓

New to you author (Hard: haven’t heard much about)

Mexican Gothic

by Silvia Moreno-Garcia

Hard Mode ✓

Gothic Fantasy (Hard: not in the Book Riot article)

Transmetropolitan, Vol. 1: Back on the Street

by Warren Ellis

Hard Mode ✓

Backlist book (Hard: published before 2000)

Red Sister

by Mark Lawrence

Hard Mode ✓

Revenge-seeking character (Hard: revenge as the major book plot)

Six Wakes

by Mur Lafferty

Hard Mode ✓

Mystery plot (Hard: not primary world urban fantasy)

Wild Sign

by Patricia Briggs

Hard Mode ✓

Comfort read (Hard: that isn’t a reread)

Tales of Nezura: Book 1: The Zevolra

by Randall Cooper

Hard Mode ✓

Debut novel (Hard: published in 2021)

Hellblazer, Vol. 1: Original Sins

by Jamie Delano

Hard Mode ✓

Cat squasher (500+ pages; Hard: 800+ pages)

Daemon Voices

by Philip Pullman

Hard Mode ✓

SFF-related nonfiction (Hard: published in the last 5 years)

Cece Rios and the Desert of Souls

by Kaela Rivera

Hard Mode ✓

Latinx or Latin American author (Hard: with fewer than 1000 Goodreads ratings)

Black Rain and Paper Cranes

by R.S. Craig

Hard Mode ✓

Self published (Hard: with fewer than 50 Goodreads ratings)


by Jeff VanderMeer

Hard Mode ✓

Forest setting (Hard: for the entire book)

Gideon the Ninth

by Tamsyn Muir

Hard Mode ✓

Genre mashup (Hard: of three or more genres)

The Midnight Library

by Matt Haig

Hard Mode ✓

Has chapter titles of more than one word (Hard: for every chapter)

An Alchemy of Masques and Mirrors

by Curtis Craddock

Hard Mode ✓

___ of ___ (Hard: and ___)

Project Hail Mary

by Andy Weir

Hard Mode ✓

First contact (Hard: that doesn’t lead to war)

Black Sun

by Rebecca Roanhorse

Trans or Nonbinary (Hard: protagonist)

The Long Way to a Small, Angry Planet

by Becky Chambers

Hard Mode ✓

Debut author (Hard: with an AMA)

A Great and Terrible Beauty

by Libba Bray

Hard Mode ✓

Witches (Hard: as the main protagonist)

To start, I have a Data file for the names of the categories:

  - "SFF anthology or collection"
  - "Set in Asia (Hard: by an Asian author)"
  - "r/Fantasy Book Club (Hard: with participation)"

It’s just markdown (for links) in a nested list. 5x5 (hard coded) at the moment, but I could probably make that flexible if I wanted.

Next, I have to add a bit of metadata to each post that’s going to be included in a Book Bingo:

- 2021 Book Bingo
    2021 Book Bingo: [3x1+]

It’s a bit annoying that I have to do this. What I’d really want is for the Taxonomy to support a hash as the data field, so I could just do:

    2021 Book Bingo: [3x1+]

But if you do that, the post isn’t included in the bingo taxonomy. So I have to include both. If I figure out a better way to do that, I’ll update this post, but for the moment, it’s okay. So bingo is a Taxonomy (so I can list all pages that match it without cycling through all pages), while bingo-data is just a $Page.Param that I can access. The field is: {row}x{column}{hard mode flag}.

Finally, I actually make a shortcode that can use that information and build a table:

{{- $title := .Get 0 -}}
{{- $slug := $title | urlize }}
{{- $data := index $ $title -}}

<h1>{{ $title }}</h1>

<table class="bingo">
    {{- range $row := seq 0 4 -}}
        {{- range $col := seq 0 4 -}}
            {{- $index := (printf "%dx%d" (add 1 $row) (add 1 $col) )}}
            {{- $indexHard := (printf "%dx%d+" (add 1 $row) (add 1 $col) )}}
            {{- $title := index $data $row $col }}

            <p>{{ $title | markdownify }}</p>
            {{- range $page := index $.Site.Taxonomies "bingo" $slug -}}
                {{- $squares := index $page.Params "bingo-data" (replace $slug "-" " ") -}}
                {{- if (or (in $squares $index) (in $squares $indexHard)) -}}
                    <a href="{{ $page.Permalink }}">
                            {{ safeHTML (index (findRE "(?m:<img.*?>)" .Content 1) 0) }}
                            <figcaption>{{ .Title }}</figcaption>
                    {{- if in $squares $indexHard -}}
                    <p>(Hard Mode)</p>
                    {{- end -}}
                {{- end -}}
            {{- end -}}
        {{- end -}}
    {{- end -}}
  • range over the rows and columns, building an HTML table
  • In each cell:
    • Include the title of the cell from the data page
    • Build a index (and indexHard) for that row and column
    • Search over all pages in the matching Taxonomy, use the Page Data to see if they match this specific index or not
    • If so, include the cover as a link, plus the ‘(Hard Mode)’ flag

I’ll admit, it feels a bit hacky, but one of the beautiful things about a static site generator is that it only has to run when rebuilding my site. The end result is just HTML. And I only have to update the individual pages, not keep a central list. I think that’s pretty neat.

Perhaps I should figure out more things I can do with Page Data. Perhaps keep track of authors as well as series? Ratings? Who knows!