Gitea/workflows #1

Closed
jheaps wants to merge 47 commits from gitea/workflows into AddProject
8 changed files with 92 additions and 19 deletions
Showing only changes of commit 743c395e11 - Show all commits
+7 -2
View File
@@ -35,7 +35,7 @@ public class ChessController(
/// </summary> /// </summary>
[HttpGet("new")] [HttpGet("new")]
[HttpGet("new/{difficulty}")] [HttpGet("new/{difficulty}")]
public ActionResult CreateGame(int difficulty = 20) public ActionResult CreateGame(int difficulty = 20, string color = "random")
{ {
var gameState = chessService.CreateNewGame(); var gameState = chessService.CreateNewGame();
_games[gameState.GameId] = gameState; _games[gameState.GameId] = gameState;
@@ -45,7 +45,12 @@ public class ChessController(
gameState.BlackJoined = true; gameState.BlackJoined = true;
Guid playerId = Guid.NewGuid(); Guid playerId = Guid.NewGuid();
Guid computerId = Guid.NewGuid(); Guid computerId = Guid.NewGuid();
var isWhite = Random.Shared.Next(2) == 0; var isWhite = color.ToLowerInvariant() switch
{
"white" => true,
"black" => false,
_ => Random.Shared.Next(2) == 0,
};
gameState.Computer = engineFactory.Create(difficulty); gameState.Computer = engineFactory.Create(difficulty);
+9
View File
@@ -89,6 +89,15 @@
</div> </div>
</div> </div>
<div id="colorModal" style="display: none;">
<p>Play as:</p>
<div id="colorButtonContainer">
<button onclick="selectColor('white')">White</button>
<button onclick="selectColor('black')">Black</button>
<button onclick="selectColor('random')">Random</button>
</div>
</div>
@section Scripts { @section Scripts {
<script src="~/js/signalr/signalr.min.js"></script> <script src="~/js/signalr/signalr.min.js"></script>
<script src="~/js/ChessScripts/GameState.js?v=@ViewData["cssVersion"]"></script> <script src="~/js/ChessScripts/GameState.js?v=@ViewData["cssVersion"]"></script>
Binary file not shown.
+41
View File
@@ -157,6 +157,47 @@
pointer-events: none; /* ensures img doesn't steal the click */ pointer-events: none; /* ensures img doesn't steal the click */
} }
#colorModal {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: #1e1e1e;
padding: 20px;
border-radius: 10px;
box-shadow: 0 0 20px rgba(0,0,0,0.6);
color: white;
z-index: 1000;
text-align: center;
}
#colorModal p {
margin-bottom: 15px;
font-size: 18px;
font-weight: bold;
}
#colorButtonContainer {
display: flex;
gap: 10px;
justify-content: center;
}
#colorButtonContainer button {
background-color: #2c2c2c;
border: none;
padding: 10px 18px;
border-radius: 8px;
cursor: pointer;
transition: transform 0.2s ease;
color: white;
}
#colorButtonContainer button:hover {
transform: scale(1.1);
background-color: #3a3a3a;
}
.chessSquare .coordinate-label { .chessSquare .coordinate-label {
position: absolute; position: absolute;
font-size: 1.2vw; font-size: 1.2vw;
@@ -4,8 +4,8 @@ const ChessAPI = {
return await response.json(); return await response.json();
}, },
async createCPUGame(difficulty) { async createCPUGame(difficulty, color = "random") {
const response = await fetch(`/api/chess/new/${difficulty}`); const response = await fetch(`/api/chess/new/${difficulty}?color=${color}`);
return await response.json(); return await response.json();
}, },
@@ -23,6 +23,16 @@ const ChessModals = {
resolve(difficulty); resolve(difficulty);
}; };
}); });
},
promptColor() {
return new Promise(resolve => {
document.getElementById("colorModal").style.display = "block";
window.selectColor = (color) => {
document.getElementById("colorModal").style.display = "none";
resolve(color);
};
});
} }
}; };
@@ -91,7 +91,8 @@ async function startCPUGame() {
try { try {
const difficulty = await ChessModals.promptDifficulty(); const difficulty = await ChessModals.promptDifficulty();
const gameData = await ChessAPI.createCPUGame(difficulty); const color = await ChessModals.promptColor();
const gameData = await ChessAPI.createCPUGame(difficulty, color);
GameState.setGameInfo(gameData.gameId, gameData.id, gameData.isWhite); GameState.setGameInfo(gameData.gameId, gameData.id, gameData.isWhite);
GameState.clearPreviousMove(); GameState.clearPreviousMove();
+21 -14
View File
@@ -208,14 +208,32 @@ static int evaluatePawn(const chess::Position& pos, const chess::Color c, const
return score; return score;
} }
static int piece_value(chess::PieceType pt) {
switch (pt) {
case chess::PAWN: return 100;
case chess::KNIGHT: return 320;
case chess::BISHOP: return 330;
case chess::ROOK: return 500;
case chess::QUEEN: return 900;
default: return 0;
}
}
static int castleIncentive(const chess::Position& pos, chess::Color c) { static int castleIncentive(const chess::Position& pos, chess::Color c) {
if (!pos.pieces(chess::QUEEN)) chess::Bitboard pcs = pos.pieces();
return 0; int total = 0;
while (pcs) {
chess::Square s = chess::pop_lsb(pcs);
chess::Piece pc = pos.piece_on(s);
chess::Color c = chess::color_of(pc);
total += piece_value(chess::type_of(pc));
}
chess::Square k = pos.king_square(c); chess::Square k = pos.king_square(c);
bool castled = (c == chess::WHITE) ? (k == chess::G1 || k == chess::C1) bool castled = (c == chess::WHITE) ? (k == chess::G1 || k == chess::C1)
: (k == chess::G8 || k == chess::C8); : (k == chess::G8 || k == chess::C8);
return castled ? 600 : 0; return castled ? (total / 10) : 0;
} }
static int evaluatePiece(const chess::Position& pos, const chess::Square& s, const chess::Piece& pc, const chess::Color& c) { static int evaluatePiece(const chess::Position& pos, const chess::Square& s, const chess::Piece& pc, const chess::Color& c) {
@@ -274,17 +292,6 @@ static int evaluate_stm(const chess::Position& pos, bool whiteToMove) {
static int score_to_tt(int s, int ply) { return s >= MATE_BOUND ? s + ply : s <= -MATE_BOUND ? s - ply : s; } static int score_to_tt(int s, int ply) { return s >= MATE_BOUND ? s + ply : s <= -MATE_BOUND ? s - ply : s; }
static int score_from_tt(int s, int ply) { return s >= MATE_BOUND ? s - ply : s <= -MATE_BOUND ? s + ply : s; } static int score_from_tt(int s, int ply) { return s >= MATE_BOUND ? s - ply : s <= -MATE_BOUND ? s + ply : s; }
static int piece_value(chess::PieceType pt) {
switch (pt) {
case chess::PAWN: return 100;
case chess::KNIGHT: return 320;
case chess::BISHOP: return 330;
case chess::ROOK: return 500;
case chess::QUEEN: return 900;
default: return 0;
}
}
/* Heuristic for searching the most promising moves first, which makes alpha-beta prune far /* Heuristic for searching the most promising moves first, which makes alpha-beta prune far
* more. Bands, highest first: the TT best move, then captures by MVV-LVA (most valuable * more. Bands, highest first: the TT best move, then captures by MVV-LVA (most valuable
* victim, least valuable attacker), then the two killer moves for this ply (quiet moves that * victim, least valuable attacker), then the two killer moves for this ply (quiet moves that