private static int GetStrongestOpponent(IEnumerable <IOpponent> opponents) // Get opponent with lowest pile height { int strongest = -1; int strongestPileHeight = 1000; foreach (IOpponent opponent in opponents) { int pileHeight = BoardHelper.GetPileMaxHeight(opponent.Board); if (pileHeight < strongestPileHeight) { strongestPileHeight = pileHeight; strongest = opponent.PlayerId; } } return(strongest); }
private static int GetWeakestOpponent(IEnumerable <IOpponent> opponents) // Get opponent with highest pile height { int weakest = -1; int weakestPileHeight = -1000; foreach (IOpponent opponent in opponents) { int pileHeight = BoardHelper.GetPileMaxHeight(opponent.Board); if (pileHeight > weakestPileHeight) { weakest = opponent.PlayerId; weakestPileHeight = pileHeight; } } return(weakest); }
private static double EvaluteMove(IBoard board, IPiece piece) { int pieceMinX; int pieceMinY; int pieceMaxX; int pieceMaxY; piece.GetAbsoluteBoundingRectangle(out pieceMinX, out pieceMinY, out pieceMaxX, out pieceMaxY); int totalHeight = BoardHelper.GetTotalCellHeight(board); int completedRows = BoardHelper.GetTotalCompletedRows(board); int totalHoles = Enumerable.Range(1, board.Width).Aggregate(0, (i, i1) => i + BoardHelper.GetBuriedHolesForColumn(board, i1)); int totalBlockades = Enumerable.Range(1, board.Width).Aggregate(0, (i, i1) => i + BoardHelper.GetBlockadesForColumn(board, i1)); int edgeTouchingAnotherBlock = 0; // TODO int edgeTouchingWall = 0; // TODO int edgeTouchingFloor = 0; //double rating = 0; //rating += -0.03 * totalHeight; //rating += -7.5 * totalHoles; //rating += -3.5 * totalBlockades; //rating += 8.0 * completedRows; //rating += 3.0 * edgeTouchingAnotherBlock; //rating += 2.5 * edgeTouchingWall; //rating += 5.0 * edgeTouchingFloor; //double rating = 0; //rating += -3.78 * totalHeight; //rating += -2.31 * totalHoles; //rating += -0.59 * totalBlockades; //rating += 1.6 * completedRows; //rating += 3.97 * edgeTouchingAnotherBlock; //rating += 6.52 * edgeTouchingWall; //rating += 0.65 * edgeTouchingFloor; double rating = 0; rating += -0.868099 * totalHeight; rating += -2.45402 * totalHoles; rating += -0.236702 * totalBlockades; rating += 3.59764 * completedRows; rating += 5.33378 * edgeTouchingAnotherBlock; rating += 8.20521 * edgeTouchingWall; rating += 0.00 * edgeTouchingFloor; return(rating); }
private static int GetStrongestOpponentWithNukeSwitchGravity(IEnumerable <IOpponent> opponents) // Search among opponents which one has a nuke/switch/gravity and the lowest board { int id = -1; int lowestPileHeight = 1000; foreach (IOpponent opponent in opponents) { if (opponent.Board.Cells.Any(x => CellHelper.GetSpecial(x) == Specials.SwitchFields || CellHelper.GetSpecial(x) == Specials.NukeField || CellHelper.GetSpecial(x) == Specials.BlockGravity)) { int pileHeight = BoardHelper.GetPileMaxHeight(opponent.Board); if (pileHeight < lowestPileHeight) { id = opponent.PlayerId; lowestPileHeight = pileHeight; } } } return(id); }
private static int GetStrongestOpponentWithBomb(IEnumerable <IOpponent> opponents) // Search among opponents which one has a bomb and the lowest board { int id = -1; int lowestPileHeight = 1000; foreach (IOpponent opponent in opponents) { if (opponent.Board.Cells.Any(x => CellHelper.IsSpecial(x) && CellHelper.GetSpecial(x) == Specials.BlockBomb)) { int pileHeight = BoardHelper.GetPileMaxHeight(opponent.Board); if (pileHeight < lowestPileHeight) { id = opponent.PlayerId; lowestPileHeight = pileHeight; } } } return(id); }
private static int GetStrongestOpponentWithBombOnBottomLine(IEnumerable <IOpponent> opponents) // Search among opponents which one has a bomb on bottom line and the lowest board { int id = -1; int lowestPileHeight = 1000; foreach (IOpponent opponent in opponents) { for (int x = 1; x <= opponent.Board.Width; x++) { Specials cellSpecial = CellHelper.GetSpecial(opponent.Board[x, 0]); if (cellSpecial == Specials.BlockBomb) { int pileHeight = BoardHelper.GetPileMaxHeight(opponent.Board); if (pileHeight < lowestPileHeight) { id = opponent.PlayerId; lowestPileHeight = pileHeight; } break; } } } return(id); }
public bool GetBestMove(IBoard board, IPiece current, IPiece next, out int bestRotationDelta, out int bestTranslationDelta, out bool rotationBeforeTranslation) { int currentBestTranslationDelta = 0; int currentBestRotationDelta = 0; double currentBestRating = -1.0e+20; // Really bad! //if (current.PosY == board.Height) // TODO: put current totally in board before trying to get best move // current.Translate(0, -1); IBoard tempBoard = board.Clone(); IPiece tempPiece = current.Clone(); //Log.Default.WriteLine(LogLevels.Debug, "Get Best Move for Piece {0} {1}", tempPiece.Value, tempPiece.Index); // Consider all possible rotations for (int trialRotationDelta = 0; trialRotationDelta < current.MaxOrientations; trialRotationDelta++) { // Copy piece tempPiece.CopyFrom(current); // Rotate tempPiece.Rotate(trialRotationDelta); // Get translation range bool isMovePossible; int minDeltaX; int maxDeltaX; BoardHelper.GetAccessibleTranslationsForOrientation(board, tempPiece, out isMovePossible, out minDeltaX, out maxDeltaX); //Log.Default.WriteLine(LogLevels.Debug, "Accessible translation {0} {1} {2} {3} {4} {5} {6}", minDeltaX, maxDeltaX, trialRotationDelta, current.PosX, current.PosY, tempPiece.Value, tempPiece.Index); //StringBuilder sb = new StringBuilder(); //for (int i = 1; i <= tempPiece.TotalCells; i++) //{ // int x, y; // tempPiece.GetCellAbsolutePosition(i, out x, out y); // sb.Append(String.Format("[{0}->{1},{2}]", i, x - tempPiece.PosX, y - tempPiece.PosY)); //} //Log.Log.Default.WriteLine("{0} {1} -> {2} {3}", trialRotationDelta, minDeltaX, maxDeltaX, sb.ToString()); if (isMovePossible) { // Consider all allowed translations for (int trialTranslationDelta = minDeltaX; trialTranslationDelta <= maxDeltaX; trialTranslationDelta++) { // Evaluate this move // Copy piece tempPiece.CopyFrom(current); // Rotate tempPiece.Rotate(trialRotationDelta); // Translate tempPiece.Translate(trialTranslationDelta, 0); // Check if move is acceptable if (board.CheckNoConflict(tempPiece)) { // Copy board tempBoard.CopyFrom(board); // Drop piece tempBoard.DropAndCommit(tempPiece); // Evaluate double trialRating = EvaluteMove(tempBoard, tempPiece); //Log.Log.Default.WriteLine("R:{0:0.0000} P:{1} R:{2} T:{3}", trialRating, trialRotationDelta, trialTranslationDelta); // Check if better than previous best if (trialRating > currentBestRating) { currentBestRating = trialRating; currentBestTranslationDelta = trialTranslationDelta; currentBestRotationDelta = trialRotationDelta; } } } } } // Commit to this move rotationBeforeTranslation = true; bestTranslationDelta = currentBestTranslationDelta; bestRotationDelta = currentBestRotationDelta; // Log.Default.WriteLine(LogLevels.Debug, "{0} {1} {2:0.000}", bestRotationDelta, bestTranslationDelta, currentBestRating); return(true); }
// The following evaluation function was adapted from Pascal code submitted by: // Pierre Dellacherie (France). (E-mail : [email protected]) // // This amazing one-piece algorithm completes an average of roughly 600 000 // rows, and often attains 2 000 000 or 2 500 000 rows. However, the algorithm // sometimes completes as few as 15 000 rows. I am fairly certain that this // is NOT due to statistically abnormal patterns in the falling piece sequence. // // Pierre Dellacherie corresponded with me via e-mail to help me with the // conversion of his Pascal code to C++. // // WARNING: // If there is a single board and piece combination with the highest // 'rating' value, it is the best combination. However, among // board and piece combinations with EQUAL 'rating' values, // the highest 'priority' value wins. // // So, the complete rating is: { rating, priority }. private static void EvaluteMove(IBoard board, IPiece piece, out double rating, out int priority) { int pieceMinX; int pieceMinY; int pieceMaxX; int pieceMaxY; piece.GetAbsoluteBoundingRectangle(out pieceMinX, out pieceMinY, out pieceMaxX, out pieceMaxY); // Landing Height (vertical midpoint) double landingHeight = 0.5 * (pieceMinY + pieceMaxY); // int completedRows = BoardHelper.GetTotalCompletedRows(board); int erodedPieceCellsMetric = 0; if (completedRows > 0) { // Count piece cells eroded by completed rows before doing collapse on pile. int countPieceCellsEliminated = BoardHelper.CountPieceCellsEliminated(board, piece, true); // Now it's okay to collapse completed rows board.CollapseCompletedRows(); // Weight eroded cells by completed rows erodedPieceCellsMetric = (completedRows * countPieceCellsEliminated); } // int pileHeight = BoardHelper.GetPileMaxHeight(board); // Each empty row (above pile height) has two (2) "transitions" // (We could call ref_Board.GetTransitionCountForRow( y ) for // these unoccupied rows, but this is an optimization.) int boardRowTransitions = 2 * (board.Height - pileHeight); // Only go up to the pile height, and later we'll account for the // remaining rows transitions (2 per empty row). for (int y = 1; y <= pileHeight; y++) { boardRowTransitions += BoardHelper.GetTransitionCountForRow(board, y); } // int boardColumnTransitions = 0; int boardBuriedHoles = 0; int boardWells = 0; for (int x = 1; x <= board.Width; x++) { boardColumnTransitions += BoardHelper.GetTransitionCountForColumn(board, x); boardBuriedHoles += BoardHelper.GetBuriedHolesForColumn(board, x); boardWells += BoardHelper.GetAllWellsForColumn(board, x); } // Final rating // [1] Punish landing height // [2] Reward eroded piece cells // [3] Punish row transitions // [4] Punish column transitions // [5] Punish buried holes (cellars) // [6] Punish wells rating = 0.0; rating += -1.0 * landingHeight; rating += 1.0 * erodedPieceCellsMetric; rating += -1.0 * boardRowTransitions; rating += -1.0 * boardColumnTransitions; rating += -4.0 * boardBuriedHoles; rating += -1.0 * boardWells; // PRIORITY: // Priority is further differentiation between possible moves. // We further rate moves accoding to the following: // * Reward deviation from center of board // * Reward pieces to the left of center of the board // * Punish rotation // Priority is less important than the rating, but among equal // ratings we select the option with the greatest priority. // In principle we could simply factor priority in to the rating, // as long as the priority was less significant than the smallest // variations in rating, but for large board widths (>100), the // risk of loss of precision in the lowest bits of the rating // is too much to tolerate. So, this priority is stored in a // separate variable. int absoluteDistanceX = Math.Abs(piece.PosX - board.PieceSpawnX); priority = 0; priority += (100 * absoluteDistanceX); if (piece.PosX < board.PieceSpawnX) { priority += 10; } priority -= piece.Orientation - 1; }
private double EvaluteMove(IBoard board, IPiece piece, out int bestRotationDelta, out int bestTranslationDelta) { int currentBestTranslationDelta = 0; int currentBestRotationDelta = 0; double currentBestRating = -1.0e+20; // Really bad! int currentBestPriority = 0; //current.Translate(0, -1); IBoard tempBoard = board.Clone(); IPiece tempPiece = piece.Clone(); // Consider all possible rotations for (int trialRotationDelta = 0; trialRotationDelta < piece.MaxOrientations; trialRotationDelta++) { // Copy piece tempPiece.CopyFrom(piece); // Rotate tempPiece.Rotate(trialRotationDelta); // Get translation range bool isMovePossible; int minDeltaX; int maxDeltaX; BoardHelper.GetAccessibleTranslationsForOrientation(board, tempPiece, out isMovePossible, out minDeltaX, out maxDeltaX); //StringBuilder sb = new StringBuilder(); //for (int i = 1; i <= tempPiece.TotalCells; i++) //{ // int x, y; // tempPiece.GetCellAbsolutePosition(i, out x, out y); // sb.Append(String.Format("[{0}->{1},{2}]", i, x - tempPiece.PosX, y - tempPiece.PosY)); //} //Log.Log.Default.WriteLine("{0} {1} -> {2} {3}", trialRotationDelta, minDeltaX, maxDeltaX, sb.ToString()); if (isMovePossible) { // Consider all allowed translations for (int trialTranslationDelta = minDeltaX; trialTranslationDelta <= maxDeltaX; trialTranslationDelta++) { // Evaluate this move // Copy piece tempPiece.CopyFrom(piece); // Rotate tempPiece.Rotate(trialRotationDelta); // Translate tempPiece.Translate(trialTranslationDelta, 0); // Check if move is acceptable if (board.CheckNoConflict(tempPiece)) { // Copy board tempBoard.CopyFrom(board); // Drop piece tempBoard.DropAndCommit(tempPiece); tempBoard.CollapseCompletedRows(); double trialRating = 0; trialRating += -0.65 * BoardHelper.GetTotalShadowedHoles(board); trialRating += -0.10 * BoardHelper.GetPileHeightWeightedCells(board); trialRating += -0.20 * BoardHelper.GetSumOfWellHeights(board); // Check if better than previous best if (trialRating > currentBestRating) { currentBestRating = trialRating; currentBestTranslationDelta = trialTranslationDelta; currentBestRotationDelta = trialRotationDelta; } } } } } // Commit to this move bestTranslationDelta = currentBestTranslationDelta; bestRotationDelta = currentBestRotationDelta; return(currentBestRating); }
public bool MultiplayerMode(IBoard board, List <Specials> inventory, int inventoryMaxSize, IEnumerable <IOpponent> opponents, List <SpecialAdvice> advices) { // Get strongest opponent int strongest = GetStrongestOpponent(opponents); // Get current special Specials special = inventory[0]; inventory.RemoveAt(0); // switch (special) { // Destroy own board and switch with strongest opponent case Specials.SwitchFields: // TODO: Log.Default.WriteLine(LogLevels.Debug, "[NORMAL]Keep S for survival"); // NOP break; // Wait case Specials.BlockGravity: case Specials.NukeField: Log.Default.WriteLine(LogLevels.Debug, "[NORMAL]Keep {0} for survival", special.ToString()); // NOP break; // Send to strongest opponent case Specials.AddLines: Log.Default.WriteLine(LogLevels.Debug, "[NORMAL]Use A on strongest {0}", strongest); advices.Add(new SpecialAdvice { SpecialAdviceAction = SpecialAdvice.SpecialAdviceActions.UseOpponent, OpponentId = strongest }); break; // Send to strongest opponent with a bomb // If none, // if inventory almost full, drop // else NOP case Specials.BlockBomb: { int bombTarget = GetStrongestOpponentWithBomb(opponents); if (bombTarget != -1) { Log.Default.WriteLine(LogLevels.Debug, "[NORMAL]use O to {0}", bombTarget); advices.Add(new SpecialAdvice { SpecialAdviceAction = SpecialAdvice.SpecialAdviceActions.UseOpponent, OpponentId = bombTarget }); } else { if (inventory.Count + 2 >= inventoryMaxSize) { Log.Default.WriteLine(LogLevels.Debug, "[NORMAL]Discard O, inventory almost full"); advices.Add(new SpecialAdvice { SpecialAdviceAction = SpecialAdvice.SpecialAdviceActions.Discard, }); } else { Log.Default.WriteLine(LogLevels.Debug, "[NORMAL]Keep O for later"); } } break; } // Send to opponent with Nuke/Gravity/Switch on bottom line // If none, send to opponent with Bomb on bottom line // If none, send to opponent with most specials on bottom line // If none, // if we have Nuke/Gravity/Switch/Bomb on bottom line, drop // else, send to ourself case Specials.ClearLines: { int targetId = GetStrongestOpponentWithNukeSwitchGravityOnBottomLine(opponents); if (targetId != -1) { Log.Default.WriteLine(LogLevels.Debug, "[NORMAL]Use C on opponent with N/G/S on bottom line {0}", targetId); advices.Add(new SpecialAdvice { SpecialAdviceAction = SpecialAdvice.SpecialAdviceActions.UseOpponent, OpponentId = targetId }); } else { targetId = GetStrongestOpponentWithBombOnBottomLine(opponents); if (targetId != -1) { Log.Default.WriteLine(LogLevels.Debug, "[NORMAL]Use C on opponent with O on bottom line {0}", targetId); advices.Add(new SpecialAdvice { SpecialAdviceAction = SpecialAdvice.SpecialAdviceActions.UseOpponent, OpponentId = targetId }); } else { targetId = GetOpponentWithMostSpecialsOnBottomLine(opponents); if (targetId != -1) { Log.Default.WriteLine(LogLevels.Debug, "[NORMAL]Use C on opponent with most specials on bottom line {0}", targetId); advices.Add(new SpecialAdvice { SpecialAdviceAction = SpecialAdvice.SpecialAdviceActions.UseOpponent, OpponentId = targetId }); } else { bool hasValuableSpecialOnBottomLine = HasNukeGravitySwitchOnBottomLine(board); if (hasValuableSpecialOnBottomLine) { Log.Default.WriteLine(LogLevels.Debug, "[NORMAL]Discard C, we have N/G/S on bottom line"); advices.Add(new SpecialAdvice { SpecialAdviceAction = SpecialAdvice.SpecialAdviceActions.Discard, }); } else { Log.Default.WriteLine(LogLevels.Debug, "[NORMAL]Use C on ourself"); advices.Add(new SpecialAdvice { SpecialAdviceAction = SpecialAdvice.SpecialAdviceActions.UseSelf, }); } } } } break; } // if we have a Bomb in our board and no Gravity/Nuke in inventory, send to ourself // send to opponent with Nuke/Gravity/Switch // if none, // if second special is Bomb // if an opponent has a bomb, // send to opponent with most specials and no bomb // if none, drop // else // send to opponent with most specials // if none, // if inventory almost full, drop // else NOP // else // send to opponent with Bomb // if none, send to opponent with most specials // if none, // if inventory almost full, drop // else NOP case Specials.ClearSpecialBlocks: { bool hasBomb = HasSpecial(board, Specials.BlockBomb); if (hasBomb && !inventory.Any(x => x == Specials.NukeField || x == Specials.BlockGravity)) { Log.Default.WriteLine(LogLevels.Debug, "[NORMAL]Use B on ourself, we have O in board and no N/G in inventory"); advices.Add(new SpecialAdvice { SpecialAdviceAction = SpecialAdvice.SpecialAdviceActions.UseSelf, }); } else { int targetId = GetStrongestOpponentWithNukeSwitchGravity(opponents); if (targetId != -1) { Log.Default.WriteLine(LogLevels.Debug, "[NORMAL]Use B on strongest opponent with N/S/G {0}", targetId); advices.Add(new SpecialAdvice { SpecialAdviceAction = SpecialAdvice.SpecialAdviceActions.UseOpponent, OpponentId = targetId }); } else { if (inventory.Any() && inventory[0] == Specials.BlockBomb) { bool hasOpponentWithBomb = HasOpponentWithBomb(opponents); if (hasOpponentWithBomb) { targetId = GetOpponentWithMostSpecialsAndNoBomb(opponents); if (targetId != -1) { Log.Default.WriteLine(LogLevels.Debug, "[NORMAL]Use B on opponent with most specials and no O {0}, we have O in inventory", targetId); advices.Add(new SpecialAdvice { SpecialAdviceAction = SpecialAdvice.SpecialAdviceActions.UseOpponent, OpponentId = targetId }); } else { Log.Default.WriteLine(LogLevels.Debug, "[NORMAL]Discard B, no opponents without O and we have O in inventory", targetId); advices.Add(new SpecialAdvice { SpecialAdviceAction = SpecialAdvice.SpecialAdviceActions.Discard, }); } } else { targetId = GetOpponentWithMostSpecials(opponents); if (targetId != -1) { Log.Default.WriteLine(LogLevels.Debug, "[NORMAL]Use B on opponent with most specials {0}", targetId); advices.Add(new SpecialAdvice { SpecialAdviceAction = SpecialAdvice.SpecialAdviceActions.UseOpponent, OpponentId = targetId }); } else { if (inventory.Count + 2 >= inventoryMaxSize) { Log.Default.WriteLine(LogLevels.Debug, "[NORMAL]Discard B, inventory is almost full"); advices.Add(new SpecialAdvice { SpecialAdviceAction = SpecialAdvice.SpecialAdviceActions.Discard, }); } else { Log.Default.WriteLine(LogLevels.Debug, "[NORMAL]Keep B for later"); } } } } else { targetId = GetStrongestOpponentWithBomb(opponents); if (targetId != -1) { Log.Default.WriteLine(LogLevels.Debug, "[NORMAL]Use B on opponent with O {0}, we don't have any O", targetId); advices.Add(new SpecialAdvice { SpecialAdviceAction = SpecialAdvice.SpecialAdviceActions.UseOpponent, OpponentId = targetId }); } else { targetId = GetOpponentWithMostSpecials(opponents); if (targetId != -1) { Log.Default.WriteLine(LogLevels.Debug, "[NORMAL]Use B on opponent with most specials {0}", targetId); advices.Add(new SpecialAdvice { SpecialAdviceAction = SpecialAdvice.SpecialAdviceActions.UseOpponent, OpponentId = targetId }); } else { if (inventory.Count + 2 >= inventoryMaxSize) { Log.Default.WriteLine(LogLevels.Debug, "[NORMAL]Discard B, inventory is almost full"); advices.Add(new SpecialAdvice { SpecialAdviceAction = SpecialAdvice.SpecialAdviceActions.Discard, }); } else { Log.Default.WriteLine(LogLevels.Debug, "[NORMAL]Keep B for later"); } } } } } } break; } // send to opponent with Nuke/Gravity/Switch // if none, send to strongest opponent case Specials.RandomBlocksClear: { int targetId = GetStrongestOpponentWithNukeSwitchGravity(opponents); if (targetId != -1) { Log.Default.WriteLine(LogLevels.Debug, "[NORMAL]Use R on strongest opponent with N/S/G {0}", targetId); advices.Add(new SpecialAdvice { SpecialAdviceAction = SpecialAdvice.SpecialAdviceActions.UseOpponent, OpponentId = targetId }); } else { targetId = GetStrongestOpponent(opponents); if (targetId != -1) { Log.Default.WriteLine(LogLevels.Debug, "[NORMAL]Use R on strongest opponent {0}", targetId); advices.Add(new SpecialAdvice { SpecialAdviceAction = SpecialAdvice.SpecialAdviceActions.UseOpponent, OpponentId = targetId }); } else { Log.Default.WriteLine(LogLevels.Debug, "[NORMAL]Keep R for later **** SHOULD NEVER HAPPEN"); } } break; } // send to weakest opponent case Specials.Immunity: { int targetId = GetWeakestOpponent(opponents); if (targetId != -1) { IBoard lastOpponentBoard = opponents.First(x => x.PlayerId == targetId).Board; if (lastOpponentBoard != null) { int pileHeight = BoardHelper.GetPileMaxHeight(lastOpponentBoard); if (pileHeight >= 10) { Log.Default.WriteLine(LogLevels.Debug, "[NORMAL]Use I on weakest opponent (board {0}) {1}", pileHeight, targetId); advices.Add(new SpecialAdvice { SpecialAdviceAction = SpecialAdvice.SpecialAdviceActions.UseOpponent, OpponentId = targetId }); } else if (inventory.Count + 2 >= inventoryMaxSize) { Log.Default.WriteLine(LogLevels.Debug, "[NORMAL]Discard I, inventory is almost full"); advices.Add(new SpecialAdvice { SpecialAdviceAction = SpecialAdvice.SpecialAdviceActions.Discard, }); } else { Log.Default.WriteLine(LogLevels.Debug, "[NORMAL]Keep I for later"); } } } break; } // TODO // if first special is Quake, // send to opponent with most towers or strongest opponent [TO BE DEFINED] // ClearColumn // Darkness // Confusion // ZebraField // Send to strongest opponent default: { int targetId = GetStrongestOpponent(opponents); if (targetId != -1) { Log.Default.WriteLine(LogLevels.Debug, "[NORMAL]Use {0} on strongest opponent {1}", special.ToString(), targetId); advices.Add(new SpecialAdvice { SpecialAdviceAction = SpecialAdvice.SpecialAdviceActions.UseOpponent, OpponentId = targetId }); } else { Log.Default.WriteLine(LogLevels.Debug, "[NORMAL]Keep {0} for later **** SHOULD NEVER HAPPEN", special.ToString()); } break; } } return(true); }
public bool PanicMode(List <Specials> inventory, IEnumerable <IOpponent> opponents, List <SpecialAdvice> advices) { // Survival // If Nuke/Gravity/Switch if (inventory.Any(x => x == Specials.NukeField || x == Specials.BlockGravity || x == Specials.SwitchFields)) { Log.Default.WriteLine(LogLevels.Debug, "[SURVIVAL] Found N/G/S in inventory"); bool saved = false; while (true) { if (!inventory.Any()) { break; // Stops when inventory is empty } // Get strongest opponent int strongest = GetStrongestOpponent(opponents); // Get current special Specials special = inventory[0]; inventory.RemoveAt(0); switch (special) { case Specials.NukeField: // Nuke/Gravity -> use it immediately case Specials.BlockGravity: Log.Default.WriteLine(LogLevels.Debug, "[SURVIVAL]Use {0}", special.ToString()); advices.Add(new SpecialAdvice { SpecialAdviceAction = SpecialAdvice.SpecialAdviceActions.UseSelf, }); saved = true; // Saved, stop emptying inventory break; case Specials.SwitchFields: // Switch -> use it only if strongest is really strong, else drop it { IBoard strongestBoard = opponents.First(x => x.PlayerId == strongest).Board; int pileHeight = BoardHelper.GetPileMaxHeight(strongestBoard); if (pileHeight <= 10) { Log.Default.WriteLine(LogLevels.Debug, "[SURVIVAL]Use S, found a valid opponent {0} with a pile {1}", strongest, pileHeight); advices.Add(new SpecialAdvice { SpecialAdviceAction = SpecialAdvice.SpecialAdviceActions.UseOpponent, OpponentId = strongest }); saved = true; // Saved, stop emptying inventory } else { Log.Default.WriteLine(LogLevels.Debug, "[SURVIVAL]Discard S, no valid opponent"); advices.Add(new SpecialAdvice { SpecialAdviceAction = SpecialAdvice.SpecialAdviceActions.Discard, }); } break; } case Specials.ClearLines: // ClearLine -> use it immediately TODO ==> this could lead to unwanted behaviour if we are emptying for a switch Log.Default.WriteLine(LogLevels.Debug, "[SURVIVAL]Use C on ourself"); advices.Add(new SpecialAdvice { SpecialAdviceAction = SpecialAdvice.SpecialAdviceActions.UseSelf, }); break; default: // Other -> use it on strongest Log.Default.WriteLine(LogLevels.Debug, "[SURVIVAL]Use {0} on strongest opponent {1}", special, strongest); advices.Add(new SpecialAdvice { SpecialAdviceAction = SpecialAdvice.SpecialAdviceActions.UseOpponent, OpponentId = strongest }); break; } if (saved) // If saved or something wrong with UseSpecial, stop loop { break; } System.Threading.Thread.Sleep(10); // delay next special use } Log.Default.WriteLine(LogLevels.Debug, "[SURVIVAL] Survival mode N/G/S finished. saved:{0}", saved); } // Nothing could save use, send everything to weakest and pray else { Log.Default.WriteLine(LogLevels.Debug, "[SURVIVAL] No N/G/S found -> use everything on weakest and try to kill him"); while (true) { if (!inventory.Any()) { break; // Stops when inventory is empty } // Get strongest opponent int weakest = GetWeakestOpponent(opponents); // Get current special Specials special = inventory[0]; inventory.RemoveAt(0); // ClearLine -> use it immediately if (special == Specials.ClearLines) { Log.Default.WriteLine(LogLevels.Debug, "[SURVIVAL]Use C on ourself"); advices.Add(new SpecialAdvice { SpecialAdviceAction = SpecialAdvice.SpecialAdviceActions.UseSelf, }); } // Other -> use it on weakest else { Log.Default.WriteLine(LogLevels.Debug, "[SURVIVAL]Use {0} on weakest opponent {1}", special.ToString(), weakest); advices.Add(new SpecialAdvice { SpecialAdviceAction = SpecialAdvice.SpecialAdviceActions.UseOpponent, OpponentId = weakest }); } } } return(true); }
public bool SoloMode(IBoard board, List <Specials> inventory, int inventoryMaxSize, List <SpecialAdvice> advices) { // drop everything except Nuke/Gravity/ClearLines // use clear line if no nuke/gravity on bottom line and board not empty or when reaching top of board or when inventory is full // use nuke when reaching top of board // use gravity when reaching mid-board Specials special = inventory.First(); int maxPile = BoardHelper.GetPileMaxHeight(board); switch (special) { case Specials.NukeField: if (maxPile >= 14) { advices.Add(new SpecialAdvice { SpecialAdviceAction = SpecialAdvice.SpecialAdviceActions.UseSelf, }); } break; case Specials.BlockGravity: if (maxPile >= 10) { advices.Add(new SpecialAdvice { SpecialAdviceAction = SpecialAdvice.SpecialAdviceActions.UseSelf, }); } break; case Specials.ClearLines: { bool hasValuableBottomLine = HasNukeGravityOnBottomLine(board); if ((!hasValuableBottomLine && maxPile > 4) || maxPile >= 14) { advices.Add(new SpecialAdvice { SpecialAdviceAction = SpecialAdvice.SpecialAdviceActions.UseSelf, }); } else if (maxPile >= 10 || inventory.Count + 2 >= inventoryMaxSize) { advices.Add(new SpecialAdvice { SpecialAdviceAction = SpecialAdvice.SpecialAdviceActions.UseSelf, }); } break; } default: advices.Add(new SpecialAdvice { SpecialAdviceAction = SpecialAdvice.SpecialAdviceActions.Discard, }); break; } return(true); }
// TODO: helpers should return IOpponent instead of int // TODO: when confusion is activated, bot doesn't do anything // TODO: immunity public bool GetSpecialAdvices(IBoard board, IPiece current, IPiece next, IEnumerable <Specials> inventory, int inventoryMaxSize, IEnumerable <IOpponent> opponents, out List <SpecialAdvice> advices) { // if solo, // drop everything except Nuke/Gravity/ClearLines // use clear line if no nuke/gravity on bottom line or when reaching top of board or when inventory is full // use nuke when reaching top of board // use gravity when reaching mid-board // Survival first: // if we are to high (above 14), // if we have Nuke or Gravity or Switch in inventory, send offensive specials to strongest opponent (lowest board) + send ClearLine to ourself until getting Nuke/Gravity/Switch // use Nuke or Gravity on ourself -> send to ourself ==> saved // use Switch // if strongest opponent board is below 10 -> send to opponent => saved // else drop it and continue to search for a Nuke or Gravity // else, // empty inventory by sending offensive specials to weakest opponent (highest board) + sending ClearLine to ourself -> not saved // If we are saved, // if there is only one player left, and we have enough AddLines to kill him, drop everything except AddLines, Quake, Confusion, Darkness and send them to last opponent // if first special is Switch, destroy own board and switch with strongest opponent (TODO) // if first special is Nuke or Gravity, NOP // if first special is AddLines, send to strongest opponent // if first special is Bomb, // send to strongest opponent with a bomb // if none, NOP // if first special is ClearLine, // send to opponent with Nuke/Gravity/Switch on bottom line // if none, send to opponent with Bomb on bottom line // if none, send to opponent with most specials on bottom line // if none, // if we have Nuke/Gravity/Switch/Bomb on bottom line, drop // else, send to ourself // if first special is ClearSpecialBlocks, // if we have a Bomb in our board and no Gravity/Nuke in inventory, send to ourself // send to opponent with Nuke/Gravity/Switch // if none, // if second special is Bomb // if an opponent has a bomb, // send to opponent with most specials and no bomb // if none, drop // else // send to opponent with most specials // if none, NOP // else // send to opponent with Bomb // if none, send to opponent with most specials // if none, NOP // if first special is RandomBlocksClear, // send to opponent with Nuke/Gravity/Switch // if none, send to strongest opponent // if first special is Quake, // send to opponent with most towers or strongest opponent [TO BE DEFINED] // if first special is Immunity, // send to weakest opponent // TODO: zebra, clear column, confusion // advices = new List <SpecialAdvice>(); // clone inventory List <Specials> internalInventory = inventory.ToList(); // No inventory if (!internalInventory.Any()) { return(false); } if (!opponents.Any()) { // Solo return(SoloMode(board, internalInventory, inventoryMaxSize, advices)); } int maxPile = BoardHelper.GetPileMaxHeight(board); if (maxPile >= 14) { // Survival return(PanicMode(internalInventory, opponents, advices)); } else { // Normal use // Check if we can kill last player int lastOpponent = GetLastOpponent(opponents); if (lastOpponent != -1) { IBoard lastOpponentBoard = opponents.First(x => x.PlayerId == lastOpponent).Board; if (lastOpponentBoard != null) { int pileHeight = BoardHelper.GetPileMaxHeight(lastOpponentBoard); int addLinesCount = internalInventory.Count(x => x == Specials.AddLines); if (addLinesCount > 0 && pileHeight + addLinesCount >= lastOpponentBoard.Height) { // Finish him return(OneOpponentMode(lastOpponent, internalInventory, advices)); } } } return(MultiplayerMode(board, internalInventory, inventoryMaxSize, opponents, advices)); } #region Simple strategy //// if negative special, //// if no other player, drop it //// else, use it on random opponent //// else if switch, drop it //// else, use it on ourself //Specials firstSpecial = inventory[0]; //int specialValue = 0; //switch (firstSpecial) //{ // case Specials.AddLines: // specialValue = -1; // break; // case Specials.ClearLines: // specialValue = +1; // break; // case Specials.NukeField: // specialValue = +1; // break; // case Specials.RandomBlocksClear: // specialValue = -1; // break; // case Specials.SwitchFields: // specialValue = 0; // break; // case Specials.ClearSpecialBlocks: // specialValue = -1; // break; // case Specials.BlockGravity: // specialValue = +1; // break; // case Specials.BlockQuake: // specialValue = -1; // break; // case Specials.BlockBomb: // specialValue = -1; // break; // case Specials.ClearColumn: // specialValue = -1; // break; // case Specials.ZebraField: // specialValue = -1; // break; //} //if (specialValue == 0) // advices.Add(new SpecialAdvice // { // SpecialAdviceAction = SpecialAdvice.SpecialAdviceActions.Discard, // }); //else if (specialValue > 0) // advices.Add(new SpecialAdvice // { // SpecialAdviceAction = SpecialAdvice.SpecialAdviceActions.UseSelf, // }); //else //{ // if (opponents.Any()) // { // // Get strongest opponent // int strongest = GetStrongestOpponent(opponents); // advices.Add(new SpecialAdvice // { // SpecialAdviceAction = SpecialAdvice.SpecialAdviceActions.UseOpponent, // OpponentId = strongest, // }); // } // else // advices.Add(new SpecialAdvice // { // SpecialAdviceAction = SpecialAdvice.SpecialAdviceActions.Discard, // }); //} #endregion }
public bool GetBestMove(IBoard board, IPiece current, IPiece next, out int bestRotationDelta, out int bestTranslationDelta, out bool rotationBeforeTranslation) { int currentBestTranslationDelta = 0; int currentBestRotationDelta = 0; double currentBestRating = -1.0e+20; // Really bad! int currentBestPriority = 0; //current.Translate(0, -1); IBoard tempBoard = board.Clone(); IPiece tempPiece = current.Clone(); // Consider all possible rotations for (int trialRotationDelta = 0; trialRotationDelta < current.MaxOrientations; trialRotationDelta++) { // Copy piece tempPiece.CopyFrom(current); // Rotate tempPiece.Rotate(trialRotationDelta); // Get translation range bool isMovePossible; int minDeltaX; int maxDeltaX; BoardHelper.GetAccessibleTranslationsForOrientation(board, tempPiece, out isMovePossible, out minDeltaX, out maxDeltaX); StringBuilder sb = new StringBuilder(); for (int i = 1; i <= tempPiece.TotalCells; i++) { int x, y; tempPiece.GetCellAbsolutePosition(i, out x, out y); sb.Append(String.Format("[{0}->{1},{2}]", i, x - tempPiece.PosX, y - tempPiece.PosY)); } //Log.Log.Default.WriteLine("{0} {1} -> {2} {3}", trialRotationDelta, minDeltaX, maxDeltaX, sb.ToString()); if (isMovePossible) { // Consider all allowed translations for (int trialTranslationDelta = minDeltaX; trialTranslationDelta <= maxDeltaX; trialTranslationDelta++) { // Evaluate this move // Copy piece tempPiece.CopyFrom(current); // Rotate tempPiece.Rotate(trialRotationDelta); // Translate tempPiece.Translate(trialTranslationDelta, 0); // Check if move is acceptable if (board.CheckNoConflict(tempPiece)) { // Copy board tempBoard.CopyFrom(board); // Drop piece tempBoard.DropAndCommit(tempPiece); // Evaluate double trialRating; int trialPriority; EvaluteMove(tempBoard, tempPiece, out trialRating, out trialPriority); //Log.Log.Default.WriteLine("R:{0:0.0000} P:{1} R:{2} T:{3}", trialRating, trialPriority, trialRotationDelta, trialTranslationDelta); // Check if better than previous best if (trialRating > currentBestRating || (Math.Abs(trialRating - currentBestRating) < 0.0001 && trialPriority > currentBestPriority)) { currentBestRating = trialRating; currentBestPriority = trialPriority; currentBestTranslationDelta = trialTranslationDelta; currentBestRotationDelta = trialRotationDelta; } } } } } // commit to this move rotationBeforeTranslation = true; bestTranslationDelta = currentBestTranslationDelta; bestRotationDelta = currentBestRotationDelta; //Console.SetCursorPosition(0, _client.Board.Height+1); // Console.WriteLine("{0} {1} {2:0.000} {3}", bestRotationDelta, bestTranslationDelta, currentBestRating, currentBestPriority); return(true); }