DiamondShapes.cs
using System;
using System.Collections.Generic;
namespace Diamonds;
/// <summary>Edge-connected shapes on the half-width / half-height diamond lattice.</summary>
public static class DiamondShapes
{
public readonly record struct Cell( int Lane, int Row );
// Rotate on the diamond lattice, preserving each building block's upright proportions.
public static (float Lane, float Row) Rotate( (float Lane, float Row) cell, int turns ) => (turns & 3) switch
{
1 => (-cell.Row, cell.Lane),
2 => (-cell.Lane, -cell.Row),
3 => (cell.Row, -cell.Lane),
_ => cell
};
public sealed class Shape( string name, params Cell[] cells )
{
public string Name { get; } = name;
// Compute from the stable identity: hotload preserves existing catalog
// objects and does not rerun their constructors to initialize new fields.
// V shapes (including Hook's three-cell bend) turn around the shared
// corner point between their arms, not the center of a diamond.
public (float Lane, float Row) Pivot => Name switch
{
"Twin" or "Crown" => (0, 0),
"Slash" or "Fork" or "Cluster" or "Hook" or "Mirrored Hook" or "Trident" => (0, 1),
"Zigzag" or "Spear" => (0.5f, 1.5f),
"Mirrored Zigzag" => (-0.5f, 1.5f),
_ => throw new InvalidOperationException( $"Missing rotation pivot for {Name}" )
};
public IReadOnlyList<Cell> Cells { get; } = Array.AsReadOnly( cells );
public (float Lane, float Row) RotatedCell( int index, int turns )
{
var cell = Cells[index];
var offset = Rotate( (cell.Lane - Pivot.Lane, cell.Row - Pivot.Row), turns );
return (Pivot.Lane + offset.Lane, Pivot.Row + offset.Row);
}
}
static IReadOnlyList<Shape> catalog;
// Hotload preserves static objects. Rebuild an older catalog when shapes are
// added, keeping existing indices stable for pieces already on the board.
public static IReadOnlyList<Shape> All => catalog?.Count == 11 ? catalog : catalog = CreateCatalog();
static IReadOnlyList<Shape> CreateCatalog() => Array.AsReadOnly<Shape>(
[
new( "Twin", new( 0, 0 ), new( 1, 1 ) ),
new( "Slash", new( -1, 0 ), new( 0, 1 ), new( 1, 2 ) ),
new( "Fork", new( 0, 0 ), new( -1, 1 ), new( 1, 1 ) ),
new( "Crown", new( -1, 0 ), new( 1, 0 ), new( 0, 1 ) ),
new( "Cluster", new( 0, 0 ), new( -1, 1 ), new( 1, 1 ), new( 0, 2 ) ),
new( "Hook", new( 0, 0 ), new( 1, 1 ), new( 0, 2 ), new( -1, 3 ) ),
new( "Mirrored Hook", new( 0, 0 ), new( -1, 1 ), new( 0, 2 ), new( 1, 3 ) ),
new( "Zigzag", new( 0, 0 ), new( 1, 1 ), new( 0, 2 ), new( 1, 3 ) ),
new( "Mirrored Zigzag", new( 0, 0 ), new( -1, 1 ), new( 0, 2 ), new( -1, 3 ) ),
new( "Spear", new( -1, 0 ), new( 0, 1 ), new( 1, 2 ), new( 2, 3 ) ),
new( "Trident", new( 0, 1 ), new( -1, 0 ), new( 1, 0 ), new( 1, 2 ) )
] );
}