summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--BCDD.csproj1
-rw-r--r--DDTable.cs145
-rw-r--r--PBNBoard.cs152
-rw-r--r--Program.cs31
4 files changed, 329 insertions, 0 deletions
diff --git a/BCDD.csproj b/BCDD.csproj
index 915d67c..50be342 100644
--- a/BCDD.csproj
+++ b/BCDD.csproj
@@ -48,6 +48,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="BCalcWrapper.cs" />
+ <Compile Include="DDTable.cs" />
<Compile Include="PBNBoard.cs" />
<Compile Include="PBNFile.cs" />
<Compile Include="Program.cs" />
diff --git a/DDTable.cs b/DDTable.cs
new file mode 100644
index 0000000..78d58b9
--- /dev/null
+++ b/DDTable.cs
@@ -0,0 +1,145 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Runtime.InteropServices;
+using System.Text.RegularExpressions;
+
+namespace BCDD
+{
+ class DDTableInvalidException : FieldNotFoundException
+ {
+ public DDTableInvalidException() : base() { }
+ public DDTableInvalidException(String msg) : base(msg) { }
+ }
+
+ class DDTable
+ {
+ private PBNBoard board;
+
+ private int[,] getEmptyTable()
+ {
+ int[,] result = new int[4, 5];
+ for (int i = 0; i < 4; i++)
+ {
+ for (int j = 0; j < 5; j++)
+ {
+ result[i, j] = -1;
+ }
+ }
+ return result;
+ }
+
+ private int[,] validateTable(int[,] table)
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ for (int j = 0; j < 5; j++)
+ {
+ if (table[i, j] > 13 || table[i, j] < 0)
+ {
+ throw new DDTableInvalidException("Invalid number of tricks: " + table[i, j].ToString());
+ }
+ }
+ }
+ return table;
+ }
+
+ public DDTable(PBNBoard board)
+ {
+ this.board = board;
+ }
+
+ public int[,] GetBCalcTable()
+ {
+ int[,] result = this.getEmptyTable();
+ String deal = this.board.GetLayout();
+ IntPtr solver = BCalcWrapper.bcalcDDS_new(Marshal.StringToHGlobalAnsi("PBN"), Marshal.StringToHGlobalAnsi(deal), 0, 0);
+ for (int denom = 0; denom < 5; denom++)
+ {
+ BCalcWrapper.bcalcDDS_setTrumpAndReset(solver, denom);
+ for (int player = 0; player < 4; player++)
+ {
+ BCalcWrapper.bcalcDDS_setPlayerOnLeadAndReset(solver, BCalcWrapper.bcalc_declarerToLeader(player));
+ result[player, denom] = 13 - BCalcWrapper.bcalcDDS_getTricksToTake(solver);
+ }
+ }
+ return this.validateTable(result);
+ }
+
+ public int[,] GetJFRTable()
+ {
+ int[,] result = this.getEmptyTable();
+ String ability = this.board.GetAbility();
+ MatchCollection abilities = this.board.ValidateAbility(ability);
+ foreach (Match playerAbility in abilities)
+ {
+ char player = playerAbility.Groups[1].Value[0];
+ int playerID = Array.IndexOf(BCalcWrapper.PLAYERS, player);
+ int denomID = 4;
+ foreach (char tricks in playerAbility.Groups[2].Value.ToCharArray())
+ {
+ result[playerID, denomID] = (tricks > '9') ? (tricks - 'A' + 10) : (tricks - '0');
+ denomID--;
+ }
+ }
+ return this.validateTable(result);
+ }
+
+ public int[,] GetPBNTable()
+ {
+ List<String> table = this.board.GetOptimumResultTable();
+ List<Match> parsedTable = this.board.ValidateOptimumResultTable(table);
+ int[,] result = this.getEmptyTable();
+ foreach (Match lineMatch in parsedTable)
+ {
+ char player = lineMatch.Groups[1].Value[0];
+ char denom = lineMatch.Groups[2].Value[0];
+ int tricks = Int16.Parse(lineMatch.Groups[3].Value);
+ int playerID = Array.IndexOf(BCalcWrapper.PLAYERS, player);
+ int denomID = Array.IndexOf(BCalcWrapper.DENOMINATIONS, denom);
+ result[playerID, denomID] = tricks;
+ }
+ return this.validateTable(result);
+ }
+
+ public int[,] GetDDTable()
+ {
+ try
+ {
+ return this.GetJFRTable();
+ }
+ catch (FieldNotFoundException)
+ {
+ try
+ {
+ return this.GetPBNTable();
+ }
+ catch (FieldNotFoundException)
+ {
+ return this.GetBCalcTable();
+ }
+ }
+ }
+
+ public static void PrintTable(int[,] ddTable)
+ {
+ foreach (char header in BCalcWrapper.DENOMINATIONS)
+ {
+ Console.Write('\t');
+ Console.Write(header);
+ }
+ Console.WriteLine();
+ for (int i = 0; i < 4; i++)
+ {
+ Console.Write(BCalcWrapper.PLAYERS[i]);
+ for (int j = 0; j < 5; j++)
+ {
+ Console.Write('\t');
+ Console.Write(ddTable[i, j].ToString());
+ }
+ Console.WriteLine();
+ }
+ }
+ }
+}
diff --git a/PBNBoard.cs b/PBNBoard.cs
index f03b809..c9ae180 100644
--- a/PBNBoard.cs
+++ b/PBNBoard.cs
@@ -36,6 +36,9 @@ namespace BCDD
{
public List<PBNField> Fields;
+ private bool? hasOptimumResultTable = null;
+ private bool? hasAbility = null;
+
public PBNBoard(List<string> lines)
{
this.Fields = new List<PBNField>();
@@ -114,5 +117,154 @@ namespace BCDD
return this.GetField("Dealer");
}
+ public MatchCollection ValidateAbility(String ability)
+ {
+ Regex abilityMatch = new Regex(@"\b([NESW]):([0-9A-D]{5})\b");
+ MatchCollection matches = abilityMatch.Matches(ability);
+ if (matches.Count != 4)
+ {
+ this.hasAbility = false;
+ throw new DDTableInvalidException("Invalid Ability line: " + ability);
+ }
+ List<String> players = new List<String>();
+ foreach (Match match in matches)
+ {
+ if (players.Contains(match.Groups[1].Value))
+ {
+ this.hasAbility = false;
+ throw new DDTableInvalidException("Duplicate entry in Ability: " + match.Groups[0].Value);
+ }
+ else
+ {
+ players.Add(match.Groups[1].Value);
+ }
+ }
+ this.hasAbility = true;
+ return matches;
+ }
+
+ public String GetAbility()
+ {
+ return this.GetField("Ability");
+ }
+
+ public void DeleteAbility()
+ {
+ this.DeleteField("Ability");
+ }
+
+ public void WriteAbility(int[,] ddTable)
+ {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < 4; i++)
+ {
+ sb.Append(BCalcWrapper.PLAYERS[i]);
+ sb.Append(':');
+ for (int j = 4; j >= 0; j--)
+ {
+ sb.Append((char)(ddTable[i, j] > 9 ? 'A' + ddTable[i, j] - 10 : ddTable[i, j] + '0'));
+ }
+ if (i < 3)
+ {
+ sb.Append(' ');
+ }
+ }
+ String abilityStr = sb.ToString();
+ this.Fields.Add(new PBNField("Ability", abilityStr));
+ }
+
+ public List<Match> ValidateOptimumResultTable(List<String> table)
+ {
+ Regex linePattern = new Regex(@"^([NESW])\s+([CDHSN])T?\s+(\d+)$");
+ List<Match> matches = new List<Match>();
+ List<String> duplicates = new List<String>();
+ foreach (String line in table)
+ {
+ Match match = linePattern.Match(line);
+ if (!match.Success)
+ {
+ this.hasOptimumResultTable = false;
+ throw new DDTableInvalidException("Invalid OptimumResultTable line: " + line);
+ }
+ String position = match.Groups[1].Value + " - " + match.Groups[2].Value;
+ if (duplicates.Contains(position))
+ {
+ this.hasOptimumResultTable = false;
+ throw new DDTableInvalidException("Duplicate OptimumResultTable line: " + line);
+ }
+ else
+ {
+ duplicates.Add(position);
+ }
+ matches.Add(match);
+ }
+ this.hasOptimumResultTable = true;
+ return matches;
+ }
+
+ public List<String> GetOptimumResultTable()
+ {
+ bool fieldFound = false;
+ List<String> result = new List<String>();
+ foreach (PBNField field in this.Fields)
+ {
+ if ("OptimumResultTable".Equals(field.Key))
+ {
+ fieldFound = true;
+ }
+ else
+ {
+ if (fieldFound)
+ {
+ if (field.Key == null)
+ {
+ result.Add(field.RawField);
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+ }
+ if (!fieldFound)
+ {
+ this.hasOptimumResultTable = false;
+ throw new FieldNotFoundException("OptimumResultTable field not found");
+ }
+ return result;
+ }
+
+ public void DeleteOptimumResultTable()
+ {
+ bool fieldFound = false;
+ List<PBNField> toRemove = new List<PBNField>();
+ foreach (PBNField field in this.Fields) {
+ if ("OptimumResultTable".Equals(field.Key))
+ {
+ fieldFound = true;
+ toRemove.Add(field);
+ }
+ else
+ {
+ if (fieldFound)
+ {
+ if (field.Key == null)
+ {
+ toRemove.Add(field);
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+ }
+ foreach (PBNField remove in toRemove)
+ {
+ this.Fields.Remove(remove);
+ }
+ }
+
}
}
diff --git a/Program.cs b/Program.cs
index e0a7149..814edce 100644
--- a/Program.cs
+++ b/Program.cs
@@ -18,6 +18,37 @@ namespace BCDD
{
Console.WriteLine("Analyzing " + filename);
PBNFile file = new PBNFile(filename);
+ foreach (PBNBoard board in file.Boards)
+ {
+ DDTable table = new DDTable(board);
+ String boardNo;
+ try
+ {
+ boardNo = board.GetNumber();
+ }
+ catch (FieldNotFoundException)
+ {
+ boardNo = "?";
+ }
+ try
+ {
+ int[,] ddTable = table.GetDDTable();
+ if (ddTable != null)
+ {
+ Console.WriteLine("Board " + boardNo);
+ DDTable.PrintTable(ddTable);
+ }
+ else
+ {
+ Console.WriteLine("ERROR: unable to determine DD table for board " + boardNo);
+ }
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine(e.Message);
+ Console.WriteLine(e.StackTrace);
+ }
+ }
}
Console.WriteLine("Press any key to continue...");
Console.ReadLine();