5dplomacy/MultiversalDiplomacy/Model/Season.cs

78 lines
2.8 KiB
C#

using System.Diagnostics.CodeAnalysis;
using System.Text.Json.Serialization;
namespace MultiversalDiplomacy.Model;
/// <summary>
/// Represents a multiversal coordinate at which a state of the map exists.
/// </summary>
[JsonConverter(typeof(SeasonJsonConverter))]
public struct Season(string timeline, int turn)
{
/// <summary>
/// The root season of every game. This is defined to avoid any confusion about what the first turn or timeline
/// should be or what season to use to key into a fresh <see cref="Timelines"/>.
/// </summary>
public static readonly Season First = new(Timelines.IntToString(0), 0);
/// <summary>
/// The timeline to which this season belongs.
/// </summary>
public string Timeline { get; } = timeline;
/// <summary>
/// The current turn, beginning at 0. Each season (spring and fall) is one turn.
/// Phases that only occur after the fall phase occur when Turn % 2 == 1.
/// The current year is (Turn / 2) + 1901.
/// </summary>
public int Turn { get; } = turn;
/// <summary>
/// The multiversal designation of this season.
/// </summary>
[JsonIgnore]
public readonly string Key => $"{this.Timeline}{this.Turn}";
/// <summary>
/// Create a new season from a tuple coordinate.
/// </summary>
public Season((string timeline, int turn) coord) : this(coord.timeline, coord.turn) { }
/// <summary>
/// Create a new season from a combined string designation.
/// </summary>
/// <param name="designation"></param>
public Season(string designation) : this(SplitKey(designation)) { }
/// <summary>
/// Extract the timeline and turn components of a season designation.
/// </summary>
/// <param name="seasonKey">A timeline-turn season designation.</param>
/// <returns>The timeline and turn components.</returns>
/// <exception cref="FormatException"></exception>
public static (string timeline, int turn) SplitKey(string seasonKey)
{
int i = 1;
for (; !char.IsAsciiDigit(seasonKey[i]) && i < seasonKey.Length; i++);
return int.TryParse(seasonKey.AsSpan(i), out int turn)
? (seasonKey[..i], turn)
: throw new FormatException($"Could not parse turn from {seasonKey}");
}
public override readonly string ToString() => Key;
/// <remarks>
/// Seasons are essentially 2D points, so they are equal when their components are equal.
/// </remarks>
public override readonly bool Equals([NotNullWhen(true)] object? obj)
=> obj is Season season
&& Timeline == season.Timeline
&& Turn == season.Turn;
public static bool operator ==(Season one, Season two) => one.Equals(two);
public static bool operator !=(Season one, Season two) => !(one == two);
public override readonly int GetHashCode() => (Timeline, Turn).GetHashCode();
}