Adding Vocab-style Puzzles to GNOME Crosswords

Laureen’s Blog

  • GSoC 2026 Final Report – Laureen Caliman

    Over the summer of 2026, I worked towards bringing the option of playing Vocab-Style puzzles to GNOME Crosswords as part of Google Summer of Code. This entailed adding support to the puzzle library, and writing the backend of the algorithm responsible for grid generation. Jonathan Blandford provides a thorough rundown on the ins and outs of Crosswords with these slides.

    The first few weeks of the start of GSoC were spent by adding a drop-down calendar widget to Crosswords Editor, and storing GDate data as ISO8601. We decided to integrate this into my design despite it not being directly related to the proposed project because it still contributed to the Crosswords app.

    For the new puzzle type, I started with adding support to the puzzle library, libipuz, which is responsible for formatting and representing puzzles styled as ‘paper-and-pencil’ crosswords. My primary mentor and I bounced ideas back and forth for some time before we started designing and writing the algorithm. We landed on an idea and I created an initial design document for the plan of action to follow for the summer. We decided that aiming for both the backend and frontend in one summer may be more work than we initially thought, so we concluded on focusing exclusively on getting a working algorithm to build from.

    The bulk of this summer consisted of writing and reconfiguring the depth-first search backtracking algorithm.

     

    GitLab Links to Code:

    An overview of my GitLab profile can be found here.

    • Implementation of New Vocab Puzzles
      • This is the bulk of the algorithm and unit tests. The user inputs a word, the word gets analyzed in a recursive function to check for crossings and constraints, we save the state of the board, backtrack if needed, and present a grid.
    • Vocab Ipuz
      • Adding new class for IpuzVocab to support vocab puzzles in the puzzle library (libipuz).
    • Check for Island Words
      • There may be a word in a list that may not share any intersection points with any others no matter how many backtracks are done. Consequently, this affects grid creation in a timely manner and may prevent grid generation at all by stating it false. We can compare the bitmask of words in the list before we even activate the algorithm to detect a word that would potentially conflict with the others.
    • Date Validation
      • Previously, the Crosswords Editor had a free-for-all AdwEntryRow. However its purpose is to present a legible date to the user, and store the date in ISO8601 format. I used GDateTime, GDate, and a Gtk Calendar widget to add a drop-down calendar in the date box. The chosen date presented in Gregorian style to the user, and stored as ISO8601 to the backend.
    • Added Dispose to Shapebg
      • Releases references to GObjects that Shapebg owns and frees Shapebg’s remaining memory.

     

    Design Docs:

     

    Blog Post Links (Most Recent -> Oldest):

     

    I also gave a lightning talk at the 2026 GNOME Users and Developers European Conference here. Thank you to the GNOME Travel Committee for making that opportunity possible.

    I still have some work to do for both Libipuz and Crosswords: finish the island-checking function detailed above, open a new MR to choose the most compact/square grid out of 500 options and present that to the user directly rather than them cherry-pick through a large selection,  create a new MR to add photos of the puzzle in libipuz using gi-docgen, convert the 500-generated grid code to a PuzzleTask, and incorporate the frontend to make this a fully-functioning part of the game.

    I would like to thank my primary mentor, Jonathan Blandford, and my other mentor, Federico Mena Quintero, for their guidance, feedback, patience, and teaching. This program was exactly what I needed to become better at development and serves as my rock to open-source contributions. I learned a lot of valuable skills such as document reading, how much to push in a commit, how to slow down trying to get a lot done at once, but simultaneously how to speed up my progression on the parts that actually matter, code with brevity, and a whole lot of dealing with nasty version control!

    I intend to continue contributing to GNOME Crosswords as well as the overall GNOME Foundation. I look forward to collaborating with more people involved in the Foundation!

     

  • Vocab-style Crosswords Update | Final Stretch

    The timeline for Google Summer of Code is coming to an end, and us interns are piecing together the final touches to our projects for submission. Thanks to the help of my mentors, and the duck sitting on my monitor, the algorithm that beats the heart of Vocab Crosswords in GNOME Crosswords has been tremendous strides in accuracy and testability. The primary focus shifted to getting the algorithm landed by the end of the summer, and working on the frontend of the application post-GSoC.

    Unit Tests

    At GUADEC, with the help of Federico, I created unit tests to see how my functions reacted in a given circumstance. Jonathan and I worked on creating different circumstances for the run and helper functions.

    image.png

    Optimization

    For user optimization, we don’t want to keep the board at a strict 30×30 grid and only allow for the first viable option. We decided to incorporate a new function to trim the dimensions of the generated grids based on the outermost edges of the letters, create a new board based on the newly calculated dimensions, trim that board down respectively, and copy the words over in the exact respective format. This is due to the libipuz grid’s origin point (0, 0) being fixed at the uppermost left corner cell. All in all, the trimming function essentially does this:

    Additionally, it is pretty ideal to have some leeway of choice on how you want your puzzle to look. Some puzzles might generate lanky, while others extensively branched out all the way to the maximum borders, and the rest perhaps condensed together. The ability to rearrange the ordering of the words is already a feature in Crosswords thanks to PuzzleTask. But, it is for known grids of typically 15×15 sandwiched together. What is different with the vocab puzzle is that the rearrangement must still respect the same constraints of intersecting at a single letter nodal point, words cannot be on top of nor right next to each other (edge of nodes must respect space), and no islands (all words must share at least one node with another word). For instance, grids A and B here pertain the same words 1 through 6, but these words can connect differently on the graph, producing two options to choose from.

    Tested, I achieved these 3 different versions of grids based off the same word bank:

    image.png      image.png      image.png

    Island Checking

    The final component I will implement within GSoC’s timeline is checking for islanding words. Say a user provides a list of words and one word absolutely cannot intersect with any other word, it shares no node. The backtracking algorithm will spend a lot of time trying to place it, or invalidate any graph generation at all. We want to check beforehand if a word would not belong along the rest, and warn the user about it. Because there are many alphabets that exist, we are going to analyze the sets of characters as guint64 bitsets and GHashTable. Every unique character gets its own bit-slot, and every word’s 64-bit mask is compared to available words using bit operations.

  • Intern Experience at GUADEC

    I would first like to mention and thank everyone for being so welcoming and open-minded to the interns and for the non-interns who also attended GUADEC for the first time. I hope to become a familiar face to everyone.

       

    The GNOME Foundation clearly goes out of their way to gather as many people as can attend to update the platform on updates and projects occurring within the ecosystem. This conference was hosted at the Facultade de Informática at the Universidade da Coruña, in A Coruña, Spain. Both me, and my fiancé, Michael Calabrese, received the GNOME travel grant, which chiefly covered our expenses of travel and accommodations.

       

    Conference

    All of the talks were informative and interesting to attend, however a couple that stood out to me were:

    One Daemon to Rule Them All: Cross-Desktop Credential Management with OO7

    OO7 is a Rust implementation of the Freedesktop Secret Service. It aims to replace the older GNOME Keyring and KWallet, with an actively maintained secret storage service that uses a unified API to precisely select credentials. The current keyring and wallet method stores all one’s information is stored in one single place, making it limiting when attempting to switch between different desktop environments. Instead, OO7 uses libsecret to encrypt individual pieces of information rather than an entire keyring. This allows specific credentials to be unlocked, while others stay locked.

    State of GNOME OS

    This talk highlighted recent improvements made to GNOME Operating System, particularly over the image-based development/system. Instead of updating packages one by one, you can get what was built and tested in a single update. One interesting thing about GNOME OS is its migration to mobile phones. This talk covered expanding hardware support and improving the testing in order to strengthen the migration and support to a span of devices.

    Intern Lightning Talks

    I had a talk myself too. Interns were given the opportunity to talk about their project and progress at that point in time. The intern lightning talk can be found here.

    GSOC

    Felipe Borges mentioned during an outreach and internship meeting that the intended purpose of Outreachy and Google Summer of Code (interning at GNOME in general) is to not only improve your current skills in programming, but to train you to become a maintainer for that repository your project is correlated with.

    I met my other mentor, Federico Mena Quintero, in person at the conference. Quintero welcomed me very openly, and offered to help me review my code and offer his perspective towards improving and the constraints of the vocab-crossword project. Quintero guided me towards writing unit tests for the existing backend functions that I wrote. All are welcome to look at what the progress on my project looks like, and review or comments are welcome towards any detail. This is the Gitlab MR that hosts the current backend.

    Outside of the Conference

    It is no doubt that the chosen city was wonderful. There was delicious seafood, traditional Spanish and Galician food, beautiful architecture, beaches, and lovely weather. The coordinators of GUADEC planned a dinner event that all attendees were welcome to. I went and had a great time listening to the stories and projects of members I hadn’t yet personally met. Members also individually coordinated get-togethers on the GUADEC Matrix channel, allowing for multiple options of gathering together at social events based on personal taste.

    Of course, the Spain vs. Argentina game was playing on one of the days of the conference, so I had to go! Like most European city centers, A Coruña set up a large screen in their main plaza to host a public viewing of the game. It was a very unique experience to chant with Spaniards, watch people pop champagne in excitement or groan loudly when distressed, and feel surrounded by the local culture.

       

    I would like to thank the GNOME Foundation for this overall experience and for giving me a shot as an intern, and Google Summer of Code for offering thorough and exceptional mentorship in open-source.

    More photos about our experience is linked to this Flikr

  • Update on Crosswords Backtracking Algorithm

    I am implementing a new type of crossword puzzle in GNOME Crosswords this summer. The current options are static crosswords of ‘known’ location. My project does the opposite, where it takes the words and places them wherever we can get the maximum amount of connections between the words. The pinnacle of this is a DFS backtracking algorithm because we want the words on the grid to be malleable in their placements in order to include the next word going down the list.

    Previously, what I had done was attempt to erase the word letter-by-letter recursively writing NULL to each cell. However, this removed every element in the string, including the letter shared at a node between two words, leaving a gap in the word left in place.

    My most current version instead focuses on state preservation. Before we even write a new word to the grid, we read the existing state of cells with focus on those connections. Now when the recursive function attempts to place a word that ends up being impossible to connect with the current setup, we look for those ‘?’ characters, erase the string, and rewrite the cushioned letter to leave the other word fully intact.

    Imagine a board with CAT written across the center, and we want to place MACAW on the grid vertically. Before the algorithm writes MACAW, it inspects the board at the calculated intersection point(s) and reviews the cells of the string length.

    Cell 1: Empty, Cell 2: Empty, Cell 3: C, Cell 4: Empty, Cell 5: Empty.

    The board saves this state in memory using a ‘?’ in place of the empty cells as ‘? ? C ? ?’. Hypothetically this makes our board look temporarily like this in memory:

    ?

    ?

    C A T

    ?

    ?

    MACAW is written to the grid and then checked in the next recursive function call to ensure if it can be kept or not in that hypothetical place. If it runs successfully, we leave it as is:

    M

    A

    C  A  T

    A

    W

    If it returns false, we need to backtrack and erase MACAW. Rather than totally erasing the word like before, we send the ? ? C ? ? state back to our overlay function – which is responsible for writing to the grid. If it sees a ‘?’, it empties the cell. If it sees a letter, it rewrites it. That way we are only backtracking and erasing the word creating an obstacle in our program. MACAW is erased, CAT remains there for the next word to be attempted.

  • Pick a word, any word

    Over the past few weeks, I have been writing the backend for the vocabulary-puzzle generator for Crosswords.

    But what entirely dictates a valid word placement whilst being mindful of all edge cases and managing the state of the puzzle upon the exploration of possible solutions?

    Finding valid intersections

    For each word in the list, the vocab generator searches for every valid location where the word at the current depth could be added (or not added) to the current puzzle. A candidate must satisfy several constraints:

    • Word must intersect existing puzzle.
    • Intersecting characters must match.
    • Existing letters must not be overwritten by conflicting letters.
    • The word is within a 30×30 board boundary.
    • Puzzle remains entirely connected.
    • Word placements cannot connect flush to their head/tail to another word.
    • Could there be multiple valid intersections?

    As more words are added, the number of possible intersections rapidly grow. A single placement may produce several valid connections, each of which must be evaluated independently.

    Modeling the intersections

    The vocab grid generator represents candidate placements by using the coordinates of the intersection, placement direction (across, down), and the offset of the intersecting character within the word. As a whole, an intersection can transform the entire puzzle. For instance, “APPLE” and “PEAR” are just two words, but share four possible intersections. Rather than selecting one immediately, we ultimately want to record every valid candidate.

    Each of these shared candidates then become a potential node in our recursive search tree. As our puzzle grows in size, the problem becomes less about placing words and more about exploring all valid intersections whilst preserving the integrity of the puzzle state as we add and rearrange words.

  • Extending Libipuz

    From white-boarding my ideas on a Google Doc, to writing a formal design document in Crosswords, my ability to communicate technical ideas clearly is being put to the test.

    Writing documentation is critical to guide others’ understanding of the code and choices made on a particular codebase. Especially when several developers are introduced to the system, a way to reference material leads to more preparedness to contribute to the codebase.

    I wrote a design document introducing the concepts I would like to implement towards creating a way to generate a dynamic grid. Critique is welcome.

    Standard libipuz crosswords currently rely on using an existing dictionary to fill a static box of X length x Y width. However, the implementation of vocab puzzles goes against this logic and instead generates a new grid of N length x M width based on a list of 0 <= W <= 30 words of 1 <= L <= 25 characters long.

    I reconsidered the idea of using a GArray to store unplaced words because I want something idempotent. To avoid unwanted time complexity bloat, the backend should not carry the memory of unplaced words. Instead, the frontend will compare the generated grid against the original list to manage words that couldn’t be placed.

    Integrating this new feature will be a fascinating technical challenge.

    I created a new IpuzVocab class which inherits from IpuzCrossword. I learned how GNOME manages its developer documentation by writing a file myself to introduce this class. Writing this document made me think about the whole picture: how vocab puzzles handle grids, clues, and guesses, comparing it to standard crossword puzzles. I wrote the support to display a vocab puzzle in light and dark mode, with my next goal to integrate them via gi-docgen.

  • My Current MR and GSoC Project Start

    Ongoing Work

    Back in March, I started to tackle this MR for GNOME Crosswords. It allowed me to learn a lot about navigating the codebase, adhere to naming conventions, and collaborating with others involved in Crosswords.

    Crosswords Editor features a section for users to input metadata, such as author name, puzzle URL, and date. Originally, the MR was to convert and store the user’s manually typed date to ISO8601. This means storing the entered date in the form YYYY-MM-DD, while the user just needs to type in a date using the GDate struct.

    Upon code review, the scope expanded to move from manual typing to selecting a date on a drop-down GTK calendar widget. I was able to implement this concept by following the architecture of an existing widget (edit-shapebg.c/h/blp) which placed shapes on the crossword puzzle. Then I connected the new calendar widget to the metadata so the chosen date from the now drop-down calendar will be stored in ISO8601 format yet display respective to the locale of the person using g_date_strftime.

    Project Start

    With the start of GSoC, I have spent a lot of time this week reading, white-boarding, and began setting up some structure to adding vocab-style crosswords to the libipuz library.

    The basic constraints and workflow that I mapped are:

    • A user can input up to 30 words (strings), with a 25 character capacity.
    • Upon hitting enter or a button to generate, a Depth-First Search (DFS) algorithm starts to piece the words together on a blank grid. The algorithm can backtrack to tear apart words and rearrange them until a valid graph is formed
      • I’m going with DFS at the moment to prioritize the longest-to-shortest word seeding the graph in order to increase possibilities of connections
    • Every time the user generates the graph, it will be treated like its own isolated creation, recalculating the layout from scratch to allow for cleaner undo/redo states and easier addition/deletion of words
    • I don’t want words being placed right next to each other unless it was intentional for a 2+ word answer, which each word would be separated by thick lines
      • Words should only connect through intersections
        • The frontend does not mutate the grid but will be able to pass actions to the backend and return a new state
        • For instance: say a user starts off their grid by typing “CAT” and then “DOG.” These words don’t have common characters, so they cannot be connected. However, instead of erasing the user’s desire to incorporate “DOG,” the string gets tucked away and saved in a GArray

        • Now our user types and enters “CADET.” The UI takes our list and passes it to the algorithm to start chewing on and deal onto the board anew. This time, allowing for “CAT,” “DOG,” and “CADET” to appear, without the user having to retype “DOG”

    I went back and forth with myself on how to represent the puzzle’s state in memory and disk. To write a custom GObject, or not.

    The Argument Against: Standard IpuzCrossword objects have a handle on grids and JSON saving, and one could write a new function that takes an array of strings and return a standard IpuzCrossword.

    The Argument For: Revisiting the “CAT,” “DOG,” and “CADET” conundrum; say an educator is creating a puzzle for his/her/their class, and they have just one word out of 20 that don’t connect to current existing possibilities of intersecting words. If they save their puzzle, it transforms to a standard IpuzCrossword, and the floating word are permanently abandoned.

    At the current moment, I want the puzzle to be its own vocab-style workspace. Therefore this would allow for the floating word to instead sit in the sidebar and wait for further instruction for a future inserted word that works, or to get deleted.

    Of course, as I gain more insight about the library, states, and optimization within the codebase, this approach is subject to refinement.

  • Introduction Post

    May 13th, 2026

    My name is Laureen Caliman, and I am a contributor for GNOME Crosswords with Google Summer of Code 2026. Crosswords are a stimulating challenge that promotes engagement in education. Computing systems are now an indelible factor of daily life, which has raised concerns on maintaining attention, long-term memory, and reinforcement of knowledge in K-12. One study was performed in Indonesia to teach students English using crosswords. A measurable improvement in the students’ coherence to the foreign language material was seen, demonstrating benefit to their education.

    Unlike traditional crossword grids which are rigidly defined, vocab-style puzzles are fitted using algorithms to shape them together. Users will have the option to edit their crossword in the Editor, and open it in Crosswords. My project entails adding the proper backend and frontend support to create vocab-style crossword puzzles.

    My mentors are:

    • Jonathan Blandford
    • Federico Mena Quintero

    About Me:

    I am an incoming Computer Engineering student at Rose-Hulman Institute of Technology. I also teach K-12 computer science, so I really relate to this project’s mission about bridging software with education.

    I am eager to contribute to this community and learn more about the inner workings of GNOME. I intend to continue contributing to GNOME for a long time after this project finishes!

    GNOME Crosswords:

    Jonathan Blandford presented an in-depth overview of the history of GNOME at GUADEC 2017, which is available to watch here. Blandford also presented Crosswords to the 2024 GUADEC here.

    Crosswords consists of two domains:

    • The Crosswords Editor, which is a tool to create and edit crossword puzzles.
    • The Crosswords Game, which is where the user plays crossword puzzles.

    My project will see to both: the editor for a user to create the puzzle, and the game platform to play it. They will also have the option to print their game in black and white using Cairo Graphics.

    Technical Challenge:

    Traditional crosswords have a stable grid ready for play; the vocab-style crossword will be weaving words together in the most logical way upon a blank canvas. My project focuses on building the algorithm which will take in words and distribute them to form a connected puzzle.

    The biggest challenge is ensuring the program doesn’t get stuck. As it places words on the board one-by-one, it has to follow strict rules:

    • The words must be interlocked and aligned.
    • The whole puzzle must be connected as a single piece.
    • Words cannot form gibberish with their connecting path or neighboring words.
    • The grid must also not enter a permanent hangup trying to fit clashing words together.

    Approach:

    I will be splitting the work across the backend logic and frontend UI to make a presentable and functional puzzle.

    This program will use an algorithm to deal words. Upon a rule violation, it will backtrack to find an adequate fitting by reversing itself, deleting unideal portions of the current layout, and reseeding words differently. Essentially, it places a word on the grid, tests the board with another word, and if it hits a wall, the board cleanly undoes that path until it finds another valid one. This will continue until all words are fitted together and simultaneously satisfies the constraints.

    The frontend will contain stateless widgets that render with the flow of the grid. The widgets should simply read the state without mutating any data.

    This trial-and-error approach will afford the program the ability to test combinations until it finds a perfectly fitting, playable crossword puzzle!

     

    Currently, we are in the “Community Bonding Period” (May 1st-24th), and I have been communicating consistently with Jonathan Blandford to refine the approach and implementation. To gain practice, I have been working on this merge request for applicable practice towards my GSoC project.