コード例 #1
0
    static PrecomputedMoveData()
    {
        BitsBoard threats;

        for (byte b = 0; b < NUM_HEXES; b++)
        {
            var start = FastIndex.FromByte(b);
            kingMoves[b]   = GenerateAllPossibleKingMoves(start).ToArray();
            knightMoves[b] = GenerateAllPossibleKnightMoves(start).ToArray();
            squireMoves[b] = GenerateAllPossibleSquireMoves(start).ToArray();
            rookRays[b]    = GenerateAllPossibleRookRays(start).ToArray();
            bishopRays[b]  = GenerateAllPossibleBishopRays(start).ToArray();

            threats = new BitsBoard();
            foreach (var move in kingMoves[b])
            {
                threats[move] = true;
            }
            kingThreats[b] = threats;

            threats = new BitsBoard();
            foreach (var move in squireMoves[b])
            {
                threats[move] = true;
            }
            squireThreats[b] = threats;

            threats = new BitsBoard();
            foreach (var move in knightMoves[b])
            {
                threats[move] = true;
            }
            knightThreats[b] = threats;
        }
    }
コード例 #2
0
ファイル: FastMove.cs プロジェクト: terite/HexChess
 public FastMove(FastIndex start, FastIndex target, MoveType moveType, FastPiece promoteTo)
 {
     this.start     = start;
     this.target    = target;
     this.moveType  = moveType;
     this.promoteTo = promoteTo;
 }
コード例 #3
0
    public void Neighbor_TryGetNeighbor_Self()
    {
        FastIndex hex = Indexes.A1;

        hex.TryGetNeighbor(HexNeighborDirection.Up, out hex);
        Assert.AreEqual(Indexes.A2, hex);
    }
コード例 #4
0
    static IEnumerable <FastIndex> GenerateAllPossibleSquireMoves(FastIndex start)
    {
        FastIndex InBounds(FastIndex idx1, FastIndex idx2)
        {
            return(idx1.IsInBounds ? idx1 : idx2);
        }

        var possibleMoves = new FastIndex[]
        {
            start[HexNeighborDirection.Up][HexNeighborDirection.UpLeft],
            start[HexNeighborDirection.UpRight][HexNeighborDirection.Up],
            InBounds(start[HexNeighborDirection.DownRight][HexNeighborDirection.UpRight],
                     start[HexNeighborDirection.UpRight][HexNeighborDirection.DownRight]),
            start[HexNeighborDirection.Down][HexNeighborDirection.DownRight],
            start[HexNeighborDirection.DownLeft][HexNeighborDirection.Down],
            InBounds(start[HexNeighborDirection.UpLeft][HexNeighborDirection.DownLeft],
                     start[HexNeighborDirection.DownLeft][HexNeighborDirection.UpLeft]),
        };

        foreach (var index in possibleMoves)
        {
            if (index.IsInBounds)
            {
                yield return(index);
            }
        }
    }
コード例 #5
0
ファイル: FastMove.cs プロジェクト: terite/HexChess
 public FastMove(HexAIMove move)
 {
     this.start     = FastIndex.FromByte(move.start.ToByte());
     this.target    = FastIndex.FromByte(move.target.ToByte());
     this.moveType  = move.moveType;
     this.promoteTo = move.promoteTo.ToFastPiece();
 }
コード例 #6
0
ファイル: FastMove.cs プロジェクト: terite/HexChess
 public FastMove(Index start, Index target, MoveType moveType, FastPiece promoteTo)
 {
     this.start     = FastIndex.FromByte(start.ToByte());
     this.target    = FastIndex.FromByte(target.ToByte());
     this.moveType  = moveType;
     this.promoteTo = promoteTo;
 }
