Task: Set puzzle



C#: using System;
using System.Collections.Generic;
using static System.Linq.Enumerable;

public static class SetPuzzle
{
    static readonly Feature[] numbers  = { (1, "One"), (2, "Two"), (3, "Three") };
    static readonly Feature[] colors   = { (1, "Red"), (2, "Green"), (3, "Purple") };
    static readonly Feature[] shadings = { (1, "Open"), (2, "Striped"), (3, "Solid") };
    static readonly Feature[] symbols  = { (1, "Oval"), (2, "Squiggle"), (3, "Diamond") };

    private readonly struct Feature
    {
        public Feature(int value, string name) => (Value, Name) = (value, name);
        public int Value { get; }
        public string Name { get; }
        public static implicit operator int(Feature f) => f.Value;
        public static implicit operator Feature((int value, string name) t) => new Feature(t.value, t.name);
        public override string ToString() => Name;
    }

    private readonly struct Card : IEquatable<Card>
    {
        public Card(Feature number, Feature color, Feature shading, Feature symbol) =>
            (Number, Color, Shading, Symbol) = (number, color, shading, symbol);

        public Feature Number { get; }
        public Feature Color { get; }
        public Feature Shading { get; }
        public Feature Symbol { get; }

        public override string ToString() => $"{Number} {Color} {Shading} {Symbol}(s)";
        public bool Equals(Card other) => Number == other.Number && Color == other.Color && Shading == other.Shading && Symbol == other.Symbol;
    }

    public static void Main() {
        Card[] deck = (
            from number in numbers
            from color in colors
            from shading in shadings
            from symbol in symbols
            select new Card(number, color, shading, symbol)
        ).ToArray();
        var random = new Random();

        Deal(deck, 9, 4, random);
        Console.WriteLine();
        Console.WriteLine();
        Deal(deck, 12, 6, random);
    }

    static void Deal(Card[] deck, int size, int target, Random random) {
        List<(Card a, Card b, Card c)> sets;
        do {
            Shuffle(deck, random.Next);
            sets = (
                from i in 0.To(size - 2)
                from j in (i + 1).To(size - 1)
                from k in (j + 1).To(size)
                select (deck[i], deck[j], deck[k])
            ).Where(IsSet).ToList();
        } while (sets.Count != target);
        Console.WriteLine("The board:");
        foreach (Card card in deck.Take(size)) Console.WriteLine(card);
        Console.WriteLine();
        Console.WriteLine("Sets:");
        foreach (var s in sets) Console.WriteLine(s);
    }

    static void Shuffle<T>(T[] array, Func<int, int, int> rng) {
        for (int i = 0; i < array.Length; i++) {
            int r = rng(i, array.Length);
            (array[r], array[i]) = (array[i], array[r]);
        }
    }

    static bool IsSet((Card a, Card b, Card c) t) =>
        AreSameOrDifferent(t.a.Number, t.b.Number, t.c.Number) &&
        AreSameOrDifferent(t.a.Color, t.b.Color, t.c.Color) &&
        AreSameOrDifferent(t.a.Shading, t.b.Shading, t.c.Shading) &&
        AreSameOrDifferent(t.a.Symbol, t.b.Symbol, t.c.Symbol);

    static bool AreSameOrDifferent(int a, int b, int c) => (a + b + c) % 3 == 0;
    static IEnumerable<int> To(this int start, int end) => Range(start, end - start - 1);
}



Java: import java.util.*;

public class SetPuzzle {

    enum Color {

        GREEN(0), PURPLE(1), RED(2);

        private Color(int v) {
            val = v;
        }
        public final int val;
    }

    enum Number {

        ONE(0), TWO(1), THREE(2);

        private Number(int v) {
            val = v;
        }
        public final int val;
    }

    enum Symbol {

        OVAL(0), DIAMOND(1), SQUIGGLE(2);

        private Symbol(int v) {
            val = v;
        }
        public final int val;
    }

    enum Fill {

        OPEN(0), STRIPED(1), SOLID(2);

        private Fill(int v) {
            val = v;
        }
        public final int val;
    }

    private static class Card implements Comparable<Card> {

        Color c;
        Number n;
        Symbol s;
        Fill f;

        @Override
        public String toString() {
            return String.format("[Card: %s, %s, %s, %s]", c, n, s, f);
        }

        @Override
        public int compareTo(Card o) {
            return (c.val - o.c.val) * 10 + (n.val - o.n.val);
        }
    }
    private static Card[] deck;

    public static void main(String[] args) {
        deck = new Card[81];
        Color[] colors = Color.values();
        Number[] numbers = Number.values();
        Symbol[] symbols = Symbol.values();
        Fill[] fillmodes = Fill.values();
        for (int i = 0; i < deck.length; i++) {
            deck[i] = new Card();
            deck[i].c = colors[i / 27];
            deck[i].n = numbers[(i / 9) % 3];
            deck[i].s = symbols[(i / 3) % 3];
            deck[i].f = fillmodes[i % 3];
        }
        findSets(12);
    }

    private static void findSets(int numCards) {
        int target = numCards / 2;
        Card[] cards;
        Card[][] sets = new Card[target][3];
        int cnt;
        do {
            Collections.shuffle(Arrays.asList(deck));
            cards = Arrays.copyOfRange(deck, 0, numCards);
            cnt = 0;

            outer:
            for (int i = 0; i < cards.length - 2; i++) {
                for (int j = i + 1; j < cards.length - 1; j++) {
                    for (int k = j + 1; k < cards.length; k++) {
                        if (validSet(cards[i], cards[j], cards[k])) {
                            if (cnt < target)
                                sets[cnt] = new Card[]{cards[i], cards[j], cards[k]};
                            if (++cnt > target) {
                                break outer;
                            }
                        }
                    }
                }
            }
        } while (cnt != target);

        Arrays.sort(cards);

        System.out.printf("GIVEN %d CARDS:\n\n", numCards);
        for (Card c : cards) {
            System.out.println(c);
        }
        System.out.println();

        System.out.println("FOUND " + target + " SETS:\n");
        for (Card[] set : sets) {
            for (Card c : set) {
                System.out.println(c);
            }
            System.out.println();
        }
    }

    private static boolean validSet(Card c1, Card c2, Card c3) {
        int tot = 0;
        tot += (c1.c.val + c2.c.val + c3.c.val) % 3;
        tot += (c1.n.val + c2.n.val + c3.n.val) % 3;
        tot += (c1.s.val + c2.s.val + c3.s.val) % 3;
        tot += (c1.f.val + c2.f.val + c3.f.val) % 3;
        return tot == 0;
    }
}