вреер

This commit is contained in:
student 2025-04-16 14:27:41 +07:00
parent 92c18b985a
commit 7427ce5067
8 changed files with 762 additions and 278 deletions

View File

@ -1,13 +1,18 @@
<!DOCTYPE html>
<html lang="en">
@using Microsoft.AspNetCore.Components.Routing
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<base href="/" />
<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" /> <!-- Эта строка важна для изолированных стилей -->
<link href="БлэкДжек.styles.css" rel="stylesheet" />
<HeadOutlet />
</head>
<body>

View File

@ -1,72 +1,128 @@
@* --- НАЧАЛО КОДА ДЛЯ BlackjackGame.razor --- *@
@page "/blackjack"
@using БлэкДжек.Components @* <--- ВОТ ЭТУ СТРОКУ НУЖНО ПРОВЕРИТЬ/ЗАМЕНИТЬ *@
@page "/blackjack"
@using БлэкДжек.Components
@implements IDisposable
<h3>Blackjack</h3>
<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="alert @(IsGameOver && PlayerScore <= 21 && (DealerScore > 21 || PlayerScore > DealerScore) ? "alert-success" : "alert-info")" role="alert">
@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>
<h4>Дилер (@(IsPlayerTurn ? "?" : DealerScore.ToString()))</h4>
<div class="hand">
<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 @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-container @(DealerHand.IndexOf(card) == 0 && IsPlayerTurn ? "card-hidden" : "")">
@if (DealerHand.IndexOf(card) == 0 && IsPlayerTurn)
{
<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 class="playing-card card-back">
<div class="card-back-design"></div>
</div>
}
else
{
@* Кнопка для старта игры, видна только до начала *@
<button class="btn btn-success btn-lg" @onclick="StartGame">Начать игру</button>
<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>
@* Блок с C# кодом *@
@code {
// --- Поля состояния игры ---
private Deck GameDeck;
private List<Card> PlayerHand = new List<Card>(); // Инициализируем, чтобы избежать null reference до старта
private List<Card> DealerHand = new List<Card>(); // Инициализируем
private List<Card> PlayerHand = new List<Card>();
private List<Card> DealerHand = new List<Card>();
// --- Вычисляемые свойства для счета ---
private int PlayerScore => CalculateScore(PlayerHand);
@ -75,13 +131,12 @@ else
// --- Флаги состояния игры ---
private bool IsPlayerTurn;
private bool IsGameOver;
private bool IsGameStarted; // Показывает, была ли нажата кнопка "Начать игру"
private string GameMessage = string.Empty; // Инициализируем пустой строкой
private bool IsGameStarted;
private string GameMessage = string.Empty;
// --- Инициализация компонента ---
protected override void OnInitialized()
{
// Игра не начинается автоматически при загрузке страницы
IsGameStarted = false;
}
@ -97,40 +152,38 @@ else
IsGameOver = false;
IsPlayerTurn = true;
GameMessage = "Ваш ход. Взять карту или стоп?";
IsGameStarted = true; // Показываем игровое поле
IsGameStarted = true;
// Раздаем по 2 карты
PlayerHand.Add(GameDeck.DealCard());
DealerHand.Add(GameDeck.DealCard()); // Первая карта дилера
DealerHand.Add(GameDeck.DealCard());
PlayerHand.Add(GameDeck.DealCard());
DealerHand.Add(GameDeck.DealCard()); // Вторая карта дилера
DealerHand.Add(GameDeck.DealCard());
// Проверка на блэкджек у игрока или дилера сразу после раздачи
if (PlayerScore == 21 && DealerHand.Count == 2) // У игрока блэкджек
// Проверка на блэкджек
if (PlayerScore == 21 && DealerHand.Count == 2)
{
if (DealerScore == 21) // У дилера тоже блэкджек
if (DealerScore == 21)
{
RevealDealerCardAndEndGame("Ничья! У обоих Блэкджек!");
}
else // Только у игрока блэкджек
else
{
RevealDealerCardAndEndGame("Блэкджек! Вы выиграли!");
}
}
else if (DealerScore == 21 && DealerHand.Count == 2) // Только у дилера блэкджек (проверяем после игрока)
else if (DealerScore == 21 && DealerHand.Count == 2)
{
RevealDealerCardAndEndGame("Блэкджек у дилера! Вы проиграли.");
}
// Если ни у кого нет блэкджека, игра продолжается с сообщением "Ваш ход..."
StateHasChanged(); // Обновляем UI, чтобы показать начальное состояние
StateHasChanged();
}
// --- Действие игрока: Взять карту (Hit) ---
private void PlayerHit()
{
if (!IsPlayerTurn || IsGameOver) return; // Нельзя брать карту не в свой ход или если игра окончена
if (!IsPlayerTurn || IsGameOver) return;
PlayerHand.Add(GameDeck.DealCard());
@ -140,45 +193,34 @@ else
}
else if (PlayerScore == 21)
{
// Если игрок набрал ровно 21, его ход автоматически завершается
PlayerStand();
}
// Если меньше 21, игрок может брать еще
StateHasChanged(); // Обновляем UI после взятия карты
StateHasChanged();
}
// --- Действие игрока: Стоп (Stand) ---
private void PlayerStand()
{
if (!IsPlayerTurn || IsGameOver) return; // Нельзя остановиться не в свой ход или если игра окончена
if (!IsPlayerTurn || IsGameOver) return;
IsPlayerTurn = false; // Передаем ход дилеру
IsPlayerTurn = false;
GameMessage = "Дилер ходит...";
StateHasChanged(); // Обновим сообщение и откроем карту дилера
StateHasChanged();
// Можно добавить небольшую задержку перед ходом дилера для наглядности
// await Task.Delay(500); // Если раскомментировать, метод должен быть async Task
DealerTurn(); // Начинаем ход дилера
DealerTurn();
}
// --- Логика хода Дилера ---
// Простая синхронная версия. Для пауз нужен async/await и Task.Delay
private void DealerTurn()
{
// Дилер берет карты, пока его счет меньше 17
while (DealerScore < 17)
{
// В реальном приложении можно добавить паузу здесь
// await Task.Delay(800);
DealerHand.Add(GameDeck.DealCard());
// StateHasChanged(); // Обновлять UI после каждой карты дилера, если есть пауза
}
// После того, как дилер закончил брать карты, определяем победителя
DetermineWinner();
StateHasChanged(); // Показать финальный результат
StateHasChanged();
}
// --- Подсчет очков для руки ---
@ -197,10 +239,10 @@ else
}
}
// Корректировка значения Тузов (с 11 на 1 при переборе)
// Корректировка значения Тузов
while (score > 21 && aceCount > 0)
{
score -= 10; // Считаем Туз как 1 вместо 11
score -= 10;
aceCount--;
}
return score;
@ -209,10 +251,8 @@ else
// --- Определение победителя ---
private void DetermineWinner()
{
// Убедимся, что карта дилера видна (IsPlayerTurn уже false)
if (PlayerScore > 21)
{
// Это условие уже должно было быть обработано в PlayerHit, но на всякий случай
EndGame("Перебор! Вы проиграли.");
}
else if (DealerScore > 21)
@ -236,16 +276,15 @@ else
// --- Метод для завершения игры и установки сообщения ---
private void RevealDealerCardAndEndGame(string message)
{
IsPlayerTurn = false; // Убедимся, что карта дилера видна
IsPlayerTurn = false;
EndGame(message);
}
private void EndGame(string message)
{
IsGameOver = true;
IsPlayerTurn = false; // Больше никто не ходит
IsPlayerTurn = false;
GameMessage = message;
// StateHasChanged(); // Обычно вызывается в методе, который вызвал EndGame
}
// --- Вспомогательный метод для определения цвета карты ---
@ -254,11 +293,9 @@ else
return (card.Suit == "♥" || card.Suit == "♦") ? "card-red" : "card-black";
}
// --- Реализация IDisposable (пока пустая, но может пригодиться) ---
// --- Реализация IDisposable ---
public void Dispose()
{
// Здесь можно остановить таймеры или отписаться от событий, если они будут добавлены
// Реализация IDisposable
}
}
@* --- КОНЕЦ КОДА ДЛЯ BlackjackGame.razor --- *@