コード例 #7
0
    static IEnumerable <FastIndex> GenerateAllPossibleKnightMoves(FastIndex start)
    {
        var possibleMoves = new FastIndex[]
        {
            start[HexNeighborDirection.Up][HexNeighborDirection.Up][HexNeighborDirection.UpLeft],
            start[HexNeighborDirection.Up][HexNeighborDirection.Up][HexNeighborDirection.UpRight],
            start[HexNeighborDirection.UpRight][HexNeighborDirection.UpRight][HexNeighborDirection.Up],
            start[HexNeighborDirection.UpRight][HexNeighborDirection.DownRight][HexNeighborDirection.UpRight],
            start[HexNeighborDirection.DownRight][HexNeighborDirection.UpRight][HexNeighborDirection.DownRight],
            start[HexNeighborDirection.DownRight][HexNeighborDirection.DownRight][HexNeighborDirection.Down],
            start[HexNeighborDirection.Down][HexNeighborDirection.Down][HexNeighborDirection.DownRight],
            start[HexNeighborDirection.Down][HexNeighborDirection.Down][HexNeighborDirection.DownLeft],
            start[HexNeighborDirection.DownLeft][HexNeighborDirection.DownLeft][HexNeighborDirection.Down],
            start[HexNeighborDirection.DownLeft][HexNeighborDirection.UpLeft][HexNeighborDirection.DownLeft],
            start[HexNeighborDirection.UpLeft][HexNeighborDirection.DownLeft][HexNeighborDirection.UpLeft],
            start[HexNeighborDirection.UpLeft][HexNeighborDirection.UpLeft][HexNeighborDirection.Up],
        };

        foreach (var index in possibleMoves)
        {
            if (index.IsInBounds)
            {
                yield return(index);
            }
        }
    }
コード例 #8
0
 public static void AddAllPossibleBishopMoves(List <FastMove> moves, FastIndex start, Team team, FastBoardNode boardNode, bool generateQuiet = true)
 {
     FastIndex[][] bishopRays = PrecomputedMoveData.bishopRays[start.ToByte()];
     foreach (var ray in bishopRays)
     {
         AddRayMoves(moves, start, team, boardNode, ray, generateQuiet);
     }
 }
コード例 #9
0
 public static void AddAllPossibleRookDirectionalMoves(List <FastMove> moves, FastIndex start, Team team, FastBoardNode boardNode, bool generateQuiet = true)
 {
     FastIndex[][] rookRays = PrecomputedMoveData.rookRays[start.ToByte()];
     foreach (var directionalIndexes in rookRays)
     {
         AddRayMoves(moves, start, team, boardNode, directionalIndexes, generateQuiet);
     }
 }
コード例 #10
0
 public void IndexByteAllValuesTest()
 {
     for (byte i = 0; i < 85; i++)
     {
         FastIndex index = FastIndex.FromByte(i);
         Assert.AreEqual(i, index.ToByte());
     }
 }
コード例 #11
0
    static IEnumerable <FastIndex[]> GenerateAllPossibleRookRays(FastIndex start)
    {
        yield return(GenerateRay(start, HexNeighborDirection.Up).ToArray());

        { // Right
            var targets = new List <FastIndex>();
            var target  = start;
            for (int i = 0; i < 20; i++)
            {
                var zigUp   = target[HexNeighborDirection.UpRight][HexNeighborDirection.DownRight];
                var zigDown = target[HexNeighborDirection.DownRight][HexNeighborDirection.UpRight];

                if (zigUp.IsInBounds)
                {
                    target = zigUp;
                }
                else if (zigDown.IsInBounds)
                {
                    target = zigDown;
                }
                else
                {
                    break;
                }

                targets.Add(target);
            }
            yield return(targets.ToArray());
        }

        yield return(GenerateRay(start, HexNeighborDirection.Down).ToArray());

        { // Left
            var target  = start;
            var targets = new List <FastIndex>();
            for (int i = 0; i < 20; i++)
            {
                var zigUp   = target[HexNeighborDirection.UpLeft][HexNeighborDirection.DownLeft];
                var zigDown = target[HexNeighborDirection.DownLeft][HexNeighborDirection.UpLeft];

                if (zigUp.IsInBounds)
                {
                    target = zigUp;
                }
                else if (zigDown.IsInBounds)
                {
                    target = zigDown;
                }
                else
                {
                    break;
                }

                targets.Add(target);
            }
            yield return(targets.ToArray());
        }
    }
コード例 #12
0
    static IEnumerable <FastIndex[]> GenerateAllPossibleBishopRays(FastIndex start)
    {
        yield return(GenerateRay(start, HexNeighborDirection.UpRight).ToArray());

        yield return(GenerateRay(start, HexNeighborDirection.DownRight).ToArray());

        yield return(GenerateRay(start, HexNeighborDirection.DownLeft).ToArray());

        yield return(GenerateRay(start, HexNeighborDirection.UpLeft).ToArray());
    }
