/PatienceSolverConsole/PatienceSolverConsole/PatienceField.cs

http://github.com/kpvleeuwen/Boggle-solver · C# · 255 lines · 192 code · 32 blank · 31 comment · 24 complexity · 855847fc285be5de249569e308fc0f51 MD5 · raw file

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Collections.ObjectModel;
  6. using System.IO;
  7. namespace PatienceSolverConsole
  8. {
  9. /// <summary>
  10. /// Immutable representation of a patience field state
  11. /// </summary>
  12. public class PatienceField
  13. {
  14. /// <summary>
  15. /// Gets valid destination stacks, from least to most likely to lead to a solution
  16. /// </summary>
  17. /// <returns></returns>
  18. public IEnumerable<CardStack> GetDestinationStacks()
  19. {
  20. return PlayStacks.Cast<CardStack>()
  21. .Concat(FinishStacks.Cast<CardStack>());
  22. }
  23. /// <summary>
  24. /// Gets valid origin stacks, from least to most likely to lead to a solution
  25. /// </summary>
  26. /// <returns></returns>
  27. public IEnumerable<CardStack> GetOriginStacks()
  28. {
  29. yield return Stock;
  30. foreach (var stack in PlayStacks)
  31. yield return stack;
  32. }
  33. public IEnumerable<PlayStack> PlayStacks { get; private set; }
  34. public IEnumerable<FinishStack> FinishStacks { get; private set; }
  35. public Stock Stock { get; private set; }
  36. private int _hash;
  37. private IList<PlayStack> _playStacksOrdered;
  38. public PatienceField(Stock stock, IEnumerable<PlayStack> playStacks, IEnumerable<FinishStack> finishStacks)
  39. {
  40. Stock = stock;
  41. PlayStacks = playStacks.ToList();
  42. FinishStacks = finishStacks.ToList();
  43. _playStacksOrdered = PlayStacks.Where(s => s.Count > 0).OrderBy(s => s.Top.GetHashCode()).ToList();
  44. _hash = DoGetHashCode();
  45. }
  46. public static PatienceField FillWithRandomCards(Random random)
  47. {
  48. var cards = GetStock().ToList();
  49. Util.Shuffle(cards, random);
  50. IEnumerable<Card> stackless = cards;
  51. var playstacks = new List<PlayStack>();
  52. var finishstacks = new List<FinishStack>();
  53. for (int playstack = 1; playstack <= 7; playstack++)
  54. {
  55. var stack = PlayStack.Create(stackless.Take(playstack));
  56. stackless = stackless.Skip(playstack);
  57. playstacks.Add(stack);
  58. }
  59. for (int finishstack = 1; finishstack <= 4; finishstack++)
  60. {
  61. var stack = FinishStack.Create(Enumerable.Empty<Card>());
  62. finishstacks.Add(stack);
  63. }
  64. var stock = new Stock(stackless, false);
  65. return new PatienceField(stock, playstacks, finishstacks);
  66. }
  67. /// <summary>
  68. /// returns all cards of every suit, ordered by suit.
  69. /// </summary>
  70. /// <returns></returns>
  71. public static IEnumerable<Card> GetStock()
  72. {
  73. foreach (var suit in Util.GetValues<Suit>())
  74. foreach (var value in Util.GetValues<Value>())
  75. yield return new Card(suit, value);
  76. }
  77. /// <summary>
  78. /// Prints the field to Console.Out
  79. /// </summary>
  80. public void DumpToConsole()
  81. {
  82. var toprow = FinishStacks.Cast<CardStack>()
  83. .Concat(new[] { Stock });
  84. DumpRows(toprow);
  85. DumpRows(PlayStacks.ToArray());
  86. }
  87. private static void DumpRows(IEnumerable<CardStack> toprow)
  88. {
  89. int i = 0;
  90. bool morerows;
  91. do
  92. {
  93. morerows = false;
  94. foreach (var row in toprow)
  95. {
  96. morerows |= row.WriteLine(i);
  97. }
  98. Console.WriteLine();
  99. i++;
  100. } while (morerows);
  101. }
  102. /// <summary>
  103. /// Two fields equal if all the stacks are the same.
  104. /// But, if the location of the stacks are rearranged, the fields are equal too.
  105. /// </summary>
  106. /// <param name="obj"></param>
  107. /// <returns></returns>
  108. public override bool Equals(object obj)
  109. {
  110. if (ReferenceEquals(this, obj)) return true;
  111. var other = obj as PatienceField;
  112. if (other == null) return false;
  113. if (other.GetHashCode() != this.GetHashCode())
  114. return false; // this should be performant
  115. if (!Stock.Equals(other.Stock))
  116. return false;
  117. return _playStacksOrdered.SequenceEqual(other._playStacksOrdered);
  118. // The finish stacks are not checked, because only the cards not in stock or on the play stacks are there.
  119. // There is only one significant order possible, so if the cards in stock and on the play stacks are equal, so must be the finish stacks.
  120. }
  121. public override int GetHashCode()
  122. {
  123. return _hash;
  124. }
  125. private int DoGetHashCode()
  126. {
  127. var mystacksOrdered = PlayStacks.OrderByDescending(s => s.GetHashCode());
  128. var hashcode = 0;
  129. foreach (var stack in mystacksOrdered)
  130. hashcode = hashcode * 81 + stack.GetHashCode();
  131. return hashcode;
  132. }
  133. public bool IsDone()
  134. {
  135. return
  136. PlayStacks
  137. .SelectMany(s => s)
  138. .All(card => card.Visible);
  139. }
  140. private int GetValue(Card c)
  141. {
  142. if (c == null) return 0;
  143. return (int)c.Value;
  144. }
  145. public PatienceField Move(Card toMove, CardStack from, CardStack to)
  146. {
  147. var newto = to.Accept(toMove, from);
  148. var newfrom = from.Remove(toMove);
  149. var newstock = Replace(Stock, from, newfrom);
  150. var newplaystacks = PlayStacks;
  151. var newfinishstacks = FinishStacks;
  152. if (from is PlayStack)
  153. newplaystacks = newplaystacks.Select(ps => Replace(ps, from, newfrom));
  154. else if (from is FinishStack)
  155. newfinishstacks = newfinishstacks.Select(ps => Replace(ps, from, newfrom));
  156. if (to is PlayStack)
  157. newplaystacks = newplaystacks.Select(ps => Replace(ps, to, newto));
  158. else if (to is FinishStack)
  159. newfinishstacks = newfinishstacks.Select(ps => Replace(ps, to, newto));
  160. return new PatienceField(newstock, newplaystacks, newfinishstacks);
  161. }
  162. private T Replace<T>(T instance, object tosearch, object replacement)
  163. {
  164. if (object.ReferenceEquals(tosearch, instance))
  165. return (T)replacement;
  166. return instance;
  167. }
  168. public PatienceField DoTrivialMoves()
  169. {
  170. // Try the tops of the playstacks
  171. foreach (var stack in PlayStacks)
  172. {
  173. if (stack.Count == 0) continue;
  174. var card = stack.Top;
  175. if (IsSafeToPlay(card))
  176. {
  177. foreach (var dest in FinishStacks.Where(s => s.CanAccept(card, stack)))
  178. {
  179. return Move(card, stack, dest).DoTrivialMoves();
  180. }
  181. }
  182. }
  183. // Try all stock cards
  184. foreach (var stockcard in Stock.Where(IsSafeToPlay))
  185. foreach (var dest in FinishStacks.Where(s => s.CanAccept(stockcard, Stock)))
  186. {
  187. return Move(stockcard, Stock, dest).DoTrivialMoves();
  188. }
  189. return this;
  190. }
  191. private bool IsSafeToPlay(Card card)
  192. { // this move does never block a solution since it
  193. // cannot block another card:
  194. // all cards that can go on top of this card,
  195. // can also enter their finish stack.
  196. if (card.Value == Value.Ace) return true;
  197. return FinishStacks.Select(f => GetValue(f.Top)).All(
  198. value => value >= GetValue(card) - 2);
  199. }
  200. internal PatienceField NextCard()
  201. {
  202. return new PatienceField(Stock.NextCard(), PlayStacks, FinishStacks);
  203. }
  204. }
  205. static class Util
  206. {
  207. public static IEnumerable<T> GetValues<T>()
  208. {
  209. return Enum.GetValues(typeof(T)).Cast<T>();
  210. }
  211. internal static void Shuffle<T>(IList<T> cards, Random random)
  212. {
  213. var number = cards.Count;
  214. for (int i = 0; i < number; i++)
  215. {
  216. var toshuffle = cards[i];
  217. var newplace = random.Next(number);
  218. cards[i] = cards[newplace];
  219. cards[newplace] = toshuffle;
  220. }
  221. }
  222. }
  223. }