From 5bb8e3d3863a7fd0cffecc7186254ca038fbc49e Mon Sep 17 00:00:00 2001 From: Michał Klichowicz Date: Sat, 12 Sep 2026 01:22:03 +0200 Subject: Board layout validation and error reporting --- src/BCDDFile.cs | 7 +++++ src/BoardLayout.cs | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/PBNBoard.cs | 50 ++++++++++++++++++++++++++++++++ 3 files changed, 141 insertions(+) create mode 100644 src/BoardLayout.cs (limited to 'src') 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 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>> Hands; + + public BoardLayout() { } + public BoardLayout(String pbnLayout) + { + this.parsePBNLayout(pbnLayout); + } + + public void parsePBNLayout(string pbnLayout) + { + this.Hands = new Dictionary>>(); + + // 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> hand = new Dictionary>(); + 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 currentHand = new List(); + 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 ValidateLayout() + { + string[] cards = { "A", "K", "Q", "J", "T", "9", "8", "7", "6", "5", "4", "3", "2" }; + + Dictionary> deck = new Dictionary>(); + for (int suitIndex = 3; suitIndex >= 0; suitIndex--) + { + foreach (string card in cards) + { + deck[BCalcWrapper.DENOMINATIONS[suitIndex].ToString() + card] = new List(); + } + } + + List errors = new List(); + + BoardLayout layout = new BoardLayout(this.GetLayout()); + foreach (KeyValuePair>> hand in layout.Hands) + { + foreach (KeyValuePair> 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> 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; + } } } -- cgit v1.3.1