コード例 #13
0
 public void FromByteTest()
 {
     Assert.AreEqual(Indexes.A1, FastIndex.FromByte(0));
     Assert.AreEqual(Indexes.E5, FastIndex.FromByte(40));
     Assert.AreEqual(Indexes.I9, FastIndex.FromByte(80));
     Assert.AreEqual(Indexes.B10, FastIndex.FromByte(81));
     Assert.AreEqual(Indexes.D10, FastIndex.FromByte(82));
     Assert.AreEqual(Indexes.F10, FastIndex.FromByte(83));
     Assert.AreEqual(Indexes.H10, FastIndex.FromByte(84));
 }
コード例 #14
0
ファイル: BitsboardTests.cs プロジェクト: spherous/HexChess
    public void ShiftDownInvalidTest()
    {
        var index = new FastIndex(1, 'A');
        var board = new BitsBoard();

        board[index] = true;
        var shifted = board.Shift(HexNeighborDirection.Down);

        AssertNoIndicesSet(shifted);
    }
コード例 #15
0
 static IEnumerable <FastIndex> GenerateAllPossibleKingMoves(FastIndex start)
 {
     foreach (var direction in AllDirections)
     {
         if (start.TryGetNeighbor(direction, out var neighbor))
         {
             yield return(neighbor);
         }
     }
 }
コード例 #16
0
 static PrecomputedMoveData()
 {
     for (byte b = 0; b < NUM_HEXES; b++)
     {
         var start = FastIndex.FromByte(b);
         kingMoves[b]   = GenerateAllPossibleKingMoves(start).ToArray();
         knightMoves[b] = GenerateAllPossibleKnightMoves(start).ToArray();
         squireMoves[b] = GenerateAllPossibleSquireMoves(start).ToArray();
         rookRays[b]    = GenerateAllPossibleRookRays(start).ToArray();
         bishopRays[b]  = GenerateAllPossibleBishopRays(start).ToArray();
     }
 }
コード例 #17
0
ファイル: BitsboardTests.cs プロジェクト: spherous/HexChess
    public void ShiftDownAcrossBoundaryTest()
    {
        var index    = FastIndex.FromByte(64);
        var expected = index[HexNeighborDirection.Down];
        var board    = new BitsBoard();

        board[index] = true;

        var shifted = board.Shift(HexNeighborDirection.Down);

        AssertOnlyIndexSet(shifted, expected);
    }
コード例 #18
0
ファイル: BitsboardTests.cs プロジェクト: spherous/HexChess
    public void ShiftDownTest()
    {
        var index = new FastIndex(5, 'E');
        var board = new BitsBoard();

        board[index] = true;
        AssertOnlyIndexSet(board, index);

        var shifted = board.Shift(HexNeighborDirection.Down);

        AssertOnlyIndexSet(board, index);
        AssertOnlyIndexSet(shifted, new FastIndex(4, 'E'));
    }
コード例 #19
0
ファイル: BitsboardTests.cs プロジェクト: spherous/HexChess
    private void AssertOnlyIndexSet(BitsBoard board, FastIndex expected)
    {
        if (board.Count == 0)
        {
            Assert.Fail($"Expected {expected}, but no indices were set");
        }

        if (!board[expected])
        {
            Assert.Fail($"Index {expected} was not set");
        }

        board[expected] = false;
        AssertNoIndicesSet(board);
    }
コード例 #20
0
    static IEnumerable <FastIndex> GenerateRay(FastIndex start, HexNeighborDirection direction)
    {
        FastIndex target = start;

        for (int j = 0; j < 20; j++)
        {
            target = target[direction];
            if (!target.IsInBounds)
            {
                break;
            }

            yield return(target);
        }
    }
コード例 #21
0
    void AssertBidirectional(FastIndex[][] moves)
    {
        for (byte b = 0; b < moves.Length; b++)
        {
            FastIndex start = FastIndex.FromByte(b);

            foreach (var target in moves[b])
            {
                var fromTarget = moves[target.HexId];
                if (Array.IndexOf(fromTarget, start) < 0)
                {
                    Assert.Fail($"{start} can attack {target}, but {target} cannot attack {start}");
                }
            }
        }
    }
