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; } }
public FastMove(FastIndex start, FastIndex target, MoveType moveType, FastPiece promoteTo) { this.start = start; this.target = target; this.moveType = moveType; this.promoteTo = promoteTo; }
public void Neighbor_TryGetNeighbor_Self() { FastIndex hex = Indexes.A1; hex.TryGetNeighbor(HexNeighborDirection.Up, out hex); Assert.AreEqual(Indexes.A2, hex); }
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); } } }
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(); }
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; }
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); } } }
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); } }
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); } }
public void IndexByteAllValuesTest() { for (byte i = 0; i < 85; i++) { FastIndex index = FastIndex.FromByte(i); Assert.AreEqual(i, index.ToByte()); } }
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()); } }
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()); }
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)); }
public void ShiftDownInvalidTest() { var index = new FastIndex(1, 'A'); var board = new BitsBoard(); board[index] = true; var shifted = board.Shift(HexNeighborDirection.Down); AssertNoIndicesSet(shifted); }
static IEnumerable <FastIndex> GenerateAllPossibleKingMoves(FastIndex start) { foreach (var direction in AllDirections) { if (start.TryGetNeighbor(direction, out var neighbor)) { yield return(neighbor); } } }
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(); } }
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); }
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')); }
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); }
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); } }
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}"); } } } }
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)); } } }
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); }
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)); } } } } }
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); } } }
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; } } }
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)); } }
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)); } } }
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); }