Add difficulty levels to "Against AI" game mode
Multiple difficulty levels have been implemented for the "Against AI" mode. This involved changes in the user interface to allow players to select the difficulty and corresponding adjustments in the game logic. Also, Artificial Intelligence behavior has been adapted according to these difficulty levels.
This commit is contained in:
@@ -19,12 +19,12 @@ public class PveController(
|
||||
{
|
||||
[Route("new")]
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> New(Guid playerId)
|
||||
public async Task<IActionResult> New(Guid playerId, int difficulty = 1)
|
||||
{
|
||||
// Add a computer player
|
||||
logger.LogInformation("Creating a new PVE game for player {playerId}.", playerId);
|
||||
var computerId = Guid.NewGuid();
|
||||
database.GetOrAddPlayer(computerId).NickName = "Computer";
|
||||
database.GetOrAddPlayer(computerId).NickName = engine.GetComputerName(difficulty) + " AI";
|
||||
//var asyncLock = new SemaphoreSlim(1, 1);
|
||||
|
||||
// Create a challenge
|
||||
@@ -61,7 +61,7 @@ public class PveController(
|
||||
logger.LogInformation("The fen {fen} means it's the computer's turn. Computer is calculating the best move.", fen);
|
||||
// Wait for the UI to update
|
||||
await Task.Delay(300);
|
||||
var bestMove = engine.GetBestMove(fen);
|
||||
var bestMove = engine.GetBestMove(fen, difficulty);
|
||||
|
||||
logger.LogInformation("Computer calculated the best move: {bestMove}", bestMove);
|
||||
await client.Send(bestMove);
|
||||
|
||||
@@ -17,17 +17,48 @@ public class ChessEngine
|
||||
});
|
||||
_engine = new Engine(channel.Writer);
|
||||
}
|
||||
|
||||
public string GetComputerName(int difficulty)
|
||||
{
|
||||
return difficulty switch
|
||||
{
|
||||
1 => "Chimpanzee",
|
||||
2 => "Easy",
|
||||
3 => "Intermediate",
|
||||
4 => "Hard",
|
||||
5 => "Brutal",
|
||||
6 => "Grandmaster",
|
||||
7 => "Unbeatable",
|
||||
_ => "Unknown"
|
||||
};
|
||||
}
|
||||
|
||||
public string GetBestMove(string fen)
|
||||
public string GetBestMove(string fen, int difficulty)
|
||||
// 1: Chimpanzee
|
||||
// 2: Easy
|
||||
// 3: Intermediate
|
||||
// 4: Hard
|
||||
// 5: Brutal
|
||||
// 6: Grandmaster
|
||||
// 7: Unbeatable
|
||||
{
|
||||
_engine.AdjustPosition($"position fen {fen}");
|
||||
var positionClone = new Position(_engine.Game.CurrentPosition);
|
||||
|
||||
var result = _engine.IDDFS(1, 10);
|
||||
// Depth = difficulty, 50% of chance to be difficulty + 1
|
||||
var depth = difficulty + (new Random().Next(2) == 0 ? 1 : 0);
|
||||
var result = _engine.IDDFS(depth, 10);
|
||||
_engine.Game.ResetCurrentPositionToBeforeSearchState();
|
||||
|
||||
return result
|
||||
.BestMove
|
||||
.ToEPDString(positionClone);
|
||||
if (difficulty > 4)
|
||||
{
|
||||
// If difficulty is higher than 5, we should use the best move
|
||||
return result.BestMove.ToEPDString(positionClone);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Randomly choose one of the moves
|
||||
return result.Moves[new Random().Next(result.Moves.Count)].ToEPDString(positionClone);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -41,7 +41,7 @@
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" asp-controller="Pve" asp-action="New" id="against-ai-link">
|
||||
<a class="nav-link" data-toggle="modal" data-target="#pve-modal">
|
||||
<i class="fa-solid fa-chalkboard-user"></i>
|
||||
Against AI
|
||||
</a>
|
||||
@@ -69,6 +69,38 @@
|
||||
|
||||
@RenderBody()
|
||||
|
||||
<div class="modal fade" id="pve-modal" tabindex="-1" role="dialog" aria-labelledby="pveModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Against AI</h5>
|
||||
<button class="close" type="button" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
@* // 1: Chimpanzee *@
|
||||
@* // 2: Easy *@
|
||||
@* // 3: Intermediate *@
|
||||
@* // 4: Hard *@
|
||||
@* // 5: Brutal *@
|
||||
@* // 6: Grandmaster *@
|
||||
@* // 7: Unbeatable *@
|
||||
<a class="btn btn-secondary btn-block btn-difficulty" asp-controller="Pve" asp-action="New" asp-route-difficulty="1">Chimpanzee</a>
|
||||
<a class="btn btn-secondary btn-block btn-difficulty" asp-controller="Pve" asp-action="New" asp-route-difficulty="2">Easy</a>
|
||||
<a class="btn btn-secondary btn-block btn-difficulty" asp-controller="Pve" asp-action="New" asp-route-difficulty="3">Intermediate</a>
|
||||
<a class="btn btn-secondary btn-block btn-difficulty" asp-controller="Pve" asp-action="New" asp-route-difficulty="4">Hard</a>
|
||||
<a class="btn btn-secondary btn-block btn-difficulty" asp-controller="Pve" asp-action="New" asp-route-difficulty="5">Brutal</a>
|
||||
<a class="btn btn-secondary btn-block btn-difficulty" asp-controller="Pve" asp-action="New" asp-route-difficulty="6">Grandmaster</a>
|
||||
<a class="btn btn-secondary btn-block btn-difficulty" asp-controller="Pve" asp-action="New" asp-route-difficulty="7">Unbeatable</a>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-secondary" type="button" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="footer mt-auto py-3">
|
||||
<div class="container">
|
||||
© @DateTime.UtcNow.Year - Aiursoft.ChessServer -
|
||||
|
||||
@@ -1,20 +1,23 @@
|
||||
import { autoTheme } from "../node_modules/@aiursoft/autodark.js/dist/esm/autodark.js";
|
||||
import { getUserName, changeName, getUserId } from "./player.js";
|
||||
import {autoTheme} from "../node_modules/@aiursoft/autodark.js/dist/esm/autodark.js";
|
||||
import {getUserName, changeName, getUserId} from "./player.js";
|
||||
|
||||
autoTheme();
|
||||
|
||||
async function loadName() {
|
||||
document.getElementById("player-nick-name").innerHTML =
|
||||
"<i class=\"fa-solid fa-user-pen mr-2\"></i>" +
|
||||
await getUserName();
|
||||
document.getElementById("against-ai-link").href += `?playerId=${await getUserId()}`;
|
||||
document.getElementById("player-nick-name").innerHTML =
|
||||
"<i class=\"fa-solid fa-user-pen mr-2\"></i>" +
|
||||
await getUserName();
|
||||
for (let element of document.getElementsByClassName("btn-difficulty")) {
|
||||
element.href += `&playerId=${await getUserId()}`;
|
||||
}
|
||||
}
|
||||
|
||||
async function promptChangeName() {
|
||||
const newName = prompt("Please enter your new name:");
|
||||
if (newName) {
|
||||
await changeName(newName);
|
||||
await loadName();
|
||||
}
|
||||
const newName = prompt("Please enter your new name:");
|
||||
if (newName) {
|
||||
await changeName(newName);
|
||||
await loadName();
|
||||
}
|
||||
}
|
||||
|
||||
await loadName();
|
||||
|
||||
Reference in New Issue
Block a user