Добавьте файлы проекта.
This commit is contained in:
parent
419186f710
commit
92c18b985a
18
Components/App.razor
Normal file
18
Components/App.razor
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap" rel="stylesheet">
|
||||||
|
<link href="css/app.css" rel="stylesheet" />
|
||||||
|
<!-- Если имя вашего проекта другое, ссылка на app.css может отличаться -->
|
||||||
|
<link href="BlazorBlackjack.styles.css" rel="stylesheet" /> <!-- Эта строка важна для изолированных стилей -->
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<Routes @rendermode="InteractiveServer" />
|
||||||
|
<script src="_framework/blazor.web.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
9
Components/Layout/MainLayout.razor
Normal file
9
Components/Layout/MainLayout.razor
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
@inherits LayoutComponentBase
|
||||||
|
|
||||||
|
@Body
|
||||||
|
|
||||||
|
<div id="blazor-error-ui">
|
||||||
|
An unhandled error has occurred.
|
||||||
|
<a href="" class="reload">Reload</a>
|
||||||
|
<a class="dismiss">🗙</a>
|
||||||
|
</div>
|
18
Components/Layout/MainLayout.razor.css
Normal file
18
Components/Layout/MainLayout.razor.css
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#blazor-error-ui {
|
||||||
|
background: lightyellow;
|
||||||
|
bottom: 0;
|
||||||
|
box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
|
||||||
|
display: none;
|
||||||
|
left: 0;
|
||||||
|
padding: 0.6rem 1.25rem 0.7rem 1.25rem;
|
||||||
|
position: fixed;
|
||||||
|
width: 100%;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
#blazor-error-ui .dismiss {
|
||||||
|
cursor: pointer;
|
||||||
|
position: absolute;
|
||||||
|
right: 0.75rem;
|
||||||
|
top: 0.5rem;
|
||||||
|
}
|
264
Components/Pages/BlackjackGame.razor
Normal file
264
Components/Pages/BlackjackGame.razor
Normal file
@ -0,0 +1,264 @@
|
|||||||
|
@* --- НАЧАЛО КОДА ДЛЯ BlackjackGame.razor --- *@
|
||||||
|
|
||||||
|
@page "/blackjack"
|
||||||
|
@using БлэкДжек.Components @* <--- ВОТ ЭТУ СТРОКУ НУЖНО ПРОВЕРИТЬ/ЗАМЕНИТЬ *@
|
||||||
|
@implements IDisposable
|
||||||
|
|
||||||
|
<h3>Blackjack</h3>
|
||||||
|
|
||||||
|
@* Область для сообщений игры *@
|
||||||
|
@if (!string.IsNullOrEmpty(GameMessage))
|
||||||
|
{
|
||||||
|
<div class="alert @(IsGameOver && PlayerScore <= 21 && (DealerScore > 21 || PlayerScore > DealerScore) ? "alert-success" : "alert-info")" role="alert">
|
||||||
|
@GameMessage
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
@* Основной блок игры, отображается после нажатия "Начать" *@
|
||||||
|
@if (IsGameStarted)
|
||||||
|
{
|
||||||
|
<div>
|
||||||
|
<h4>Дилер (@(IsPlayerTurn ? "?" : DealerScore.ToString()))</h4>
|
||||||
|
<div class="hand">
|
||||||
|
@foreach (var card in DealerHand)
|
||||||
|
{
|
||||||
|
@* Условие для скрытия первой карты дилера во время хода игрока *@
|
||||||
|
<div class="card @GetCardColor(card)">
|
||||||
|
@((DealerHand.IndexOf(card) == 0 && IsPlayerTurn) ? "???" : card.Display)
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h4>Игрок (@PlayerScore)</h4>
|
||||||
|
<div class="hand">
|
||||||
|
@foreach (var card in PlayerHand)
|
||||||
|
{
|
||||||
|
<div class="card @GetCardColor(card)">
|
||||||
|
@card.Display
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
@* Кнопки действий *@
|
||||||
|
<div class="actions">
|
||||||
|
<button class="btn btn-primary" @onclick="PlayerHit" disabled="@(!IsPlayerTurn || IsGameOver)">Взять карту (Hit)</button>
|
||||||
|
<button class="btn btn-secondary" @onclick="PlayerStand" disabled="@(!IsPlayerTurn || IsGameOver)">Стоп (Stand)</button>
|
||||||
|
<button class="btn btn-warning" @onclick="StartGame">Новая игра</button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
@* Кнопка для старта игры, видна только до начала *@
|
||||||
|
<button class="btn btn-success btn-lg" @onclick="StartGame">Начать игру</button>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@* Блок с C# кодом *@
|
||||||
|
@code {
|
||||||
|
// --- Поля состояния игры ---
|
||||||
|
private Deck GameDeck;
|
||||||
|
private List<Card> PlayerHand = new List<Card>(); // Инициализируем, чтобы избежать null reference до старта
|
||||||
|
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(); // Обновляем UI, чтобы показать начальное состояние
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// --- Действие игрока: Взять карту (Hit) ---
|
||||||
|
private void PlayerHit()
|
||||||
|
{
|
||||||
|
if (!IsPlayerTurn || IsGameOver) return; // Нельзя брать карту не в свой ход или если игра окончена
|
||||||
|
|
||||||
|
PlayerHand.Add(GameDeck.DealCard());
|
||||||
|
|
||||||
|
if (PlayerScore > 21)
|
||||||
|
{
|
||||||
|
EndGame("Перебор! Вы проиграли.");
|
||||||
|
}
|
||||||
|
else if (PlayerScore == 21)
|
||||||
|
{
|
||||||
|
// Если игрок набрал ровно 21, его ход автоматически завершается
|
||||||
|
PlayerStand();
|
||||||
|
}
|
||||||
|
// Если меньше 21, игрок может брать еще
|
||||||
|
|
||||||
|
StateHasChanged(); // Обновляем UI после взятия карты
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Действие игрока: Стоп (Stand) ---
|
||||||
|
private void PlayerStand()
|
||||||
|
{
|
||||||
|
if (!IsPlayerTurn || IsGameOver) return; // Нельзя остановиться не в свой ход или если игра окончена
|
||||||
|
|
||||||
|
IsPlayerTurn = false; // Передаем ход дилеру
|
||||||
|
GameMessage = "Дилер ходит...";
|
||||||
|
StateHasChanged(); // Обновим сообщение и откроем карту дилера
|
||||||
|
|
||||||
|
// Можно добавить небольшую задержку перед ходом дилера для наглядности
|
||||||
|
// await Task.Delay(500); // Если раскомментировать, метод должен быть async Task
|
||||||
|
DealerTurn(); // Начинаем ход дилера
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// --- Логика хода Дилера ---
|
||||||
|
// Простая синхронная версия. Для пауз нужен async/await и Task.Delay
|
||||||
|
private void DealerTurn()
|
||||||
|
{
|
||||||
|
// Дилер берет карты, пока его счет меньше 17
|
||||||
|
while (DealerScore < 17)
|
||||||
|
{
|
||||||
|
// В реальном приложении можно добавить паузу здесь
|
||||||
|
// await Task.Delay(800);
|
||||||
|
DealerHand.Add(GameDeck.DealCard());
|
||||||
|
// StateHasChanged(); // Обновлять UI после каждой карты дилера, если есть пауза
|
||||||
|
}
|
||||||
|
|
||||||
|
// После того, как дилер закончил брать карты, определяем победителя
|
||||||
|
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++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Корректировка значения Тузов (с 11 на 1 при переборе)
|
||||||
|
while (score > 21 && aceCount > 0)
|
||||||
|
{
|
||||||
|
score -= 10; // Считаем Туз как 1 вместо 11
|
||||||
|
aceCount--;
|
||||||
|
}
|
||||||
|
return score;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Определение победителя ---
|
||||||
|
private void DetermineWinner()
|
||||||
|
{
|
||||||
|
// Убедимся, что карта дилера видна (IsPlayerTurn уже false)
|
||||||
|
if (PlayerScore > 21)
|
||||||
|
{
|
||||||
|
// Это условие уже должно было быть обработано в PlayerHit, но на всякий случай
|
||||||
|
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;
|
||||||
|
// StateHasChanged(); // Обычно вызывается в методе, который вызвал EndGame
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Вспомогательный метод для определения цвета карты ---
|
||||||
|
private string GetCardColor(Card card)
|
||||||
|
{
|
||||||
|
return (card.Suit == "♥" || card.Suit == "♦") ? "card-red" : "card-black";
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Реализация IDisposable (пока пустая, но может пригодиться) ---
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
// Здесь можно остановить таймеры или отписаться от событий, если они будут добавлены
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@* --- КОНЕЦ КОДА ДЛЯ BlackjackGame.razor --- *@
|
169
Components/Pages/BlackjackGame.razor.razor
Normal file
169
Components/Pages/BlackjackGame.razor.razor
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
/* BlackjackGame.razor.css - Стили для компонента Blackjack */
|
||||||
|
|
||||||
|
/* Общие стили для секций игрока и дилера */
|
||||||
|
h4 {
|
||||||
|
margin-bottom: 0.8rem; /* Немного больше отступа снизу */
|
||||||
|
font-weight: 600; /* Немного жирнее */
|
||||||
|
color: #e0e0e0; /* Светлый цвет для темного фона (если будете добавлять) */
|
||||||
|
text-shadow: 1px 1px 2px rgba(0,0,0,0.5); /* Легкая тень для читаемости */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Контейнер для карт (рука) */
|
||||||
|
.hand {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap; /* Карты будут переноситься, если не помещаются */
|
||||||
|
gap: 10px; /* Пространство между картами */
|
||||||
|
min-height: 95px; /* Минимальная высота, чтобы контейнер не схлопывался без карт */
|
||||||
|
margin-bottom: 1.5rem; /* Отступ под рукой */
|
||||||
|
padding: 8px;
|
||||||
|
background-color: rgba(0, 0, 0, 0.1); /* Легкий фон для области карт */
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1); /* Тонкая светлая рамка */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Стиль для отдельной карты */
|
||||||
|
.card {
|
||||||
|
background-color: #fff; /* Белый фон карты */
|
||||||
|
border: 1px solid #ababab; /* Серая рамка */
|
||||||
|
border-radius: 6px; /* Скругленные углы */
|
||||||
|
padding: 15px 10px; /* Внутренние отступы (больше по вертикали) */
|
||||||
|
min-width: 65px; /* Минимальная ширина карты */
|
||||||
|
text-align: center;
|
||||||
|
font-size: 1.5em; /* Размер шрифта ранга/масти */
|
||||||
|
font-weight: bold;
|
||||||
|
box-shadow: 3px 3px 7px rgba(0, 0, 0, 0.25); /* Тень для объема */
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
user-select: none; /* Запретить выделение текста на карте */
|
||||||
|
transition: transform 0.1s ease-in-out; /* Плавность при наведении (если добавим) */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Если захотите эффект при наведении */
|
||||||
|
/*
|
||||||
|
.card:hover {
|
||||||
|
transform: translateY(-3px);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Стиль для скрытой карты дилера ("???") */
|
||||||
|
/* Мы не можем легко стилизовать сам текст "???", но можем стилизовать */
|
||||||
|
/* первую карту в руке дилера, пока ход игрока. */
|
||||||
|
/* Однако, текущая логика просто выводит текст "???" в .card */
|
||||||
|
/* Давайте просто сделаем текст "?" серым, если бы мы обернули его в span */
|
||||||
|
/* Если вы изменили Razor, как обсуждалось ранее: */
|
||||||
|
/*
|
||||||
|
.hidden-card-content {
|
||||||
|
color: #9e9e9e;
|
||||||
|
font-style: italic;
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
/* В текущем варианте специальный стиль для "???" не применяется, она будет в стандартной карте */
|
||||||
|
|
||||||
|
/* Цвета для мастей */
|
||||||
|
.card-red {
|
||||||
|
color: #D32F2F; /* Насыщенный красный */
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-black {
|
||||||
|
color: #212121; /* Глубокий черный */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Контейнер для кнопок действий */
|
||||||
|
.actions {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 12px; /* Расстояние между кнопками */
|
||||||
|
margin-top: 1.5rem; /* Отступ сверху */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Стиль для кнопок */
|
||||||
|
.actions button,
|
||||||
|
.btn-lg { /* Стиль для кнопки "Начать игру" */
|
||||||
|
padding: 10px 20px; /* Увеличим кнопки */
|
||||||
|
font-size: 1em;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 5px;
|
||||||
|
border: none; /* Убираем стандартную рамку Bootstrap */
|
||||||
|
transition: background-color 0.2s ease, transform 0.1s ease; /* Плавные переходы */
|
||||||
|
box-shadow: 0 2px 5px rgba(0,0,0,0.2); /* Легкая тень для кнопок */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Улучшенные стили для кнопок Bootstrap (можно настроить под себя) */
|
||||||
|
.actions .btn-primary { /* Взять карту */
|
||||||
|
background-color: #1976D2; /* Синий */
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
.actions .btn-primary:hover:not(:disabled) {
|
||||||
|
background-color: #1565C0;
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions .btn-secondary { /* Стоп */
|
||||||
|
background-color: #6c757d; /* Серый */
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
.actions .btn-secondary:hover:not(:disabled) {
|
||||||
|
background-color: #5a6268;
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions .btn-warning { /* Новая игра */
|
||||||
|
background-color: #FFA000; /* Оранжевый */
|
||||||
|
color: #212121; /* Темный текст на оранжевом */
|
||||||
|
}
|
||||||
|
.actions .btn-warning:hover:not(:disabled) {
|
||||||
|
background-color: #FF8F00;
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Кнопка "Начать игру" */
|
||||||
|
.btn-success.btn-lg {
|
||||||
|
background-color: #388E3C; /* Зеленый */
|
||||||
|
color: white;
|
||||||
|
font-size: 1.2em; /* Крупнее */
|
||||||
|
padding: 12px 25px;
|
||||||
|
}
|
||||||
|
.btn-success.btn-lg:hover:not(:disabled) {
|
||||||
|
background-color: #2E7D32;
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Стиль для отключенных кнопок */
|
||||||
|
.actions button:disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 0.5; /* Сделать полупрозрачными */
|
||||||
|
box-shadow: none; /* Убрать тень у неактивных */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Сообщения игры */
|
||||||
|
.alert {
|
||||||
|
margin-top: 1rem;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-weight: 500;
|
||||||
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Успешное сообщение (выигрыш) */
|
||||||
|
.alert-success {
|
||||||
|
background-color: #E8F5E9; /* Светло-зеленый фон */
|
||||||
|
color: #2E7D32; /* Темно-зеленый текст */
|
||||||
|
border: 1px solid #A5D6A7; /* Зеленая рамка */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Информационное сообщение (ход игры, ничья, проигрыш) */
|
||||||
|
.alert-info {
|
||||||
|
background-color: #E3F2FD; /* Светло-голубой фон */
|
||||||
|
color: #1565C0; /* Темно-синий текст */
|
||||||
|
border: 1px solid #90CAF9; /* Голубая рамка */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Разделитель */
|
||||||
|
hr {
|
||||||
|
border: none; /* Убираем стандартную линию */
|
||||||
|
border-top: 1px solid rgba(255, 255, 255, 0.2); /* Светлая полупрозрачная линия */
|
||||||
|
margin: 2rem 0; /* Увеличим отступы */
|
||||||
|
}
|
36
Components/Pages/Error.razor
Normal file
36
Components/Pages/Error.razor
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
@page "/Error"
|
||||||
|
@using System.Diagnostics
|
||||||
|
|
||||||
|
<PageTitle>Error</PageTitle>
|
||||||
|
|
||||||
|
<h1 class="text-danger">Error.</h1>
|
||||||
|
<h2 class="text-danger">An error occurred while processing your request.</h2>
|
||||||
|
|
||||||
|
@if (ShowRequestId)
|
||||||
|
{
|
||||||
|
<p>
|
||||||
|
<strong>Request ID:</strong> <code>@RequestId</code>
|
||||||
|
</p>
|
||||||
|
}
|
||||||
|
|
||||||
|
<h3>Development Mode</h3>
|
||||||
|
<p>
|
||||||
|
Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
|
||||||
|
It can result in displaying sensitive information from exceptions to end users.
|
||||||
|
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
|
||||||
|
and restarting the app.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
@code{
|
||||||
|
[CascadingParameter]
|
||||||
|
private HttpContext? HttpContext { get; set; }
|
||||||
|
|
||||||
|
private string? RequestId { get; set; }
|
||||||
|
private bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
|
||||||
|
|
||||||
|
protected override void OnInitialized() =>
|
||||||
|
RequestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier;
|
||||||
|
}
|
7
Components/Pages/Home.razor
Normal file
7
Components/Pages/Home.razor
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
@page "/"
|
||||||
|
|
||||||
|
<PageTitle>Home</PageTitle>
|
||||||
|
|
||||||
|
<h1>Hello, world!</h1>
|
||||||
|
|
||||||
|
Welcome to your new app.
|
6
Components/Routes.razor
Normal file
6
Components/Routes.razor
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<Router AppAssembly="typeof(Program).Assembly">
|
||||||
|
<Found Context="routeData">
|
||||||
|
<RouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)" />
|
||||||
|
<FocusOnNavigate RouteData="routeData" Selector="h1" />
|
||||||
|
</Found>
|
||||||
|
</Router>
|
10
Components/_Imports.razor
Normal file
10
Components/_Imports.razor
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
@using System.Net.Http
|
||||||
|
@using System.Net.Http.Json
|
||||||
|
@using Microsoft.AspNetCore.Components.Forms
|
||||||
|
@using Microsoft.AspNetCore.Components.Routing
|
||||||
|
@using Microsoft.AspNetCore.Components.Web
|
||||||
|
@using static Microsoft.AspNetCore.Components.Web.RenderMode
|
||||||
|
@using Microsoft.AspNetCore.Components.Web.Virtualization
|
||||||
|
@using Microsoft.JSInterop
|
||||||
|
@using БлэкДжек
|
||||||
|
@using БлэкДжек.Components
|
22
Models/Card.cs
Normal file
22
Models/Card.cs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
namespace БлэкДжек.Components // Замените BlazorBlackjack на имя вашего проекта
|
||||||
|
{
|
||||||
|
public class Card
|
||||||
|
{
|
||||||
|
public string Suit { get; set; } // Масть (♥, ♦, ♣, ♠)
|
||||||
|
public string Rank { get; set; } // Ранг (2, 3, ..., 10, J, Q, K, A)
|
||||||
|
public int Value { get; set; } // Значение (J,Q,K = 10, A = 11 или 1)
|
||||||
|
|
||||||
|
// Для удобного отображения
|
||||||
|
public string Display => $"{Rank}{Suit}";
|
||||||
|
// Можно добавить свойство для пути к изображению карты, если хотите графику
|
||||||
|
// public string ImagePath => $"images/cards/{Rank.ToLower()}{SuitChar}.png";
|
||||||
|
// private char SuitChar => Suit switch { "♥" => 'h', "♦" => 'd', "♣" => 'c', "♠" => 's', _ => ' ' };
|
||||||
|
|
||||||
|
public Card(string suit, string rank, int value)
|
||||||
|
{
|
||||||
|
Suit = suit;
|
||||||
|
Rank = rank;
|
||||||
|
Value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
66
Models/Deck.cs
Normal file
66
Models/Deck.cs
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace БлэкДжек.Components // Замените BlazorBlackjack на имя вашего проекта
|
||||||
|
{
|
||||||
|
public class Deck
|
||||||
|
{
|
||||||
|
public List<Card> Cards { get; private set; }
|
||||||
|
|
||||||
|
public Deck()
|
||||||
|
{
|
||||||
|
InitializeDeck();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeDeck()
|
||||||
|
{
|
||||||
|
Cards = new List<Card>();
|
||||||
|
string[] suits = { "♥", "♦", "♣", "♠" };
|
||||||
|
string[] ranks = { "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A" };
|
||||||
|
|
||||||
|
foreach (var suit in suits)
|
||||||
|
{
|
||||||
|
foreach (var rank in ranks)
|
||||||
|
{
|
||||||
|
int value = 0;
|
||||||
|
if (int.TryParse(rank, out int numValue))
|
||||||
|
{
|
||||||
|
value = numValue;
|
||||||
|
}
|
||||||
|
else if (rank == "J" || rank == "Q" || rank == "K")
|
||||||
|
{
|
||||||
|
value = 10;
|
||||||
|
}
|
||||||
|
else if (rank == "A")
|
||||||
|
{
|
||||||
|
value = 11; // Туз по умолчанию 11
|
||||||
|
}
|
||||||
|
Cards.Add(new Card(suit, rank, value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Shuffle()
|
||||||
|
{
|
||||||
|
Random rng = new Random();
|
||||||
|
Cards = Cards.OrderBy(c => rng.Next()).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Card DealCard()
|
||||||
|
{
|
||||||
|
if (Cards.Count == 0)
|
||||||
|
{
|
||||||
|
// Можно пересоздать и перемешать колоду, если она закончилась
|
||||||
|
// InitializeDeck();
|
||||||
|
// Shuffle();
|
||||||
|
// Или просто вернуть null/выбросить исключение
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Card card = Cards[0];
|
||||||
|
Cards.RemoveAt(0);
|
||||||
|
return card;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
27
Program.cs
Normal file
27
Program.cs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
using БлэкДжек.Components;
|
||||||
|
|
||||||
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
|
// Add services to the container.
|
||||||
|
builder.Services.AddRazorComponents()
|
||||||
|
.AddInteractiveServerComponents();
|
||||||
|
|
||||||
|
var app = builder.Build();
|
||||||
|
|
||||||
|
// Configure the HTTP request pipeline.
|
||||||
|
if (!app.Environment.IsDevelopment())
|
||||||
|
{
|
||||||
|
app.UseExceptionHandler("/Error", createScopeForErrors: true);
|
||||||
|
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
|
||||||
|
app.UseHsts();
|
||||||
|
}
|
||||||
|
|
||||||
|
app.UseHttpsRedirection();
|
||||||
|
|
||||||
|
app.UseStaticFiles();
|
||||||
|
app.UseAntiforgery();
|
||||||
|
|
||||||
|
app.MapRazorComponents<App>()
|
||||||
|
.AddInteractiveServerRenderMode();
|
||||||
|
|
||||||
|
app.Run();
|
38
Properties/launchSettings.json
Normal file
38
Properties/launchSettings.json
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json.schemastore.org/launchsettings.json",
|
||||||
|
"iisSettings": {
|
||||||
|
"windowsAuthentication": false,
|
||||||
|
"anonymousAuthentication": true,
|
||||||
|
"iisExpress": {
|
||||||
|
"applicationUrl": "http://localhost:10828",
|
||||||
|
"sslPort": 44355
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"profiles": {
|
||||||
|
"http": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"dotnetRunMessages": true,
|
||||||
|
"launchBrowser": true,
|
||||||
|
"applicationUrl": "http://localhost:5097",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"https": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"dotnetRunMessages": true,
|
||||||
|
"launchBrowser": true,
|
||||||
|
"applicationUrl": "https://localhost:7115;http://localhost:5097",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"IIS Express": {
|
||||||
|
"commandName": "IISExpress",
|
||||||
|
"launchBrowser": true,
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
8
appsettings.Development.json
Normal file
8
appsettings.Development.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft.AspNetCore": "Warning"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
9
appsettings.json
Normal file
9
appsettings.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft.AspNetCore": "Warning"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AllowedHosts": "*"
|
||||||
|
}
|
29
wwwroot/app.css
Normal file
29
wwwroot/app.css
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
h1:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.valid.modified:not([type=checkbox]) {
|
||||||
|
outline: 1px solid #26b050;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invalid {
|
||||||
|
outline: 1px solid #e50000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.validation-message {
|
||||||
|
color: #e50000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blazor-error-boundary {
|
||||||
|
background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
|
||||||
|
padding: 1rem 1rem 1rem 3.7rem;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blazor-error-boundary::after {
|
||||||
|
content: "An error has occurred."
|
||||||
|
}
|
||||||
|
|
||||||
|
.darker-border-checkbox.form-check-input {
|
||||||
|
border-color: #929292;
|
||||||
|
}
|
9
БлэкДжек.csproj
Normal file
9
БлэкДжек.csproj
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
</Project>
|
25
БлэкДжек.sln
Normal file
25
БлэкДжек.sln
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.13.35828.75 d17.13
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "БлэкДжек", "БлэкДжек.csproj", "{11339889-A9DE-4748-A250-599F6D56EDA1}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{11339889-A9DE-4748-A250-599F6D56EDA1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{11339889-A9DE-4748-A250-599F6D56EDA1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{11339889-A9DE-4748-A250-599F6D56EDA1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{11339889-A9DE-4748-A250-599F6D56EDA1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {816006BE-33DE-493A-8068-5FFC09509947}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
Loading…
x
Reference in New Issue
Block a user