コード例 #22
0
 public static void AddAllPossibleKnightMoves(List <FastMove> moves, FastIndex start, Team team, FastBoardNode boardNode, bool generateQuiet = true)
 {
     foreach (var target in PrecomputedMoveData.knightMoves[start.ToByte()])
     {
         if (boardNode.TryGetPiece(target, out (Team team, FastPiece piece)occupier))
         {
             if (occupier.team != team)
             {
                 moves.Add(new FastMove(start, target, MoveType.Attack));
             }
         }
         else if (generateQuiet)
         {
             moves.Add(new FastMove(start, target, MoveType.Move));
         }
     }
 }
コード例 #23
0
ファイル: BitsboardTests.cs プロジェクト: spherous/HexChess
    private void AssertNoIndicesSet(BitsBoard board)
    {
        if (board.Count > 0)
        {
            System.Collections.Generic.List <string> setIndices = new System.Collections.Generic.List <string>();
            for (byte b = 0; b < 85; b++)
            {
                if (board[b])
                {
                    UnityEngine.Debug.LogError($"Byte {b} unexpectedly set");
                    setIndices.Add(FastIndex.FromByte(b).ToString());
                }
            }

            Assert.Fail($"Unexpectedly set indices: {string.Join(", ", setIndices)}");
        }
        Assert.AreEqual(0, board.Count);
    }
コード例 #24
0
    public static void AddAllPossibleRookMoves(List <FastMove> moves, FastIndex start, Team team, FastBoardNode boardNode, bool generateQuiet = true)
    {
        AddAllPossibleRookDirectionalMoves(moves, start, team, boardNode, generateQuiet);

        if (generateQuiet)
        {
            foreach (var target in PrecomputedMoveData.kingMoves[start.ToByte()])
            {
                if (boardNode.TryGetPiece(target, out (Team team, FastPiece piece)occupier))
                {
                    if (occupier.team == team && Contains(DefendableTypes, occupier.piece))
                    {
                        moves.Add(new FastMove(start, target, MoveType.Defend));
                    }
                }
            }
        }
    }
コード例 #25
0
ファイル: BitsboardTests.cs プロジェクト: spherous/HexChess
    public void ShiftComprehensivetest([ValueSource(typeof(PrecomputedMoveData), nameof(PrecomputedMoveData.AllDirections))] HexNeighborDirection direction)
    {
        for (byte b = 0; b < 85; b++)
        {
            FastIndex index    = FastIndex.FromByte(b);
            var       original = new BitsBoard();
            original[index] = true;

            var expected = index[direction];
            var shifted  = original.Shift(direction);
            if (expected.IsInBounds)
            {
                AssertOnlyIndexSet(shifted, expected);
            }
            else
            {
                AssertNoIndicesSet(shifted);
            }
        }
    }
コード例 #26
0
 static void AddRayMoves(List <FastMove> moves, FastIndex start, Team team, FastBoardNode boardNode, FastIndex[] ray, bool generateQuiet = true)
 {
     foreach (var target in ray)
     {
         var occupant = boardNode[target];
         if (occupant.team == Team.None)
         {
             if (generateQuiet)
             {
                 moves.Add(new FastMove(start, target, MoveType.Move)); // empty
             }
         }
         else
         {
             if (occupant.team != team)
             {
                 moves.Add(new FastMove(start, target, MoveType.Attack)); // rip and tear
             }
             break;
         }
     }
 }
コード例 #27
0
    public static void AddAllPossibleMoves(List <FastMove> moves, FastIndex start, FastPiece piece, Team team, FastBoardNode boardNode, bool generateQuiet = true)
    {
        switch (piece)
        {
        case FastPiece.King:
            AddAllPossibleKingMoves(moves, start, team, boardNode, generateQuiet);
            return;

        case FastPiece.Queen:
            AddAllPossibleQueenMoves(moves, start, team, boardNode, generateQuiet);
            return;

        case FastPiece.Rook:
            AddAllPossibleRookMoves(moves, start, team, boardNode, generateQuiet);
            return;

        case FastPiece.Knight:
            AddAllPossibleKnightMoves(moves, start, team, boardNode, generateQuiet);
            return;

        case FastPiece.Bishop:
            AddAllPossibleBishopMoves(moves, start, team, boardNode, generateQuiet);
            return;

        case FastPiece.Squire:
            AddAllPossibleSquireMoves(moves, start, team, boardNode, generateQuiet);
            return;

        case FastPiece.Pawn:
            AddAllPossiblePawnMoves(moves, start, team, boardNode, generateQuiet);
            return;

        default:
            throw new ArgumentException($"Unhandled piece type: {piece}", nameof(piece));
        }
    }
