70 lines
2.7 KiB
C#
70 lines
2.7 KiB
C#
using System.Text.Json.Serialization;
|
|
|
|
namespace MultiversalDiplomacy.Model;
|
|
|
|
/// <summary>
|
|
/// A shared counter for handing out new timeline designations.
|
|
/// </summary>
|
|
[JsonConverter(typeof(TimelineFactoryJsonConverter))]
|
|
public class TimelineFactory(int nextTimeline)
|
|
{
|
|
private static readonly char[] Letters = [
|
|
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
|
|
'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
|
|
'u', 'v', 'w', 'x', 'y', 'z',
|
|
];
|
|
|
|
/// <summary>
|
|
/// Convert a string timeline identifier to its serial number.
|
|
/// </summary>
|
|
/// <param name="timeline">Timeline identifier.</param>
|
|
/// <returns>Integer.</returns>
|
|
public static int StringToInt(string timeline)
|
|
{
|
|
int result = Array.IndexOf(Letters, timeline[0]);
|
|
for (int i = 1; i < timeline.Length; i++) {
|
|
// The result is incremented by one because timeline designations are not a true base26 system.
|
|
// The "ones digit" maps a-z 0-25, but the "tens digit" maps a to 1, so "10" (26) is "aa" and not "a0"
|
|
result = (result + 1) * 26;
|
|
result += Array.IndexOf(Letters, timeline[i]);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Convert a timeline serial number to its string identifier.
|
|
/// </summary>
|
|
/// <param name="serial">Integer.</param>
|
|
/// <returns>Timeline identifier.</returns>
|
|
public static string IntToString(int serial) {
|
|
static int downshift(int i ) => (i - (i % 26)) / 26;
|
|
IEnumerable<char> result = [Letters[serial % 26]];
|
|
for (int remainder = downshift(serial); remainder > 0; remainder = downshift(remainder) - 1) {
|
|
// We subtract 1 after downshifting for the same reason we add 1 above after upshifting.
|
|
result = result.Prepend(Letters[(remainder % 26 + 25) % 26]);
|
|
}
|
|
return new string(result.ToArray());
|
|
}
|
|
|
|
/// <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 TimelineFactory() : this(0) { }
|
|
|
|
public int nextTimeline = nextTimeline;
|
|
|
|
public string NextTimeline() => IntToString(nextTimeline++);
|
|
}
|