Match/MatchState.Builders.cs

The file manages player preferences for 'Builders Yard' default cards. It stores player choices and includes logic to distribute cards when those defaults are triggered.

using System;
using System.Collections.Generic;
using System.Linq;
using CitySim.Cards;

namespace CitySim.Match;

/// <summary>The owner's persistent choice for the next Builders' Yard default.</summary>
public sealed record BuilderChoice(Guid Player, bool Common);

public sealed partial class MatchState
{
    readonly Dictionary<Guid,bool> builderChoices = new();

    /// <summary>True selects a random Common; false selects a Street, the default.</summary>
    public bool BuilderCommon(Guid player) => builderChoices.GetValueOrDefault(player);

    /// <summary>Choose the next round's extra card while this player owns a Builders' Yard.</summary>
    public bool ChooseBuilderDefault(Guid player, bool common)
    {
        if (!players.ContainsKey(player) || Phase == MatchPhase.EndGame || !Cells().Any(c => c.Owner == player && c.CardId == "builders-yard")) return false;
        if (BuilderCommon(player) == common) return true;
        if (common) builderChoices[player] = true; else builderChoices.Remove(player);
        Events.Tray(player);
        return true;
    }

    internal BuilderChoice[] BuilderChoiceSnapshot() => builderChoices.OrderBy(p => p.Key).Select(p => new BuilderChoice(p.Key,p.Value)).ToArray();

    internal void LoadBuilderChoices(IEnumerable<BuilderChoice> choices)
    {
        var entries = (choices ?? Array.Empty<BuilderChoice>()).ToArray();
        if (entries.Any(choice => choice == null || !players.ContainsKey(choice.Player)) ||
            entries.Select(choice => choice.Player).Distinct().Count() != entries.Length)
            throw new ArgumentException("Invalid Builder preference checkpoint.");
        builderChoices.Clear();
        foreach (var choice in entries) if (choice.Common) builderChoices[choice.Player] = true;
    }

    /// <summary>Deal each owned builder's card in placement order, after shared defaults.</summary>
    internal void BuilderDefaults(Random random)
    {
        foreach (var source in Cells().OrderBy(c => c.Order))
        foreach (var effect in CardLibrary.Get(source.CardId).Effects.Where(e => e.Hook == EffectHook.RoundStart && e.Rule is "builder-street" or "builder-choice"))
        for (var i = 0; i < effect.Amount; i++)
        {
            if (!players.TryGetValue(source.Owner,out var owner) || owner.Hand.Count >= Settings.HandLimit) break;
            var card = effect.Rule == "builder-choice" ? CardDraw.Common(random) : "street";
            GiveCard(source.Owner,card);
        }
    }
}