313 lines
11 KiB
Plaintext
313 lines
11 KiB
Plaintext
@page "/"
|
||
@page "/blackjack"
|
||
@using БлэкДжек.Components
|
||
@implements IDisposable
|
||
@rendermode InteractiveServer
|
||
|
||
<div class="casino-background">
|
||
<div class="blackjack-container">
|
||
<div class="game-header">
|
||
<h1 class="game-title">Blackjack</h1>
|
||
<div class="chip-stack"></div>
|
||
</div>
|
||
|
||
@* Область для сообщений игры *@
|
||
@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>
|
||
}
|
||
|
||
@* Основной блок игры, отображается после нажатия "Начать" *@
|
||
@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>
|
||
}
|
||
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>
|
||
</div>
|
||
|
||
<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>
|
||
</div>
|
||
|
||
<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>
|
||
|
||
@* Кнопки действий *@
|
||
<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>
|
||
}
|
||
</div>
|
||
</div>
|
||
|
||
@code {
|
||
// --- Поля состояния игры ---
|
||
private Deck GameDeck;
|
||
private List<Card> PlayerHand = new List<Card>();
|
||
private List<Card> DealerHand = new List<Card>();
|
||
|
||
// --- Вычисляемые свойства для счета ---
|
||
private int PlayerScore => CalculateScore(PlayerHand);
|
||
private int DealerScore => CalculateScore(DealerHand);
|
||
|
||
// --- Флаги состояния игры ---
|
||
private bool IsPlayerTurn;
|
||
private bool IsGameOver;
|
||
private bool IsGameStarted;
|
||
private string GameMessage = string.Empty;
|
||
|
||
// --- Инициализация компонента ---
|
||
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 = "Ваш ход. Взять карту или стоп?";
|
||
IsGameStarted = true;
|
||
|
||
// Раздаем по 2 карты
|
||
PlayerHand.Add(GameDeck.DealCard());
|
||
DealerHand.Add(GameDeck.DealCard());
|
||
PlayerHand.Add(GameDeck.DealCard());
|
||
DealerHand.Add(GameDeck.DealCard());
|
||
|
||
// Проверка на блэкджек
|
||
if (PlayerScore == 21 && DealerHand.Count == 2)
|
||
{
|
||
if (DealerScore == 21)
|
||
{
|
||
RevealDealerCardAndEndGame("Ничья! У обоих Блэкджек!");
|
||
}
|
||
else
|
||
{
|
||
RevealDealerCardAndEndGame("Блэкджек! Вы выиграли!");
|
||
}
|
||
}
|
||
else if (DealerScore == 21 && DealerHand.Count == 2)
|
||
{
|
||
RevealDealerCardAndEndGame("Блэкджек у дилера! Вы проиграли.");
|
||
}
|
||
|
||
StateHasChanged();
|
||
}
|
||
|
||
// --- Действие игрока: Взять карту (Hit) ---
|
||
private void PlayerHit()
|
||
{
|
||
if (!IsPlayerTurn || IsGameOver) return;
|
||
|
||
var newCard = GameDeck.DealCard();
|
||
if (newCard != null)
|
||
{
|
||
PlayerHand.Add(newCard);
|
||
|
||
if (PlayerScore > 21)
|
||
{
|
||
EndGame("Перебор! Вы проиграли.");
|
||
}
|
||
else if (PlayerScore == 21)
|
||
{
|
||
PlayerStand();
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// Колода пуста, можно обработать этот случай
|
||
GameMessage = "Колода пуста! Создается новая колода...";
|
||
GameDeck = new Deck();
|
||
GameDeck.Shuffle();
|
||
}
|
||
|
||
StateHasChanged();
|
||
}
|
||
|
||
// --- Действие игрока: Стоп (Stand) ---
|
||
private void PlayerStand()
|
||
{
|
||
if (!IsPlayerTurn || IsGameOver) return;
|
||
|
||
IsPlayerTurn = false;
|
||
GameMessage = "Дилер ходит...";
|
||
StateHasChanged();
|
||
|
||
DealerTurn();
|
||
}
|
||
|
||
// --- Логика хода Дилера ---
|
||
private void DealerTurn()
|
||
{
|
||
while (DealerScore < 17)
|
||
{
|
||
DealerHand.Add(GameDeck.DealCard());
|
||
}
|
||
|
||
DetermineWinner();
|
||
StateHasChanged();
|
||
}
|
||
|
||
// --- Подсчет очков для руки ---
|
||
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++;
|
||
}
|
||
}
|
||
|
||
// Корректировка значения Тузов
|
||
while (score > 21 && aceCount > 0)
|
||
{
|
||
score -= 10;
|
||
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)
|
||
{
|
||
IsPlayerTurn = false;
|
||
EndGame(message);
|
||
}
|
||
|
||
private void EndGame(string message)
|
||
{
|
||
IsGameOver = true;
|
||
IsPlayerTurn = false;
|
||
GameMessage = message;
|
||
}
|
||
|
||
// --- Вспомогательный метод для определения цвета карты ---
|
||
private string GetCardColor(Card card)
|
||
{
|
||
return (card.Suit == "♥" || card.Suit == "♦") ? "card-red" : "card-black";
|
||
}
|
||
|
||
// --- Реализация IDisposable ---
|
||
public void Dispose()
|
||
{
|
||
// Реализация IDisposable
|
||
}
|
||
}
|