RosettaCodeData/Task/Set-puzzle/D/set-puzzle-1.d
2015-02-20 00:35:01 -05:00

96 lines
3 KiB
D

import std.stdio, std.random, std.array, std.conv, std.traits,
std.exception, std.range, std.algorithm;
const class SetDealer {
protected {
enum Color: ubyte {green, purple, red}
enum Number: ubyte {one, two, three}
enum Symbol: ubyte {oval, diamond, squiggle}
enum Fill: ubyte {open, striped, solid}
static struct Card {
Color c;
Number n;
Symbol s;
Fill f;
}
static immutable Card[81] deck;
}
static this() pure nothrow @safe {
immutable colors = [EnumMembers!Color];
immutable numbers = [EnumMembers!Number];
immutable symbols = [EnumMembers!Symbol];
immutable fill = [EnumMembers!Fill];
deck = deck.length.iota.map!(i => Card(colors[i / 27],
numbers[(i / 9) % 3],
symbols[(i / 3) % 3],
fill[i % 3])).array;
}
// randomSample produces a sorted output that's convenient in our
// case because we're printing to stout. Normally you would want
// to shuffle.
immutable(Card)[] deal(in uint numCards) const {
enforce(numCards < deck.length, "Number of cards too large");
return deck[].randomSample(numCards).array;
}
// The summed enums of valid sets are always zero or a multiple
// of 3.
bool validSet(in ref Card c1, in ref Card c2, in ref Card c3)
const pure nothrow @safe @nogc {
return !((c1.c + c2.c + c3.c) % 3 ||
(c1.n + c2.n + c3.n) % 3 ||
(c1.s + c2.s + c3.s) % 3 ||
(c1.f + c2.f + c3.f) % 3);
}
immutable(Card)[3][] findSets(in Card[] cards, in uint target = 0)
const pure nothrow @safe {
immutable len = cards.length;
if (len < 3)
return null;
typeof(return) sets;
foreach (immutable i; 0 .. len - 2)
foreach (immutable j; i + 1 .. len - 1)
foreach (immutable k; j + 1 .. len)
if (validSet(cards[i], cards[j], cards[k])) {
sets ~= [cards[i], cards[j], cards[k]];
if (target != 0 && sets.length > target)
return null;
}
return sets;
}
}
const final class SetPuzzleDealer : SetDealer {
enum {basic = 9, advanced = 12}
override immutable(Card)[] deal(in uint numCards = basic) const {
immutable numSets = numCards / 2;
typeof(return) cards;
do {
cards = super.deal(numCards);
} while (findSets(cards, numSets).length != numSets);
return cards;
}
}
void main() {
const dealer = new SetPuzzleDealer;
const cards = dealer.deal;
writefln("DEALT %d CARDS:", cards.length);
writefln("%(%s\n%)", cards);
immutable sets = dealer.findSets(cards);
immutable len = sets.length;
writefln("\nFOUND %d SET%s:", len, len == 1 ? "" : "S");
writefln("%(%(%s\n%)\n\n%)", sets);
}