Пример #1
0
        private List <ProbabilityLine> CrunchByMineCount(List <ProbabilityLine> target)
        {
            //if (target.Count == 0) {
            //    return target;
            //}

            // sort the solutions by number of mines
            target.Sort();

            List <ProbabilityLine> result = new List <ProbabilityLine>();

            int             mc  = target[0].GetMineCount();
            ProbabilityLine npl = new ProbabilityLine(this.boxes.Count);

            npl.SetMineCount(mc);

            foreach (ProbabilityLine pl in target)
            {
                if (pl.GetMineCount() != mc)
                {
                    result.Add(npl);
                    mc  = pl.GetMineCount();
                    npl = new ProbabilityLine(this.boxes.Count);
                    npl.SetMineCount(mc);
                }
                MergeLineProbabilities(npl, pl);
            }

            //if (npl.GetMineCount() >= minTotalMines) {
            result.Add(npl);
            //}
            //information.Write("Probability line has " + npl.GetMineCount() + " mines");

            return(result);
        }
Пример #2
0
        // calculate a probability for each un-revealed tile on the board
        public void Process()
        {
            this.mask = new bool[this.boxes.Count];

            // create an initial solution of no mines anywhere
            ProbabilityLine held = new ProbabilityLine(this.boxes.Count);

            held.SetSolutionCount(1);
            this.heldProbs.Add(held);

            // add an empty probability line to get us started
            this.workingProbs.Add(new ProbabilityLine(this.boxes.Count));

            NextWitness nextWitness = FindFirstWitness();

            while (nextWitness != null)
            {
                // mark the new boxes as processed - which they will be soon
                foreach (Box box in nextWitness.GetNewBoxes())
                {
                    this.mask[box.GetUID()] = true;
                }

                this.workingProbs = MergeProbabilities(nextWitness);

                nextWitness = FindNextWitness(nextWitness);
            }

            CalculateBoxProbabilities();
        }
Пример #3
0
        // this is used to recursively place the missing Mines into the available boxes for the probability line
        private List <ProbabilityLine> DistributeMissingMines(ProbabilityLine pl, NextWitness nw, int missingMines, int index)
        {
            //console.log("Distributing " + missingMines + " missing mines to box " + nw.newBoxes[index].uid);

            this.recursions++;
            if (this.recursions % 100000 == 0)
            {
                information.Write("Solution Counter recursision = " + recursions);
            }

            List <ProbabilityLine> result = new List <ProbabilityLine>();

            // if there is only one box left to put the missing mines we have reach the end of this branch of recursion
            if (nw.GetNewBoxes().Count - index == 1)
            {
                // if there are too many for this box then the probability can't be valid
                if (nw.GetNewBoxes()[index].GetMaxMines() < missingMines)
                {
                    //console.log("Abandon (1)");
                    return(result);
                }
                // if there are too few for this box then the probability can't be valid
                if (nw.GetNewBoxes()[index].GetMinMines() > missingMines)
                {
                    //console.log("Abandon (2)");
                    return(result);
                }
                // if there are too many for this game then the probability can't be valid
                if (pl.GetMineCount() + missingMines > this.maxTotalMines)
                {
                    //console.log("Abandon (3)");
                    return(result);
                }

                // otherwise place the mines in the probability line
                pl.SetMineBoxCount(nw.GetNewBoxes()[index].GetUID(), new BigInteger(missingMines));

                pl.SetMineCount(pl.GetMineCount() + missingMines);
                result.Add(pl);
                //console.log("Distribute missing mines line after " + pl.mineBoxCount);
                return(result);
            }


            // this is the recursion
            int maxToPlace = Math.Min(nw.GetNewBoxes()[index].GetMaxMines(), missingMines);

            for (int i = nw.GetNewBoxes()[index].GetMinMines(); i <= maxToPlace; i++)
            {
                ProbabilityLine npl = ExtendProbabilityLine(pl, nw.GetNewBoxes()[index], i);

                List <ProbabilityLine> r1 = DistributeMissingMines(npl, nw, missingMines - i, index + 1);
                result.AddRange(r1);
            }

            return(result);
        }
Пример #4
0
        // counts the number of mines already placed
        private BigInteger CountPlacedMines(ProbabilityLine pl, NextWitness nw)
        {
            BigInteger result = 0;

            foreach (Box b in nw.GetOldBoxes())
            {
                result = result + pl.GetMineBoxCount(b.GetUID());
            }

            return(result);
        }
