summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMichał Klichowicz <emkael@tlen.pl>2026-09-12 01:22:03 +0200
committerMichał Klichowicz <emkael@tlen.pl>2026-09-12 01:22:03 +0200
commit5bb8e3d3863a7fd0cffecc7186254ca038fbc49e (patch)
tree3b7ef141d961d27182f8edac6341dc35789123b8 /src
parent7f34ea7410125e0ab74e7aa647ec68e2ec75db32 (diff)
Board layout validation and error reporting
Diffstat (limited to 'src')
-rw-r--r--src/BCDDFile.cs7
-rw-r--r--src/BoardLayout.cs84
-rw-r--r--src/PBNBoard.cs50
3 files changed, 141 insertions, 0 deletions
diff --git a/src/BCDDFile.cs b/src/BCDDFile.cs
index f0e7522..36a0872 100644
--- a/src/BCDDFile.cs
+++ b/src/BCDDFile.cs
@@ -57,6 +57,13 @@ namespace BCDD
}
try
{
+ List<String> validationErrors = board.ValidateLayout();
+ if (validationErrors.Count > 0)
+ {
+ throw new InvalidLayoutException(
+ String.Join("; ", validationErrors.ToArray())
+ );
+ }
int[,] ddTable = table.GetDDTable();
if (ddTable != null)
{
diff --git a/src/BoardLayout.cs b/src/BoardLayout.cs
new file mode 100644
index 0000000..74c9d0d
--- /dev/null
+++ b/src/BoardLayout.cs
@@ -0,0 +1,84 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+
+namespace BCDD
+{
+ class InvalidLayoutException : Exception
+ {
+ public InvalidLayoutException() : base() { }
+ public InvalidLayoutException(String msg) : base(msg) { }
+ }
+
+ class BoardLayout
+ {
+ public Dictionary<String, Dictionary<String, List<String>>> Hands;
+
+ public BoardLayout() { }
+ public BoardLayout(String pbnLayout)
+ {
+ this.parsePBNLayout(pbnLayout);
+ }
+
+ public void parsePBNLayout(string pbnLayout)
+ {
+ this.Hands = new Dictionary<String, Dictionary<String, List<String>>>();
+
+ // split first player designation from hands
+ string[] deal = pbnLayout.Split(':');
+ if (deal.Length < 2)
+ {
+ throw new InvalidLayoutException(String.Format("First hand undefined in PBN layout: {0}", pbnLayout));
+ }
+ if (deal[0].Length != 1 || !BCalcWrapper.PLAYERS.Contains(deal[0][0]))
+ {
+ throw new InvalidLayoutException(String.Format("Invalid first hand designation: {0}", deal[0]));
+ }
+
+ // determine order of players for hands
+ int handLetterIndex = Array.IndexOf(BCalcWrapper.PLAYERS, deal[0][0]);
+ char[] handLetters = new char[4];
+ Array.Copy(BCalcWrapper.PLAYERS, handLetterIndex, handLetters, 0, 4 - handLetterIndex);
+ Array.Copy(BCalcWrapper.PLAYERS, 0, handLetters, 4 - handLetterIndex, handLetterIndex);
+
+ // split cards into hands
+ string[] playerHands = deal[1].Split(' ');
+ if (playerHands.Length != 4)
+ {
+ throw new InvalidLayoutException(String.Format("Deal does not contain four hands: {0}", deal[1]));
+ }
+
+ // split each hand into suits
+ for (int playerIndex = 0; playerIndex < playerHands.Length; playerIndex++)
+ {
+ Dictionary<String, List<String>> hand = new Dictionary<string, List<String>>();
+ string[] cards = playerHands[playerIndex].Split('.');
+ if (cards.Length != 4)
+ {
+ throw new InvalidLayoutException(String.Format("Hand does not contain four suits: {0}", playerHands[playerIndex]));
+ }
+ int playerCardCount = 0;
+ for (int suitIndex = 0; suitIndex < cards.Length; suitIndex++)
+ {
+ List<String> currentHand = new List<String>();
+ foreach (String card in Regex.Split(cards[suitIndex], string.Empty))
+ {
+ if (card.Length > 0)
+ {
+ currentHand.Add(card);
+ playerCardCount++;
+ }
+ }
+ hand[(BCalcWrapper.DENOMINATIONS[3 - suitIndex]).ToString()] = currentHand;
+ }
+ if (playerCardCount != 13)
+ {
+ throw new InvalidLayoutException(String.Format("Hand does not contain 13 cards: {0}", playerHands[playerIndex]));
+ }
+ this.Hands[(handLetters[playerIndex]).ToString()] = hand;
+ }
+ }
+ }
+}
diff --git a/src/PBNBoard.cs b/src/PBNBoard.cs
index 69019c0..fb36f00 100644
--- a/src/PBNBoard.cs
+++ b/src/PBNBoard.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
@@ -445,5 +446,54 @@ namespace BCDD
this.WriteAbility(ddTable);
}
}
+
+ public List<String> ValidateLayout()
+ {
+ string[] cards = { "A", "K", "Q", "J", "T", "9", "8", "7", "6", "5", "4", "3", "2" };
+
+ Dictionary<String, List<String>> deck = new Dictionary<String, List<String>>();
+ for (int suitIndex = 3; suitIndex >= 0; suitIndex--)
+ {
+ foreach (string card in cards)
+ {
+ deck[BCalcWrapper.DENOMINATIONS[suitIndex].ToString() + card] = new List<String>();
+ }
+ }
+
+ List<String> errors = new List<string>();
+
+ BoardLayout layout = new BoardLayout(this.GetLayout());
+ foreach (KeyValuePair<String, Dictionary<String, List<String>>> hand in layout.Hands)
+ {
+ foreach (KeyValuePair<String, List<String>> suit in hand.Value)
+ {
+ foreach (String card in suit.Value)
+ {
+ if (cards.Contains(card.ToUpper()))
+ {
+ deck[suit.Key + card.ToUpper()].Add(hand.Key);
+ }
+ else
+ {
+ errors.Add(String.Format("Invalid card in {0}: {1}{2}", hand.Key, suit.Key, card));
+ }
+ }
+ }
+ }
+
+ foreach (KeyValuePair<String, List<String>> card in deck)
+ {
+ if (card.Value.Count == 0)
+ {
+ errors.Add(String.Format("Missing card: {0}", card.Key));
+ }
+ if (card.Value.Count > 1)
+ {
+ errors.Add(String.Format("Duplicate card in {0}: {1}", String.Join(", ", card.Value.ToArray()), card.Key));
+ }
+ }
+
+ return errors;
+ }
}
}