コード例 #28
0
    public static void AddAllPossiblePawnMoves(List <FastMove> moves, FastIndex start, Team team, FastBoardNode boardNode, bool generateQuiet = true)
    {
        Team enemy = team.Enemy();

        HexNeighborDirection leftAttackDir   = HexNeighborDirection.UpLeft;
        HexNeighborDirection leftPassantDir  = HexNeighborDirection.DownLeft;
        HexNeighborDirection rightAttackDir  = HexNeighborDirection.UpRight;
        HexNeighborDirection rightPassantDir = HexNeighborDirection.DownRight;
        HexNeighborDirection forwardDir      = HexNeighborDirection.Up;

        if (team != Team.White)
        {
            leftAttackDir   = HexNeighborDirection.DownLeft;
            leftPassantDir  = HexNeighborDirection.UpLeft;
            rightAttackDir  = HexNeighborDirection.DownRight;
            rightPassantDir = HexNeighborDirection.UpRight;
            forwardDir      = HexNeighborDirection.Down;
        }

        if (start.TryGetNeighbor(leftAttackDir, out var leftAttack))
        {
            var leftVictim = boardNode[leftAttack];
            if (leftVictim.team == enemy)
            {
                moves.Add(new FastMove(start, leftAttack, MoveType.Attack));
            }
            if (leftVictim.team == Team.None)
            {
                if (start.TryGetNeighbor(leftPassantDir, out var leftPassant) && boardNode.PassantableIndex == leftPassant)
                {
                    moves.Add(new FastMove(start, leftAttack, MoveType.EnPassant));
                }
            }
        }

        if (start.TryGetNeighbor(rightAttackDir, out var rightAttack))
        {
            var rightVictim = boardNode[rightAttack];
            if (rightVictim.team == enemy)
            {
                moves.Add(new FastMove(start, rightAttack, MoveType.Attack));
            }
            if (rightVictim.team == Team.None)
            {
                if (start.TryGetNeighbor(rightPassantDir, out var rightPassant) && boardNode.PassantableIndex == rightPassant)
                {
                    moves.Add(new FastMove(start, rightAttack, MoveType.EnPassant));
                }
            }
        }

        if (!generateQuiet)
        {
            return;
        }

        // One forward
        bool hasForward = start.TryGetNeighbor(forwardDir, out var forward);

        if (hasForward && !boardNode.IsOccupied(forward))
        {
            if (forward.TryGetNeighbor(forwardDir, out var twoForward))
            {
                moves.Add(new FastMove(start, forward, MoveType.Move));

                // Two forward on 1st move
                if (PawnIsAtStart(team, (Index)start) && !boardNode.IsOccupied(twoForward))
                {
                    moves.Add(new FastMove(start, twoForward, MoveType.Move));
                }
            }
            else
            {
                // if two squares forward doesn't exist, that means we're on the last rank for this pawn.
                moves.Add(new FastMove(start, forward, MoveType.Move, FastPiece.Queen));
                moves.Add(new FastMove(start, forward, MoveType.Move, FastPiece.Rook));
                moves.Add(new FastMove(start, forward, MoveType.Move, FastPiece.Knight));
                moves.Add(new FastMove(start, forward, MoveType.Move, FastPiece.Squire));
            }
        }
    }
コード例 #29
0
 public static void AddAllPossibleQueenMoves(List <FastMove> moves, FastIndex start, Team team, FastBoardNode boardNode, bool generateQuiet = true)
 {
     AddAllPossibleRookDirectionalMoves(moves, start, team, boardNode, generateQuiet);
     AddAllPossibleBishopMoves(moves, start, team, boardNode, generateQuiet);
 }