Example #1
0
        /// <summary>
        /// Modifies this board in-place by shifting those cards that can be shifted or merged in the specified direction.
        /// </summary>
        /// <param name="newCardCells">The possible locations for a new card will be added to this array.</param>
        /// <returns>Whether anything was able to be shifted.</returns>
        public unsafe bool ShiftInPlace(ShiftDirection dir, IntVector2D *newCardCells)
        {
            ulong oldBoard = _board;

            switch (dir)
            {
            case ShiftDirection.Left:
                ShiftLeft(newCardCells);
                break;

            case ShiftDirection.Right:
                ShiftRight(newCardCells);
                break;

            case ShiftDirection.Up:
                ShiftUp(newCardCells);
                break;

            case ShiftDirection.Down:
                ShiftDown(newCardCells);
                break;

            default:
                throw new NotSupportedException("Unknown ShiftDirection '" + dir + "'.");
            }
            return(oldBoard != _board);
        }
Example #2
0
        /// <summary>
        /// Shifts this board in-place down.
        /// </summary>
        private unsafe void ShiftDown(IntVector2D *newCardCells)
        {
            {
                ulong prevBoard = _board;

                ulong cell1Index = (_board & MASK_0_3) >> SHIFT_0_3;
                ulong cell2Index = (_board & MASK_0_2) >> SHIFT_0_2;

                ulong arrayLookup = cell2Index | (cell1Index << 4);
                cell1Index = DEST_SHIFT_RESULTS[arrayLookup];
                cell2Index = SOURCE_SHIFT_RESULTS[arrayLookup];

                ulong cell3Index = (_board & MASK_0_1) >> SHIFT_0_1;
                arrayLookup = cell3Index | (cell2Index << 4);
                cell2Index  = DEST_SHIFT_RESULTS[arrayLookup];
                cell3Index  = SOURCE_SHIFT_RESULTS[arrayLookup];

                ulong cell4Index = (_board & MASK_0_0) >> SHIFT_0_0;
                arrayLookup = cell4Index | (cell3Index << 4);
                cell3Index  = DEST_SHIFT_RESULTS[arrayLookup];
                cell4Index  = SOURCE_SHIFT_RESULTS[arrayLookup];

                _board = (_board & ~(MASK_0_3 | MASK_0_2 | MASK_0_1 | MASK_0_0)) |
                         (cell1Index << SHIFT_0_3) |
                         (cell2Index << SHIFT_0_2) |
                         (cell3Index << SHIFT_0_1) |
                         (cell4Index << SHIFT_0_0);
                if (prevBoard != _board)
                {
                    newCardCells[0] = new IntVector2D(0, 0);
                }
                else
                {
                    newCardCells[0] = new IntVector2D(-1, -1);
                }
            }

            {
                ulong prevBoard = _board;

                ulong cell1Index = (_board & MASK_1_3) >> SHIFT_1_3;
                ulong cell2Index = (_board & MASK_1_2) >> SHIFT_1_2;

                ulong arrayLookup = cell2Index | (cell1Index << 4);
                cell1Index = DEST_SHIFT_RESULTS[arrayLookup];
                cell2Index = SOURCE_SHIFT_RESULTS[arrayLookup];

                ulong cell3Index = (_board & MASK_1_1) >> SHIFT_1_1;
                arrayLookup = cell3Index | (cell2Index << 4);
                cell2Index  = DEST_SHIFT_RESULTS[arrayLookup];
                cell3Index  = SOURCE_SHIFT_RESULTS[arrayLookup];

                ulong cell4Index = (_board & MASK_1_0) >> SHIFT_1_0;
                arrayLookup = cell4Index | (cell3Index << 4);
                cell3Index  = DEST_SHIFT_RESULTS[arrayLookup];
                cell4Index  = SOURCE_SHIFT_RESULTS[arrayLookup];

                _board = (_board & ~(MASK_1_3 | MASK_1_2 | MASK_1_1 | MASK_1_0)) |
                         (cell1Index << SHIFT_1_3) |
                         (cell2Index << SHIFT_1_2) |
                         (cell3Index << SHIFT_1_1) |
                         (cell4Index << SHIFT_1_0);
                if (prevBoard != _board)
                {
                    newCardCells[1] = new IntVector2D(1, 0);
                }
                else
                {
                    newCardCells[1] = new IntVector2D(-1, -1);
                }
            }

            {
                ulong prevBoard = _board;

                ulong cell1Index = (_board & MASK_2_3) >> SHIFT_2_3;
                ulong cell2Index = (_board & MASK_2_2) >> SHIFT_2_2;

                ulong arrayLookup = cell2Index | (cell1Index << 4);
                cell1Index = DEST_SHIFT_RESULTS[arrayLookup];
                cell2Index = SOURCE_SHIFT_RESULTS[arrayLookup];

                ulong cell3Index = (_board & MASK_2_1) >> SHIFT_2_1;
                arrayLookup = cell3Index | (cell2Index << 4);
                cell2Index  = DEST_SHIFT_RESULTS[arrayLookup];
                cell3Index  = SOURCE_SHIFT_RESULTS[arrayLookup];

                ulong cell4Index = (_board & MASK_2_0) >> SHIFT_2_0;
                arrayLookup = cell4Index | (cell3Index << 4);
                cell3Index  = DEST_SHIFT_RESULTS[arrayLookup];
                cell4Index  = SOURCE_SHIFT_RESULTS[arrayLookup];

                _board = (_board & ~(MASK_2_3 | MASK_2_2 | MASK_2_1 | MASK_2_0)) |
                         (cell1Index << SHIFT_2_3) |
                         (cell2Index << SHIFT_2_2) |
                         (cell3Index << SHIFT_2_1) |
                         (cell4Index << SHIFT_2_0);
                if (prevBoard != _board)
                {
                    newCardCells[2] = new IntVector2D(2, 0);
                }
                else
                {
                    newCardCells[2] = new IntVector2D(-1, -1);
                }
            }

            {
                ulong prevBoard = _board;

                ulong cell1Index = (_board & MASK_3_3) >> SHIFT_3_3;
                ulong cell2Index = (_board & MASK_3_2) >> SHIFT_3_2;

                ulong arrayLookup = cell2Index | (cell1Index << 4);
                cell1Index = DEST_SHIFT_RESULTS[arrayLookup];
                cell2Index = SOURCE_SHIFT_RESULTS[arrayLookup];

                ulong cell3Index = (_board & MASK_3_1) >> SHIFT_3_1;
                arrayLookup = cell3Index | (cell2Index << 4);
                cell2Index  = DEST_SHIFT_RESULTS[arrayLookup];
                cell3Index  = SOURCE_SHIFT_RESULTS[arrayLookup];

                ulong cell4Index = (_board & MASK_3_0) >> SHIFT_3_0;
                arrayLookup = cell4Index | (cell3Index << 4);
                cell3Index  = DEST_SHIFT_RESULTS[arrayLookup];
                cell4Index  = SOURCE_SHIFT_RESULTS[arrayLookup];

                _board = (_board & ~(MASK_3_3 | MASK_3_2 | MASK_3_1 | MASK_3_0)) |
                         (cell1Index << SHIFT_3_3) |
                         (cell2Index << SHIFT_3_2) |
                         (cell3Index << SHIFT_3_1) |
                         (cell4Index << SHIFT_3_0);
                if (prevBoard != _board)
                {
                    newCardCells[3] = new IntVector2D(3, 0);
                }
                else
                {
                    newCardCells[3] = new IntVector2D(-1, -1);
                }
            }
        }