Пример #5
0
        // create a new probability line by taking the old and adding the mines to the new Box
        private ProbabilityLine ExtendProbabilityLine(ProbabilityLine pl, Box newBox, int mines)
        {
            //console.log("Extended probability line: Adding " + mines + " mines to box " + newBox.uid);
            //console.log("Extended probability line before" + pl.mineBoxCount);

            ProbabilityLine result = new ProbabilityLine(this.boxes.Count);

            result.SetMineCount(pl.GetMineCount() + mines);

            result.CopyMineBoxCount(pl);

            result.SetMineBoxCount(newBox.GetUID(), new BigInteger(mines));

            //console.log("Extended probability line after " + result.mineBoxCount);

            return(result);
        }
Пример #6
0
        // calculate how many ways this solution can be generated and roll them into one
        private void MergeLineProbabilities(ProbabilityLine npl, ProbabilityLine pl)
        {
            BigInteger solutions = 1;

            for (int i = 0; i < this.boxes.Count; i++)
            {
                solutions = solutions * (BigInteger)SMALL_COMBINATIONS[this.boxes[i].GetTiles().Count][(int)pl.GetMineBoxCount(i)];
            }

            npl.SetSolutionCount(npl.GetSolutionCount() + solutions);

            for (int i = 0; i < this.boxes.Count; i++)
            {
                if (this.mask[i])    // if this box has been involved in this solution - if we don't do this the hash gets corrupted by boxes = 0 mines because they weren't part of this edge
                {
                    npl.SetMineBoxCount(i, npl.GetMineBoxCount(i) + pl.GetMineBoxCount(i) * solutions);
                }
            }
        }
