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)

Annihilation

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:

bingo:
- 2021 Book Bingo
bingo-data:
    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:

bingo:
    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 $.Site.Data.bingo $title -}}

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

<table class="bingo">
    {{- range $row := seq 0 4 -}}
    <tr>
        {{- range $col := seq 0 4 -}}
        <td>
            {{- $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 }}">
                        <figure>
                            {{ safeHTML (index (findRE "(?m:<img.*?>)" .Content 1) 0) }}
                            <figcaption>{{ .Title }}</figcaption>
                        </figure> 
                    </a>
                    {{- if in $squares $indexHard -}}
                    <p>(Hard Mode)</p>
                    {{- end -}}
                {{- end -}}
            {{- end -}}
        </td>
        {{- end -}}
    </tr>
    {{- end -}}
</table>
  • 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!