Example #3
0
        /// <summary>
        /// Returns whether the specified ShiftDirection is valid for the specified game.
        /// </summary>
        private unsafe static bool TestShiftDirection(FastBoard board, ShiftDirection dir)
        {
            IntVector2D *newCardCells = stackalloc IntVector2D[4];

            return(board.ShiftInPlace(dir, newCardCells));
        }
        /// <summary>
        /// Returns the quality value for shifting the specified board in the specified direction.
        /// Returns null if shifting in that direction is not possible.
        /// </summary>
        private unsafe float?EvaluateMoveForBoard(FastBoard board, FastDeck deck, ulong knownNextCardIndex, ShiftDirection dir, int recursionsLeft, ref long movesEvaluated)
        {
            FastBoard    shiftedBoard = board;
            IntVector2D *newCardCells = stackalloc IntVector2D[4];

            if (shiftedBoard.ShiftInPlace(dir, newCardCells))
            {
                float totalQuality = 0;
                float totalWeight  = 0;

                if (knownNextCardIndex == ulong.MaxValue)                // Special value for bonus card.
                {
                    ByteList12 indexes = new ByteList12();
                    Game.GetPossibleBonusCardIndexes(board.GetMaxCardIndex(), ref indexes);
                    for (int i = 0; i < indexes.Count; i++)
                    {
                        ulong cardIndex = indexes.Items[i];
                        for (int j = 0; j < 4; j++)
                        {
                            IntVector2D cell = newCardCells[j];
                            if (cell.X < 0)
                            {
                                continue;
                            }

                            FastBoard newBoard = shiftedBoard;
                            newBoard.SetCardIndex(cell, cardIndex);

                            float quality;
                            if (recursionsLeft == 0 || GetBestMoveForBoard(newBoard, deck, 0, recursionsLeft - 1, out quality, ref movesEvaluated) == null)
                            {
                                quality = _evaluator(newBoard);
                                movesEvaluated++;
                            }

                            totalQuality += quality;
                            totalWeight  += 1;
                        }
                    }
                }
                else if (knownNextCardIndex > 0)
                {
                    FastDeck newDeck = deck;
                    newDeck.Remove(knownNextCardIndex);
                    for (int i = 0; i < 4; i++)
                    {
                        IntVector2D cell = newCardCells[i];
                        if (cell.X < 0)
                        {
                            continue;
                        }

                        FastBoard newBoard = shiftedBoard;
                        newBoard.SetCardIndex(cell, knownNextCardIndex);

                        float quality;
                        if (recursionsLeft == 0 || GetBestMoveForBoard(newBoard, newDeck, 0, recursionsLeft - 1, out quality, ref movesEvaluated) == null)
                        {
                            quality = _evaluator(newBoard);
                            movesEvaluated++;
                        }

                        totalQuality += quality;
                        totalWeight  += 1;
                    }
                }
                else if (_moveSearchDepth - recursionsLeft - 1 < _cardCountDepth)
                {
                    if (deck.Ones > 0)
                    {
                        FastDeck newDeck = deck;
                        newDeck.RemoveOne();
                        for (int i = 0; i < 4; i++)
                        {
                            IntVector2D cell = newCardCells[i];
                            if (cell.X < 0)
                            {
                                continue;
                            }

                            FastBoard newBoard = shiftedBoard;
                            newBoard.SetCardIndex(cell, 1);

                            float quality;
                            if (recursionsLeft == 0 || GetBestMoveForBoard(newBoard, newDeck, 0, recursionsLeft - 1, out quality, ref movesEvaluated) == null)
                            {
                                quality = _evaluator(newBoard);
                                movesEvaluated++;
                            }

                            totalQuality += quality * deck.Ones;
                            totalWeight  += deck.Ones;
                        }
                    }

                    if (deck.Twos > 0)
                    {
                        FastDeck newDeck = deck;
                        newDeck.RemoveTwo();
                        for (int i = 0; i < 4; i++)
                        {
                            IntVector2D cell = newCardCells[i];
                            if (cell.X < 0)
                            {
                                continue;
                            }

                            FastBoard newBoard = shiftedBoard;
                            newBoard.SetCardIndex(cell, 2);

                            float quality;
                            if (recursionsLeft == 0 || GetBestMoveForBoard(newBoard, newDeck, 0, recursionsLeft - 1, out quality, ref movesEvaluated) == null)
                            {
                                quality = _evaluator(newBoard);
                                movesEvaluated++;
                            }

                            totalQuality += quality * deck.Twos;
                            totalWeight  += deck.Twos;
                        }
                    }

                    if (deck.Threes > 0)
                    {
                        FastDeck newDeck = deck;
                        newDeck.RemoveThree();
                        for (int i = 0; i < 4; i++)
                        {
                            IntVector2D cell = newCardCells[i];
                            if (cell.X < 0)
                            {
                                continue;
                            }

                            FastBoard newBoard = shiftedBoard;
                            newBoard.SetCardIndex(cell, 3);

                            float quality;
                            if (recursionsLeft == 0 || GetBestMoveForBoard(newBoard, newDeck, 0, recursionsLeft - 1, out quality, ref movesEvaluated) == null)
                            {
                                quality = _evaluator(newBoard);
                                movesEvaluated++;
                            }

                            totalQuality += quality * deck.Threes;
                            totalWeight  += deck.Threes;
                        }
                    }

                    // Note that we're not taking the chance of getting a bonus card into consideration. That would be way too expensive at not much benefit.
                }
                else
                {
                    float quality;
                    if (recursionsLeft == 0 || GetBestMoveForBoard(shiftedBoard, deck, 0, recursionsLeft - 1, out quality, ref movesEvaluated) == null)
                    {
                        quality = _evaluator(shiftedBoard);
                        movesEvaluated++;
                    }

                    totalQuality += quality;
                    totalWeight  += 1;
                }

                return(totalQuality / totalWeight);
            }
            else
            {
                return(null);
            }
        }