View File

@ -0,0 +1,592 @@
:root {
/* Обновленная цветовая палитра - более яркие и контрастные цвета */
--casino-green: #2a9d8f;
--casino-dark-green: #226f66;
--felt-green: #40a886;
--wood-brown: #b76e40;
--gold: #ffbe0b;
--light-gold: #ffe066;
--dark-gold: #e29400;
--red: #ff5e5b;
--black: #2b2b2b;
--white: #ffffff;
--gray: #7d8597;
--light-gray: #f1f3f5;
--blue: #3a86ff;
/* Улучшенные тени и эффекты - без затемнений */
--card-shadow: 0 6px 12px rgba(0, 0, 0, 0.2);
--button-shadow: 0 6px 0 var(--dark-gold);
--transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
--text-shadow: none;
}
/* Основной фон казино - убрано затемнение */
.casino-background {
background-image: url('_content/БлэкДжек/images/casino-bg.jpg');
background-size: cover;
background-position: center;
min-height: 100vh;
padding: 2rem;
box-sizing: border-box;
display: flex;
justify-content: center;
align-items: center;
font-family: 'Poppins', 'Montserrat', sans-serif;
color: var(--white);
}
/* Основной контейнер игры */
.blackjack-container {
width: 100%;
max-width: 1000px;
background: linear-gradient(145deg, var(--casino-green), var(--casino-dark-green));
border-radius: 24px;
box-shadow: 0 15px 30px rgba(0, 0, 0, 0.3);
padding: 2.5rem;
position: relative;
overflow: hidden;
border: 8px solid var(--wood-brown);
}
/* Декоративный элемент с фишками */
.chip-stack {
background-image: url('_content/БлэкДжек/images/chips-stack.png');
background-size: contain;
background-repeat: no-repeat;
width: 90px;
height: 90px;
position: absolute;
right: 2rem;
top: 2rem;
filter: drop-shadow(0 5px 10px rgba(0, 0, 0, 0.2));
}
/* Заголовок игры */
.game-header {
display: flex;
justify-content: center;
align-items: center;
position: relative;
margin-bottom: 2.5rem;
}
.game-title {
color: var(--light-gold);
text-transform: uppercase;
text-align: center;
font-size: 3.2rem;
margin: 0;
letter-spacing: 4px;
font-weight: 800;
position: relative;
}
.game-title::after {
content: '';
position: absolute;
bottom: -12px;
left: 50%;
transform: translateX(-50%);
width: 70%;
height: 4px;
background: linear-gradient(90deg, transparent, var(--gold), transparent);
}
/* Область сообщений - улучшена читаемость */
.message-box {
background-color: #ffffff;
border-left: 6px solid var(--gold);
padding: 1.2rem;
margin: 1.2rem auto 2.2rem;
color: var(--black);
border-radius: 10px;
box-shadow: 0 8px 15px rgba(0, 0, 0, 0.2);
text-align: center;
max-width: 85%;
transition: var(--transition);
}
.message-win {
border-left: 6px solid #4caf50;
background-color: #e8f5e9;
color: #2e7d32;
}
.message-lose {
border-left: 6px solid var(--red);
background-color: #ffebee;
color: #c62828;
}
.message-info {
border-left: 6px solid var(--blue);
background-color: #e1f5fe;
color: #0277bd;
}
.message-content {
font-size: 1.3rem;
font-weight: 600;
letter-spacing: 0.5px;
}
/* Область игры */
.game-area {
background-color: var(--felt-green);
border-radius: 20px;
padding: 2.2rem;
box-shadow: inset 0 0 20px rgba(0, 0, 0, 0.3);
position: relative;
border: 4px solid var(--casino-dark-green);
}
/* Стили секций игроков */
.player-section {
margin-bottom: 2.4rem;
position: relative;
}
.player-info {
display: flex;
align-items: center;
gap: 1.2rem;
margin-bottom: 1.2rem;
}
.player-avatar {
width: 55px;
height: 55px;
border-radius: 50%;
background-position: center;
background-size: cover;
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
border: 3px solid var(--gold);
}
.dealer-avatar {
background-image: url('_content/БлэкДжек/images/dealer-avatar.png');
}
.player-avatar-user {
background-image: url('_content/БлэкДжек/images/player-avatar.png');
}
.player-title {
color: var(--white);
font-size: 1.7rem;
margin: 0;
font-weight: 600;
letter-spacing: 0.5px;
}
.score-badge {
background: #ffffff;
padding: 0.3rem 0.8rem;
border-radius: 50px;
font-size: 1.2rem;
margin-left: 0.8rem;
color: var(--black);
border: 2px solid var(--dark-gold);
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.2);
}
.table-felt {
background-color: var(--felt-green);
padding: 1.2rem;
border-radius: 16px;
box-shadow: inset 0 0 15px rgba(0, 0, 0, 0.3);
}
/* Карты в руке */
.hand {
display: flex;
flex-wrap: wrap;
min-height: 170px;
gap: 0.8rem;
padding: 0.8rem;
position: relative;
justify-content: center;
}
.dealer-hand::before {
content: '';
position: absolute;
top: -12px;
left: 50%;
transform: translateX(-50%);
width: 75%;
height: 3px;
background: linear-gradient(90deg, transparent, var(--gold), transparent);
}
/* Разделитель между дилером и игроком */
.table-divider {
display: flex;
justify-content: center;
gap: 15px;
margin: 2rem 0;
position: relative;
}
.table-divider::before {
content: '';
position: absolute;
top: 50%;
left: 5%;
right: 5%;
height: 2px;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.7), transparent);
}
/* Игральные фишки */
.chip {
width: 40px;
height: 40px;
border-radius: 50%;
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
position: relative;
z-index: 1;
transition: transform 0.3s ease;
}
.chip:hover {
transform: translateY(-3px);
}
.red-chip {
background: radial-gradient(circle at 30% 30%, #ff8a8a 0%, var(--red) 60%);
border: 3px dashed #ffc7c7;
}
.blue-chip {
background: radial-gradient(circle at 30% 30%, #94d0ff 0%, var(--blue) 60%);
border: 3px dashed #c5e5ff;
}
.green-chip {
background: radial-gradient(circle at 30% 30%, #89e89c 0%, #3cac4c 60%);
border: 3px dashed #d2f9db;
}
.black-chip {
background: radial-gradient(circle at 30% 30%, #a6aaaf 0%, var(--black) 60%);
border: 3px dashed #e2e6eb;
}
/* Стили для контейнера карты */
.card-container {
position: relative;
transform-style: preserve-3d;
transition: transform 0.8s cubic-bezier(0.175, 0.885, 0.32, 1.275);
margin: 0.3rem;
}
.card-container.card-hidden {
transform: rotateY(0deg);
}
/* Игральная карта */
.playing-card {
width: 110px;
height: 154px;
background-color: var(--white);
border-radius: 10px;
box-shadow: var(--card-shadow);
position: relative;
display: flex;
justify-content: center;
align-items: center;
transform-style: preserve-3d;
backface-visibility: hidden;
border: 1px solid rgba(0, 0, 0, 0.15);
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.card-container:hover .playing-card {
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.3);
transform: translateY(-5px);
}
.card-back {
background-color: #3f51b5;
transform: rotateY(0deg);
position: relative;
background: linear-gradient(145deg, #5c6bc0, #3949ab);
}
.card-back-design {
width: 85%;
height: 85%;
background-image: url('_content/БлэкДжек/images/card-back.png');
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
.card-red {
color: #e53935;
}
.card-black {
color: #212121;
}
/* Уголки карты */
.card-corner {
position: absolute;
display: flex;
flex-direction: column;
align-items: center;
line-height: 1;
}
.top-left {
top: 8px;
left: 8px;
}
.bottom-right {
bottom: 8px;
right: 8px;
transform: rotate(180deg);
}
.card-rank {
font-size: 1.5rem;
font-weight: bold;
}
.card-suit {
font-size: 1.3rem;
}
.card-center-suit {
font-size: 3.2rem;
filter: drop-shadow(0 2px 3px rgba(0, 0, 0, 0.1));
}
/* Кнопки действий */
.action-buttons {
display: flex;
justify-content: center;
gap: 2rem;
margin-top: 2.5rem;
flex-wrap: wrap;
}
/* 3D кнопки */
.btn-3d {
position: relative;
padding: 14px 28px;
background: linear-gradient(to bottom, var(--gold), var(--dark-gold));
color: var(--black);
border: none;
border-radius: 50px;
font-size: 1.1rem;
font-weight: 700;
text-transform: uppercase;
box-shadow: var(--button-shadow), 0 10px 20px rgba(0, 0, 0, 0.2);
transition: all 0.25s;
cursor: pointer;
outline: none;
min-width: 160px;
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
letter-spacing: 1px;
}
.btn-3d:hover {
background: linear-gradient(to bottom, var(--light-gold), var(--gold));
transform: translateY(-3px);
box-shadow: 0 9px 0 var(--dark-gold), 0 14px 25px rgba(0, 0, 0, 0.2);
}
.btn-3d:active {
transform: translateY(3px);
box-shadow: 0 3px 0 var(--dark-gold), 0 5px 10px rgba(0, 0, 0, 0.1);
}
.btn-3d:disabled {
background: linear-gradient(to bottom, var(--gray), #939cad);
color: var(--light-gray);
cursor: not-allowed;
box-shadow: 0 6px 0 #636b7c, 0 10px 20px rgba(0, 0, 0, 0.1);
opacity: 0.8;
}
.btn-hit {
background: linear-gradient(to bottom, #76cf79, #48a04c);
box-shadow: 0 6px 0 #3e8942, 0 10px 20px rgba(0, 0, 0, 0.2);
color: white;
}
.btn-hit:hover {
background: linear-gradient(to bottom, #91d994, #53b557);
box-shadow: 0 9px 0 #3e8942, 0 14px 25px rgba(0, 0, 0, 0.2);
}
.btn-hit:active {
box-shadow: 0 3px 0 #3e8942, 0 5px 10px rgba(0, 0, 0, 0.1);
}
.btn-stand {
background: linear-gradient(to bottom, #ff7875, #d63d38);
box-shadow: 0 6px 0 #c62828, 0 10px 20px rgba(0, 0, 0, 0.2);
color: white;
}
.btn-stand:hover {
background: linear-gradient(to bottom, #ff9793, #e34744);
box-shadow: 0 9px 0 #c62828, 0 14px 25px rgba(0, 0, 0, 0.2);
}
.btn-stand:active {
box-shadow: 0 3px 0 #c62828, 0 5px 10px rgba(0, 0, 0, 0.1);
}
.btn-new-game {
background: linear-gradient(to bottom, #7986cb, #495cc9);
color: var(--white);
box-shadow: 0 6px 0 #3949ab, 0 10px 20px rgba(0, 0, 0, 0.2);
}
.btn-new-game:hover {
background: linear-gradient(to bottom, #9aa5d6, #5969d2);
box-shadow: 0 9px 0 #3949ab, 0 14px 25px rgba(0, 0, 0, 0.2);
}
.btn-new-game:active {
box-shadow: 0 3px 0 #3949ab, 0 5px 10px rgba(0, 0, 0, 0.1);
}
.btn-icon {
display: inline-block;
width: 24px;
height: 24px;
background-size: contain;
background-repeat: no-repeat;
filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.2));
}
.hit-icon {
background-image: url('_content/БлэкДжек/images/hit-icon.svg');
}
.stand-icon {
background-image: url('_content/БлэкДжек/images/stand-icon.svg');
}
.new-game-icon {
background-image: url('_content/БлэкДжек/images/new-game-icon.svg');
}
/* Начальный экран */
.start-screen {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 4rem 1rem;
}
.game-logo {
width: 350px;
height: 230px;
background-image: url('_content/БлэкДжек/images/blackjack-logo.png');
background-size: contain;
background-position: center;
background-repeat: no-repeat;
margin-bottom: 4rem;
filter: drop-shadow(0 10px 20px rgba(0, 0, 0, 0.3));
transition: transform 0.5s ease;
}
.game-logo:hover {
transform: scale(1.05);
}
.btn-start {
font-size: 1.5rem;
padding: 18px 45px;
min-width: 220px;
box-shadow: 0 8px 0 var(--dark-gold), 0 15px 25px rgba(0, 0, 0, 0.2);
}
.btn-start:hover {
transform: translateY(-4px);
box-shadow: 0 12px 0 var(--dark-gold), 0 20px 35px rgba(0, 0, 0, 0.2);
}
/* Адаптивные стили */
@media (max-width: 768px) {
.blackjack-container {
padding: 1.5rem;
}
.game-title {
font-size: 2.2rem;
}
.playing-card {
width: 90px;
height: 126px;
}
.card-rank {
font-size: 1.3rem;
}
.card-center-suit {
font-size: 2.6rem;
}
.action-buttons {
gap: 1.2rem;
}
.btn-3d {
padding: 12px 20px;
min-width: 140px;
font-size: 1rem;
}
}
@media (max-width: 480px) {
.playing-card {
width: 70px;
height: 98px;
}
.card-rank {
font-size: 1.1rem;
}
.card-suit {
font-size: 1rem;
}
.card-center-suit {
font-size: 2.2rem;
}
.action-buttons {
flex-direction: column;
align-items: center;
gap: 1rem;
}
.btn-3d {
width: 100%;
max-width: 220px;
}
.game-area {
padding: 1.5rem;
}
.message-content {
font-size: 1.1rem;
}
}

View File

@ -1,169 +0,0 @@
/* 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; /* Увеличим отступы */
}

View File

@ -1,6 +1,19 @@
<Router AppAssembly="typeof(Program).Assembly">
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.AspNetCore.Components.Routing
<Router AppAssembly="@typeof(Program).Assembly">
<Found Context="routeData">
<RouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)" />
<FocusOnNavigate RouteData="routeData" Selector="h1" />
@if (routeData.PageType.Name == "Index" || string.IsNullOrEmpty(routeData.PageType.Name))
{
<Navigate href="/blackjack" />
}
else
{
<RouteView RouteData="@routeData" />
}
</Found>
<NotFound>
<PageTitle>Не найдено</PageTitle>
<p role="alert">Извините, страница не найдена.</p>
</NotFound>
</Router>

View File

@ -2,11 +2,14 @@ using БлэкДжек.Components;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHttpContextAccessor();
// Add services to the container.
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents();
var app = builder.Build();
app.UseStaticFiles();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())

View File

@ -1,3 +1,6 @@
/* Äîáàâüòå â wwwroot/css/app.css èëè äðóãîé ãëîáàëüíûé ôàéë CSS */
@import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@400;500;600;700&display=swap');
h1:focus {
outline: none;
}

View File

@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.13.35828.75 d17.13
VisualStudioVersion = 17.13.35828.75
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "БлэкДжек", "БлэкДжек.csproj", "{11339889-A9DE-4748-A250-599F6D56EDA1}"
EndProject