Пример #7
0
        // take a look at what edges we have and show some information - trying to get some ideas on how we can get faster good guesses
        private void AnalyseAllEdges()
        {
            //Console.WriteLine("Number of tiles off the edge " + tilesOffEdge);
            //Console.WriteLine("Number of mines to find " + minesLeft);

            this.edgeMinesMin = 0;
            this.edgeMinesMax = 0;

            foreach (EdgeStore edge in edgeStore)
            {
                int edgeMinMines = edge.data[0].GetMineCount();
                int edgeMaxMines = edge.data[edge.data.Count - 1].GetMineCount();

                edgeMinesMin = edgeMinesMin + edgeMinMines;
                edgeMinesMax = edgeMinesMax + edgeMaxMines;
            }

            information.Write("Min mines on all edges " + edgeMinesMin + ", max " + edgeMinesMax);
            this.edgeMinesMaxLeft = this.edgeMinesMax;  // these values are used  in the merge logic to reduce the number of lines need to keep
            this.edgeMinesMinLeft = this.edgeMinesMin;

            this.mineCountLowerCutoff = this.edgeMinesMin;
            this.mineCountUpperCutoff = Math.Min(this.edgeMinesMax, this.minesLeft);  // can't have more mines than are left

            // comment this out when doing large board analysis
            return;

            // the code below reduces the range of mine count values to just be the 'significant' range

            List <ProbabilityLine> store  = new List <ProbabilityLine>();
            List <ProbabilityLine> store1 = new List <ProbabilityLine>();

            ProbabilityLine init = new ProbabilityLine(0);

            init.SetSolutionCount(1);

            store.Add(init);

            // combine all the edges to determine the relative weights of the mine count
            foreach (EdgeStore edgeDetails in edgeStore)
            {
                foreach (ProbabilityLine pl in edgeDetails.data)
                {
                    BigInteger plSolCount = pl.GetSolutionCount();

                    foreach (ProbabilityLine epl in store)
                    {
                        if (pl.GetMineCount() + epl.GetMineCount() <= this.maxTotalMines)
                        {
                            ProbabilityLine newpl = new ProbabilityLine(0);

                            newpl.SetMineCount(pl.GetMineCount() + epl.GetMineCount());

                            BigInteger eplSolCount = epl.GetSolutionCount();

                            newpl.SetSolutionCount(pl.GetSolutionCount() * eplSolCount);

                            store1.Add(newpl);
                        }
                    }
                }

                store.Clear();

                // sort into mine order
                store1.Sort();

                int             mc  = store1[0].GetMineCount();
                ProbabilityLine npl = new ProbabilityLine(0);
                npl.SetMineCount(mc);

                foreach (ProbabilityLine pl in store1)
                {
                    if (pl.GetMineCount() != mc)
                    {
                        store.Add(npl);
                        mc  = pl.GetMineCount();
                        npl = new ProbabilityLine(0);
                        npl.SetMineCount(mc);
                    }
                    npl.SetSolutionCount(npl.GetSolutionCount() + pl.GetSolutionCount());
                }

                store.Add(npl);
                store1.Clear();
            }

            BigInteger total      = 0;
            int        mineValues = 0;

            foreach (ProbabilityLine pl in store)
            {
                if (pl.GetMineCount() >= this.minTotalMines)                                                       // if the mine count for this solution is less than the minimum it can't be valid
                {
                    BigInteger mult = SolverMain.Calculate(this.minesLeft - pl.GetMineCount(), this.tilesOffEdge); //# of ways the rest of the board can be formed
                    total = total + mult * pl.GetSolutionCount();
                    mineValues++;
                }
            }

            //this.mineCountLowerCutoff = this.edgeMinesMin;
            //this.mineCountUpperCutoff = Math.Min(this.edgeMinesMax, this.minesLeft);  // can't have more mines than are left
            BigInteger soFar = 0;

            foreach (ProbabilityLine pl in store)
            {
                if (pl.GetMineCount() >= this.minTotalMines)                                                       // if the mine count for this solution is less than the minimum it can't be valid
                {
                    BigInteger mult = SolverMain.Calculate(this.minesLeft - pl.GetMineCount(), this.tilesOffEdge); //# of ways the rest of the board can be formed
                    soFar = soFar + mult * pl.GetSolutionCount();
                    double perc = Combination.DivideBigIntegerToDouble(soFar, total, 6) * 100;
                    //Console.WriteLine("Mine count " + pl.GetMineCount() + " has solution count " + pl.GetSolutionCount() + " multiplier " + mult + " running % " + perc);
                    //Console.WriteLine("Mine count " + pl.GetMineCount() + " has solution count " + pl.GetSolutionCount() + " has running % " + perc);

                    if (mineValues > 30 && perc < 2.5)
                    {
                        this.mineCountLowerCutoff = pl.GetMineCount();
                    }

                    if (mineValues > 30 && perc > 97.5)
                    {
                        this.mineCountUpperCutoff = pl.GetMineCount();
                        break;
                    }
                }
            }

            information.Write("Significant range " + this.mineCountLowerCutoff + " - " + this.mineCountUpperCutoff);
            //this.edgeMinesMaxLeft = this.edgeMinesMax;
            //this.edgeMinesMinLeft = this.edgeMinesMin;


            return;

            // below here are experimental ideas on getting a good guess on very large boards

            int midRangeAllMines = (this.mineCountLowerCutoff + this.mineCountUpperCutoff) / 2;

            BigInteger[] tally       = new BigInteger[boxes.Count];
            double[]     probability = new double[boxes.Count];


            foreach (EdgeStore edgeDetails in edgeStore)
            {
                int sizeRangeEdgeMines = (edgeDetails.data[edgeDetails.data.Count - 1].GetMineCount() - edgeDetails.data[0].GetMineCount()) / 2;

                int start = (this.mineCountLowerCutoff - edgeDetails.data[0].GetMineCount() + this.mineCountUpperCutoff - edgeDetails.data[edgeDetails.data.Count - 1].GetMineCount()) / 2;
                //int start = midRangeAllMines - sizeRangeEdgeMines;

                //BigInteger mult = Combination.Calculate(this.minesLeft - start, this.tilesOffEdge);
                BigInteger totalTally = 0;

                foreach (ProbabilityLine pl in edgeDetails.data)
                {
                    BigInteger mult = Combination.Calculate(this.minesLeft - start - pl.GetMineCount(), this.tilesOffEdge);
                    totalTally += mult * pl.GetSolutionCount();
                    for (int i = 0; i < boxes.Count; i++)
                    {
                        if (edgeDetails.mask[i])
                        {
                            BigInteger work = pl.GetMineBoxCount(i) * mult;
                            tally[i] += work;
                        }
                    }

                    //mult = mult * (this.tilesOffEdge - start) / (start + 1);
                    //start++;
                }

                for (int i = 0; i < boxes.Count; i++)
                {
                    if (edgeDetails.mask[i])
                    {
                        probability[i] = Combination.DivideBigIntegerToDouble(tally[i], totalTally, 6) / boxes[i].GetTiles().Count;
                    }
                }

                int minIndex = -1;
                for (int i = 0; i < boxes.Count; i++)
                {
                    if (edgeDetails.mask[i])
                    {
                        if (minIndex == -1 || probability[i] < probability[minIndex])
                        {
                            minIndex = i;
                        }
                    }
                }

                if (minIndex != -1)
                {
                    information.Write("Best guess is " + boxes[minIndex].GetTiles()[0].AsText() + " with " + (1 - probability[minIndex]));
                }
                else
                {
                    information.Write("No Guess found");
                }
            }
        }
