PageRenderTime 48ms CodeModel.GetById 18ms app.highlight 10ms RepoModel.GetById 1ms app.codeStats 0ms

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