2025-04-16 14:27:41 +07:00
|
|
|
|
@page "/blackjack"
|
|
|
|
|
@using БлэкДжек.Components
|
2025-04-16 10:24:33 +07:00
|
|
|
|
@implements IDisposable
|
|
|
|
|
|
2025-04-16 14:27:41 +07:00
|
|
|
|
<div class="casino-background">
|
|
|
|
|
<div class="blackjack-container">
|
|
|
|
|
<div class="game-header">
|
|
|
|
|
<h1 class="game-title">Blackjack</h1>
|
|
|
|
|
<div class="chip-stack"></div>
|
|
|
|
|
</div>
|
2025-04-16 10:24:33 +07:00
|
|
|
|
|
2025-04-16 14:27:41 +07:00
|
|
|
|
@* Область для сообщений игры *@
|
|
|
|
|
@if (!string.IsNullOrEmpty(GameMessage))
|
|
|
|
|
{
|
|
|
|
|
<div class="message-box @(IsGameOver && PlayerScore <= 21 && (DealerScore > 21 || PlayerScore > DealerScore) ? "message-win" : IsGameOver ? "message-lose" : "message-info")" role="alert">
|
|
|
|
|
<div class="message-content">@GameMessage</div>
|
|
|
|
|
</div>
|
|
|
|
|
}
|
2025-04-16 10:24:33 +07:00
|
|
|
|
|
2025-04-16 14:27:41 +07:00
|
|
|
|
@* Основной блок игры, отображается после нажатия "Начать" *@
|
|
|
|
|
@if (IsGameStarted)
|
|
|
|
|
{
|
|
|
|
|
<div class="game-area">
|
|
|
|
|
<div class="player-section dealer-section">
|
|
|
|
|
<div class="player-info">
|
|
|
|
|
<div class="player-avatar dealer-avatar"></div>
|
|
|
|
|
<h2 class="player-title">Дилер <span class="score-badge">@(IsPlayerTurn ? "?" : DealerScore.ToString())</span></h2>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="table-felt">
|
|
|
|
|
<div class="hand dealer-hand">
|
|
|
|
|
@foreach (var card in DealerHand)
|
|
|
|
|
{
|
|
|
|
|
<div class="card-container @(DealerHand.IndexOf(card) == 0 && IsPlayerTurn ? "card-hidden" : "")">
|
|
|
|
|
@if (DealerHand.IndexOf(card) == 0 && IsPlayerTurn)
|
|
|
|
|
{
|
|
|
|
|
<div class="playing-card card-back">
|
|
|
|
|
<div class="card-back-design"></div>
|
|
|
|
|
</div>
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
<div class="playing-card @GetCardColor(card)">
|
|
|
|
|
<div class="card-corner top-left">
|
|
|
|
|
<div class="card-rank">@card.Rank</div>
|
|
|
|
|
<div class="card-suit">@card.Suit</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="card-center-suit">@card.Suit</div>
|
|
|
|
|
<div class="card-corner bottom-right">
|
|
|
|
|
<div class="card-rank">@card.Rank</div>
|
|
|
|
|
<div class="card-suit">@card.Suit</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
}
|
|
|
|
|
</div>
|
|
|
|
|
}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2025-04-16 10:24:33 +07:00
|
|
|
|
</div>
|
|
|
|
|
|
2025-04-16 14:27:41 +07:00
|
|
|
|
<div class="table-divider">
|
|
|
|
|
<div class="chip red-chip"></div>
|
|
|
|
|
<div class="chip blue-chip"></div>
|
|
|
|
|
<div class="chip green-chip"></div>
|
|
|
|
|
<div class="chip black-chip"></div>
|
2025-04-16 10:24:33 +07:00
|
|
|
|
</div>
|
|
|
|
|
|
2025-04-16 14:27:41 +07:00
|
|
|
|
<div class="player-section player-section-user">
|
|
|
|
|
<div class="player-info">
|
|
|
|
|
<div class="player-avatar player-avatar-user"></div>
|
|
|
|
|
<h2 class="player-title">Игрок <span class="score-badge">@PlayerScore</span></h2>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="table-felt">
|
|
|
|
|
<div class="hand player-hand">
|
|
|
|
|
@foreach (var card in PlayerHand)
|
|
|
|
|
{
|
|
|
|
|
<div class="card-container">
|
|
|
|
|
<div class="playing-card @GetCardColor(card)">
|
|
|
|
|
<div class="card-corner top-left">
|
|
|
|
|
<div class="card-rank">@card.Rank</div>
|
|
|
|
|
<div class="card-suit">@card.Suit</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="card-center-suit">@card.Suit</div>
|
|
|
|
|
<div class="card-corner bottom-right">
|
|
|
|
|
<div class="card-rank">@card.Rank</div>
|
|
|
|
|
<div class="card-suit">@card.Suit</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2025-04-16 10:24:33 +07:00
|
|
|
|
|
2025-04-16 14:27:41 +07:00
|
|
|
|
@* Кнопки действий *@
|
|
|
|
|
<div class="action-buttons">
|
|
|
|
|
<button class="btn-3d btn-hit" @onclick="PlayerHit" disabled="@(!IsPlayerTurn || IsGameOver)">
|
|
|
|
|
<span class="btn-icon hit-icon"></span>
|
|
|
|
|
<span>Взять карту</span>
|
|
|
|
|
</button>
|
|
|
|
|
<button class="btn-3d btn-stand" @onclick="PlayerStand" disabled="@(!IsPlayerTurn || IsGameOver)">
|
|
|
|
|
<span class="btn-icon stand-icon"></span>
|
|
|
|
|
<span>Стоп</span>
|
|
|
|
|
</button>
|
|
|
|
|
<button class="btn-3d btn-new-game" @onclick="StartGame">
|
|
|
|
|
<span class="btn-icon new-game-icon"></span>
|
|
|
|
|
<span>Новая игра</span>
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
<div class="start-screen">
|
|
|
|
|
<div class="game-logo"></div>
|
|
|
|
|
<button class="btn-3d btn-start" @onclick="StartGame">Начать игру</button>
|
|
|
|
|
</div>
|
|
|
|
|
}
|
2025-04-16 10:24:33 +07:00
|
|
|
|
</div>
|
2025-04-16 14:27:41 +07:00
|
|
|
|
</div>
|
2025-04-16 10:24:33 +07:00
|
|
|
|
|
|
|
|
|
@code {
|
|
|
|
|
// --- Поля состояния игры ---
|
|
|
|
|
private Deck GameDeck;
|
2025-04-16 14:27:41 +07:00
|
|
|
|
private List<Card> PlayerHand = new List<Card>();
|
|
|
|
|
private List<Card> DealerHand = new List<Card>();
|
2025-04-16 10:24:33 +07:00
|
|
|
|
|
|
|
|
|
// --- Вычисляемые свойства для счета ---
|
|
|
|
|
private int PlayerScore => CalculateScore(PlayerHand);
|
|
|
|
|
private int DealerScore => CalculateScore(DealerHand);
|
|
|
|
|
|
|
|
|
|
// --- Флаги состояния игры ---
|
|
|
|
|
private bool IsPlayerTurn;
|
|
|
|
|
private bool IsGameOver;
|
2025-04-16 14:27:41 +07:00
|
|
|
|
private bool IsGameStarted;
|
|
|
|
|
private string GameMessage = string.Empty;
|
2025-04-16 10:24:33 +07:00
|
|
|
|
|
|
|
|
|
// --- Инициализация компонента ---
|
|
|
|
|
protected override void OnInitialized()
|
|
|
|
|
{
|
|
|
|
|
IsGameStarted = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- Метод начала новой игры ---
|
|
|
|
|
private void StartGame()
|
|
|
|
|
{
|
|
|
|
|
GameDeck = new Deck();
|
|
|
|
|
GameDeck.Shuffle();
|
|
|
|
|
|
|
|
|
|
PlayerHand = new List<Card>();
|
|
|
|
|
DealerHand = new List<Card>();
|
|
|
|
|
|
|
|
|
|
IsGameOver = false;
|
|
|
|
|
IsPlayerTurn = true;
|
|
|
|
|
GameMessage = "Ваш ход. Взять карту или стоп?";
|
2025-04-16 14:27:41 +07:00
|
|
|
|
IsGameStarted = true;
|
2025-04-16 10:24:33 +07:00
|
|
|
|
|
|
|
|
|
// Раздаем по 2 карты
|
|
|
|
|
PlayerHand.Add(GameDeck.DealCard());
|
2025-04-16 14:27:41 +07:00
|
|
|
|
DealerHand.Add(GameDeck.DealCard());
|
2025-04-16 10:24:33 +07:00
|
|
|
|
PlayerHand.Add(GameDeck.DealCard());
|
2025-04-16 14:27:41 +07:00
|
|
|
|
DealerHand.Add(GameDeck.DealCard());
|
2025-04-16 10:24:33 +07:00
|
|
|
|
|
2025-04-16 14:27:41 +07:00
|
|
|
|
// Проверка на блэкджек
|
|
|
|
|
if (PlayerScore == 21 && DealerHand.Count == 2)
|
2025-04-16 10:24:33 +07:00
|
|
|
|
{
|
2025-04-16 14:27:41 +07:00
|
|
|
|
if (DealerScore == 21)
|
2025-04-16 10:24:33 +07:00
|
|
|
|
{
|
|
|
|
|
RevealDealerCardAndEndGame("Ничья! У обоих Блэкджек!");
|
|
|
|
|
}
|
2025-04-16 14:27:41 +07:00
|
|
|
|
else
|
2025-04-16 10:24:33 +07:00
|
|
|
|
{
|
|
|
|
|
RevealDealerCardAndEndGame("Блэкджек! Вы выиграли!");
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-04-16 14:27:41 +07:00
|
|
|
|
else if (DealerScore == 21 && DealerHand.Count == 2)
|
2025-04-16 10:24:33 +07:00
|
|
|
|
{
|
|
|
|
|
RevealDealerCardAndEndGame("Блэкджек у дилера! Вы проиграли.");
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-16 14:27:41 +07:00
|
|
|
|
StateHasChanged();
|
2025-04-16 10:24:33 +07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- Действие игрока: Взять карту (Hit) ---
|
|
|
|
|
private void PlayerHit()
|
|
|
|
|
{
|
2025-04-16 14:27:41 +07:00
|
|
|
|
if (!IsPlayerTurn || IsGameOver) return;
|
2025-04-16 10:24:33 +07:00
|
|
|
|
|
|
|
|
|
PlayerHand.Add(GameDeck.DealCard());
|
|
|
|
|
|
|
|
|
|
if (PlayerScore > 21)
|
|
|
|
|
{
|
|
|
|
|
EndGame("Перебор! Вы проиграли.");
|
|
|
|
|
}
|
|
|
|
|
else if (PlayerScore == 21)
|
|
|
|
|
{
|
|
|
|
|
PlayerStand();
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-16 14:27:41 +07:00
|
|
|
|
StateHasChanged();
|
2025-04-16 10:24:33 +07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- Действие игрока: Стоп (Stand) ---
|
|
|
|
|
private void PlayerStand()
|
|
|
|
|
{
|
2025-04-16 14:27:41 +07:00
|
|
|
|
if (!IsPlayerTurn || IsGameOver) return;
|
2025-04-16 10:24:33 +07:00
|
|
|
|
|
2025-04-16 14:27:41 +07:00
|
|
|
|
IsPlayerTurn = false;
|
2025-04-16 10:24:33 +07:00
|
|
|
|
GameMessage = "Дилер ходит...";
|
2025-04-16 14:27:41 +07:00
|
|
|
|
StateHasChanged();
|
2025-04-16 10:24:33 +07:00
|
|
|
|
|
2025-04-16 14:27:41 +07:00
|
|
|
|
DealerTurn();
|
2025-04-16 10:24:33 +07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- Логика хода Дилера ---
|
|
|
|
|
private void DealerTurn()
|
|
|
|
|
{
|
|
|
|
|
while (DealerScore < 17)
|
|
|
|
|
{
|
|
|
|
|
DealerHand.Add(GameDeck.DealCard());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DetermineWinner();
|
2025-04-16 14:27:41 +07:00
|
|
|
|
StateHasChanged();
|
2025-04-16 10:24:33 +07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- Подсчет очков для руки ---
|
|
|
|
|
private int CalculateScore(List<Card> hand)
|
|
|
|
|
{
|
|
|
|
|
if (hand == null || hand.Count == 0) return 0;
|
|
|
|
|
|
|
|
|
|
int score = 0;
|
|
|
|
|
int aceCount = 0;
|
|
|
|
|
foreach (var card in hand)
|
|
|
|
|
{
|
|
|
|
|
score += card.Value;
|
|
|
|
|
if (card.Rank == "A")
|
|
|
|
|
{
|
|
|
|
|
aceCount++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-16 14:27:41 +07:00
|
|
|
|
// Корректировка значения Тузов
|
2025-04-16 10:24:33 +07:00
|
|
|
|
while (score > 21 && aceCount > 0)
|
|
|
|
|
{
|
2025-04-16 14:27:41 +07:00
|
|
|
|
score -= 10;
|
2025-04-16 10:24:33 +07:00
|
|
|
|
aceCount--;
|
|
|
|
|
}
|
|
|
|
|
return score;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- Определение победителя ---
|
|
|
|
|
private void DetermineWinner()
|
|
|
|
|
{
|
|
|
|
|
if (PlayerScore > 21)
|
|
|
|
|
{
|
|
|
|
|
EndGame("Перебор! Вы проиграли.");
|
|
|
|
|
}
|
|
|
|
|
else if (DealerScore > 21)
|
|
|
|
|
{
|
|
|
|
|
EndGame("У дилера перебор! Вы выиграли!");
|
|
|
|
|
}
|
|
|
|
|
else if (PlayerScore > DealerScore)
|
|
|
|
|
{
|
|
|
|
|
EndGame("Вы выиграли!");
|
|
|
|
|
}
|
|
|
|
|
else if (DealerScore > PlayerScore)
|
|
|
|
|
{
|
|
|
|
|
EndGame("Дилер выиграл!");
|
|
|
|
|
}
|
|
|
|
|
else // Очки равны
|
|
|
|
|
{
|
|
|
|
|
EndGame("Ничья (Push)!");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- Метод для завершения игры и установки сообщения ---
|
|
|
|
|
private void RevealDealerCardAndEndGame(string message)
|
|
|
|
|
{
|
2025-04-16 14:27:41 +07:00
|
|
|
|
IsPlayerTurn = false;
|
2025-04-16 10:24:33 +07:00
|
|
|
|
EndGame(message);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void EndGame(string message)
|
|
|
|
|
{
|
|
|
|
|
IsGameOver = true;
|
2025-04-16 14:27:41 +07:00
|
|
|
|
IsPlayerTurn = false;
|
2025-04-16 10:24:33 +07:00
|
|
|
|
GameMessage = message;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- Вспомогательный метод для определения цвета карты ---
|
|
|
|
|
private string GetCardColor(Card card)
|
|
|
|
|
{
|
|
|
|
|
return (card.Suit == "♥" || card.Suit == "♦") ? "card-red" : "card-black";
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-16 14:27:41 +07:00
|
|
|
|
// --- Реализация IDisposable ---
|
2025-04-16 10:24:33 +07:00
|
|
|
|
public void Dispose()
|
|
|
|
|
{
|
2025-04-16 14:27:41 +07:00
|
|
|
|
// Реализация IDisposable
|
2025-04-16 10:24:33 +07:00
|
|
|
|
}
|
|
|
|
|
}
|