Set

Here's the repo so you can play for yourself!

The linear thing

This repository contains a version of Set implemented with linear logic (I think. I don't know much about linear logic). In this version of the code, the GameCore structure, which handles the actual logic of the game and makes sure that all moves are valid, does not store the cards that are currently in play. Instead, when the code starts a new game, it recieves the GameCore object and the 12 starting cards, which it has to store itself. When a player selects three cards, the textui code passes those three cards to the game core, and (sometimes) recieves three new cards back.

Using some clever compile-time stuff, we can ensure that a card is never used multiple times, and that cards from one game are never used in a different game.

The first part of that is relatively easy: we just don't implement Clone on the GameCards, and the GameCore.move() function takes ownership of the cards passed into it, so playing a move requires giving up the card.

The second part is a bit harder: I used a trick from this post.

The basic idea is to add do-nothing lifetime parameters to the GameCore and GameCard objects, and then instead of exposing the new() method on GameCore, we have a with_core method that takes a closure which accepts a GameCore as its argument.

Because of the for <'any> trait bound on the closure, the closure has to work for any possible lifetime of the GameCore, which means it can't use cards from one GameCore with a different GameCore. The exact details of this are more complicated and I refer interested readers to the post above for more clarification.

In line with that post, I too will recommend not doing this, except as a fun little experiment like here. But it does expose an interesting potential for complex compile-time checks in a borrow-checking system like Rust's.