Пример #8
0
        // this combines newly generated probabilities with ones we have already stored from other independent sets of witnesses
        private void CombineProbabilities()
        {
            List <ProbabilityLine> result = new List <ProbabilityLine>();

            if (this.workingProbs.Count == 0)
            {
                information.Write("working probabilites list is empty!!");
                return;
            }

            // see if we can find a common divisor
            BigInteger hcd = workingProbs[0].GetSolutionCount();

            foreach (ProbabilityLine pl in workingProbs)
            {
                hcd = BigInteger.GreatestCommonDivisor(hcd, pl.GetSolutionCount());
            }
            foreach (ProbabilityLine pl in heldProbs)
            {
                hcd = BigInteger.GreatestCommonDivisor(hcd, pl.GetSolutionCount());
            }
            information.Write("Greatest Common Divisor is " + hcd);

            solutionCountMultiplier = solutionCountMultiplier * hcd;

            int mineCountMin = workingProbs[0].GetMineCount() + heldProbs[0].GetMineCount();

            // shrink the window
            edgeMinesMinLeft = edgeMinesMinLeft - workingProbs[0].GetMineCount();
            edgeMinesMaxLeft = edgeMinesMaxLeft - workingProbs[workingProbs.Count - 1].GetMineCount();

            foreach (ProbabilityLine pl in workingProbs)
            {
                BigInteger plSolCount = pl.GetSolutionCount() / hcd;

                foreach (ProbabilityLine epl in heldProbs)
                {
                    // if the mine count can never reach the lower cuttoff then ignore it
                    if (pl.GetMineCount() + epl.GetMineCount() + edgeMinesMaxLeft < this.mineCountLowerCutoff)
                    {
                        continue;
                    }

                    // if the mine count will always be pushed beyonf the upper cuttoff then ignore it
                    if (pl.GetMineCount() + epl.GetMineCount() + edgeMinesMinLeft > this.mineCountUpperCutoff)
                    {
                        continue;
                    }

                    ProbabilityLine newpl = new ProbabilityLine(this.boxes.Count);

                    newpl.SetMineCount(pl.GetMineCount() + epl.GetMineCount());

                    BigInteger eplSolCount = epl.GetSolutionCount() / hcd;

                    newpl.SetSolutionCount(pl.GetSolutionCount() * eplSolCount);

                    for (int k = 0; k < this.boxes.Count; k++)
                    {
                        BigInteger w1 = pl.GetMineBoxCount(k) * eplSolCount;
                        BigInteger w2 = epl.GetMineBoxCount(k) * plSolCount;
                        newpl.SetMineBoxCount(k, w1 + w2);
                    }

                    result.Add(newpl);
                }
            }

            //Console.WriteLine("Solution multiplier is " + solutionCountMultiplier);

            this.heldProbs.Clear();

            // if result is empty this is an impossible position
            if (result.Count == 0)
            {
                Console.WriteLine("Impossible position encountered");
                return;
            }

            if (result.Count == 1)
            {
                heldProbs.AddRange(result);
                return;
            }

            // sort into mine order
            result.Sort();

            // and combine them into a single probability line for each mine count
            int             mc  = result[0].GetMineCount();
            ProbabilityLine npl = new ProbabilityLine(this.boxes.Count);

            npl.SetMineCount(mc);

            int startMC = mc;

            foreach (ProbabilityLine pl in result)
            {
                if (pl.GetMineCount() != mc)
                {
                    this.heldProbs.Add(npl);
                    mc  = pl.GetMineCount();
                    npl = new ProbabilityLine(this.boxes.Count);
                    npl.SetMineCount(mc);
                }
                npl.SetSolutionCount(npl.GetSolutionCount() + pl.GetSolutionCount());

                for (int j = 0; j < this.boxes.Count; j++)
                {
                    npl.SetMineBoxCount(j, npl.GetMineBoxCount(j) + pl.GetMineBoxCount(j));
                }
            }

            this.heldProbs.Add(npl);
        }