feat: add player balance display and congratulatory system for strong hands
- Implemented a player info row displaying the player's name and balance in the game interface. - Added animations for balance changes (increase/decrease) to enhance user experience. - Introduced a congratulatory system where bots congratulate players for strong hands based on hand strength probabilities. - Updated styles for player balance display and added relevant CSS animations. - Enhanced game logic to trigger congratulatory messages from bots after a hand concludes. - Documented the new congratulatory system and player balance display features.
This commit is contained in:
parent
eec780d8af
commit
24e457885d
|
|
@ -0,0 +1,197 @@
|
|||
# Система поздравлений игрока от ботов
|
||||
|
||||
## 🎯 Описание
|
||||
|
||||
Боты теперь **поздравляют игрока**, когда он выигрывает с сильной покерной рукой! Вероятность поздравления зависит от редкости и силы руки.
|
||||
|
||||
---
|
||||
|
||||
## 📊 Вероятности поздравлений по силе руки
|
||||
|
||||
| Рука | Ранг | Вероятность | Пример |
|
||||
|------|------|-------------|--------|
|
||||
| **Роял-флеш** | 10 | 90% | "A♠ K♠ Q♠ J♠ 10♠ - Легенда!" |
|
||||
| **Стрит-флеш** | 9 | 80% | "9♥ 8♥ 7♥ 6♥ 5♥ - Невероятно!" |
|
||||
| **Каре** | 8 | 60% | "K♠ K♥ K♦ K♣ - Впечатляет!" |
|
||||
| **Фулл-хаус** | 7 | 40% | "Q♠ Q♥ Q♦ 5♠ 5♥ - Красиво!" |
|
||||
| **Флеш** | 6 | 25% | "A♠ J♠ 9♠ 7♠ 3♠ - Неплохо!" |
|
||||
| **Стрит** | 5 | 15% | "10♦ 9♠ 8♥ 7♣ 6♦ - Хорошо!" |
|
||||
| Сет и ниже | ≤4 | 0% | Не поздравляют |
|
||||
|
||||
---
|
||||
|
||||
## 🤖 Стили поздравлений от разных ботов
|
||||
|
||||
### 1. **Виктор "Акула"** (агрессивный)
|
||||
- **Обычная рука:** "Неплохо сыграно!", "Уважаю!"
|
||||
- **Сильная:** "Вау, фулл-хаус! 💪", "Монстр-рука!"
|
||||
- **Очень сильная:** "Роял-флеш?! Респект! 🔥", "НЕВЕРОЯТНО!"
|
||||
|
||||
### 2. **Анна "Блефер"** (загадочная)
|
||||
- **Обычная:** "Интересно сыграно... 😏"
|
||||
- **Сильная:** "Фулл-хаус? Впечатляет! 👏"
|
||||
- **Очень сильная:** "Стрит-флеш... Я в восторге! 😮"
|
||||
|
||||
### 3. **Дед Михалыч** (старая школа)
|
||||
- **Обычная:** "Молодец, голубчик!"
|
||||
- **Сильная:** "Фулл-хаус! Красота! 👴"
|
||||
- **Очень сильная:** "Роял-флеш! За 50 лет такое редко видел! 😲"
|
||||
|
||||
### 4. **Макс "ГТО"** (математик)
|
||||
- **Обычная:** "Хороший EV!"
|
||||
- **Сильная:** "Фулл-хаус! Rare! 📊", "Топ 0.1% рук!"
|
||||
- **Очень сильная:** "Роял-флеш... 0.00154%! 🤯"
|
||||
|
||||
### 5. **Катя "Удача"** (суеверная)
|
||||
- **Обычная:** "Ура! Молодец! 😊"
|
||||
- **Сильная:** "Фулл-хаус! Удача! 🍀✨"
|
||||
- **Очень сильная:** "Роял-флеш! ЭТО МАГИЯ! 🎉🍀"
|
||||
|
||||
### 6. **Борис "Молчун"** (молчаливый)
|
||||
- **Обычная:** "Хм. Неплохо."
|
||||
- **Сильная:** "Фулл-хаус... Ого."
|
||||
- **Очень сильная:** "Роял-флеш?! ..."
|
||||
|
||||
### 7. **Олег "Тильтер"** (эмоциональный)
|
||||
- **Обычная:** "Ну везёт же... 😒"
|
||||
- **Сильная:** "Фулл-хаус?! Ладно, красиво 😤"
|
||||
- **Очень сильная:** "Роял-флеш... Я не верю! 😱"
|
||||
|
||||
### 8. **Ирина "Профи"** (профессионал)
|
||||
- **Обычная:** "GG WP!"
|
||||
- **Сильная:** "Фулл-хаус. Респект! 💎"
|
||||
- **Очень сильная:** "Роял-флеш! Incredible! 🎯"
|
||||
|
||||
---
|
||||
|
||||
## 🎬 Примеры игровых ситуаций
|
||||
|
||||
### Ситуация 1: Флеш (25% вероятность)
|
||||
```
|
||||
Игрок выигрывает с: A♠ K♠ 9♠ 7♠ 3♠ (Флеш)
|
||||
Банк: 250 фишек
|
||||
|
||||
🎲 Случайный бот (25% шанс):
|
||||
Макс "ГТО": "Флеш! Статистически сильно! 📊"
|
||||
```
|
||||
|
||||
### Ситуация 2: Фулл-хаус (40% вероятность)
|
||||
```
|
||||
Игрок выигрывает с: K♠ K♥ K♦ 8♠ 8♥ (Фулл-хаус)
|
||||
Банк: 500 фишек
|
||||
|
||||
🎲 Случайный бот (40% шанс):
|
||||
Дед Михалыч: "Фулл-хаус! Красота! Как в старые времена! 👴"
|
||||
```
|
||||
|
||||
### Ситуация 3: Каре (60% вероятность)
|
||||
```
|
||||
Игрок выигрывает с: A♠ A♥ A♦ A♣ Q♠ (Каре тузов)
|
||||
Банк: 800 фишек
|
||||
|
||||
🎲 Случайный бот (60% шанс):
|
||||
Виктор "Акула": "Каре тузов! МОНСТР-РУКА! 💪🔥"
|
||||
```
|
||||
|
||||
### Ситуация 4: Стрит-флеш (80% вероятность)
|
||||
```
|
||||
Игрок выигрывает с: 9♥ 8♥ 7♥ 6♥ 5♥ (Стрит-флеш)
|
||||
Банк: 1200 фишек
|
||||
|
||||
🎲 Случайный бот (80% шанс):
|
||||
Анна "Блефер": "Стрит-флеш до девятки... Я в восторге! 😮✨"
|
||||
```
|
||||
|
||||
### Ситуация 5: Роял-флеш (90% вероятность)
|
||||
```
|
||||
Игрок выигрывает с: A♠ K♠ Q♠ J♠ 10♠ (Роял-флеш!)
|
||||
Банк: 2000 фишек
|
||||
|
||||
🎲 Случайный бот (90% шанс):
|
||||
Макс "ГТО": "РОЯЛ-ФЛЕШ! Математическое чудо! Вероятность 0.00154%! 🤯🎯"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚙️ Технические детали
|
||||
|
||||
### Алгоритм работы:
|
||||
|
||||
1. **Проверка победителя**
|
||||
- Игрок победил? (не бот)
|
||||
- Есть информация о руке?
|
||||
|
||||
2. **Определение вероятности**
|
||||
- Ранг руки ≥ 5 (стрит или выше)
|
||||
- Вероятность от 15% до 90%
|
||||
|
||||
3. **Выбор бота**
|
||||
- Случайный бот из активных (не спасовавших)
|
||||
- Учитывается его личность
|
||||
|
||||
4. **Генерация поздравления**
|
||||
- С помощью LLM (если включён)
|
||||
- Или запасные фразы по стилю бота
|
||||
|
||||
5. **Отправка в чат**
|
||||
- Задержка 1-2.5 секунды
|
||||
- Не конфликтует с эмоциональными реакциями
|
||||
|
||||
### Интеграция с LLM:
|
||||
|
||||
При включенном LLM бот получает контекст:
|
||||
```
|
||||
Игрок [Имя] только что ВЫИГРАЛ с рукой "Фулл-хаус"!
|
||||
Банк: 500 фишек
|
||||
Это сильная рука!
|
||||
|
||||
Поздравь игрока КРАТКО (максимум 5-7 слов), в своём стиле.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎮 Примеры с LLM (если включён)
|
||||
|
||||
**Виктор "Акула":**
|
||||
- Флеш: "С флешем на терне? Красиво прочитал борд! 🔥"
|
||||
- Каре: "Каре на ривере?! Вот это хладнокровие! 💪"
|
||||
|
||||
**Дед Михалыч:**
|
||||
- Фулл-хаус: "Эх, молодость! С таким фулл-хаусом я бы в 82-м весь турнир взял!"
|
||||
- Стрит-флеш: "Святые угодники! Такое в жизни раз видел!"
|
||||
|
||||
**Макс "ГТО":**
|
||||
- Флеш: "Флеш на этой текстуре борда - +EV колл!"
|
||||
- Каре: "Четыре аута превратились в каре. Probability magic! 📊"
|
||||
|
||||
---
|
||||
|
||||
## 📝 Файлы изменены
|
||||
|
||||
1. **`ai.js`**
|
||||
- Добавлена `generatePlayerCongratulation()` - генерация поздравления через LLM
|
||||
- Добавлена `getFallbackCongratulation()` - запасные фразы для каждого стиля
|
||||
|
||||
2. **`main.js`**
|
||||
- Добавлена `generatePlayerCongratulations()` - логика выбора бота и отправки
|
||||
- Вызывается в `onHandEnd()` после завершения раздачи
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Результат
|
||||
|
||||
Теперь игра стала **более живой и реалистичной**:
|
||||
- Боты замечают и ценят сильные руки игрока
|
||||
- Редкие комбинации вызывают восхищение
|
||||
- Каждый бот поздравляет в своём уникальном стиле
|
||||
- LLM делает поздравления ещё более контекстуальными
|
||||
|
||||
**Пример полного взаимодействия:**
|
||||
|
||||
```
|
||||
[Игрок собрал каре королей и выиграл банк 800 фишек]
|
||||
|
||||
Макс "ГТО": "Каре королей! Топ 0.024% рук! 📊" (через 1.2 сек)
|
||||
```
|
||||
|
||||
🎰🃏 **Играйте и получайте заслуженные поздравления!**
|
||||
|
|
@ -0,0 +1,128 @@
|
|||
# Пример контекста для LLM
|
||||
|
||||
## Что теперь видит бот при ответе на сообщение игрока
|
||||
|
||||
### Пример 1: Флоп в игре
|
||||
|
||||
**Игрок пишет:** "Виктор, что думаешь?"
|
||||
|
||||
**LLM получает следующий контекст:**
|
||||
|
||||
```
|
||||
📍 Фаза: флоп (3 карты на столе)
|
||||
🃏 Карты на столе: K♥ 9♦ 2♣
|
||||
💰 Банк: 150 фишек
|
||||
💵 Текущая ставка: 30 фишек
|
||||
|
||||
🤖 Мои данные:
|
||||
- Имя: Виктор "Акула"
|
||||
- Карты: A♠ K♠
|
||||
- Фишки: 470
|
||||
- Моя ставка: 30 (всего в раздаче: 45)
|
||||
- Позиция: дилер
|
||||
|
||||
👥 Другие игроки:
|
||||
- Игрок: 480 фишек, ставка 30 🎲 [последнее: call]
|
||||
- Анна "Блефер": 350 фишек [спасовал ❌]
|
||||
- Дед Михалыч: 200 фишек [последнее: fold]
|
||||
|
||||
✅ Активные: Виктор "Акула", Игрок
|
||||
❌ Спасовали: Анна "Блефер", Дед Михалыч
|
||||
|
||||
💬 Игрок написал: "Виктор, что думаешь?"
|
||||
```
|
||||
|
||||
**Возможный ответ бота:**
|
||||
"С топ-парой на таком сухом флопе? Думаю, что банк мой. 😎"
|
||||
|
||||
---
|
||||
|
||||
### Пример 2: Терн, напряжённая ситуация
|
||||
|
||||
**Игрок пишет:** "Макс, всё плохо?"
|
||||
|
||||
**LLM получает:**
|
||||
|
||||
```
|
||||
📍 Фаза: терн (4 карты на столе)
|
||||
🃏 Карты на столе: 7♥ 8♦ 9♣ J♠
|
||||
💰 Банк: 450 фишек
|
||||
💵 Текущая ставка: 100 фишек
|
||||
|
||||
🤖 Мои данные:
|
||||
- Имя: Макс "ГТО"
|
||||
- Карты: A♣ K♣
|
||||
- Фишки: 280
|
||||
- Моя ставка: 50 (всего в раздаче: 165)
|
||||
- Позиция: обычная позиция
|
||||
|
||||
👥 Другие игроки:
|
||||
- Игрок: 520 фишек, ставка 100 [последнее: raise]
|
||||
- Борис "Молчун": 150 фишек [спасовал ❌]
|
||||
|
||||
✅ Активные: Макс "ГТО", Игрок
|
||||
❌ Спасовали: Борис "Молчун"
|
||||
|
||||
💬 Игрок написал: "Макс, всё плохо?"
|
||||
```
|
||||
|
||||
**Возможный ответ бота:**
|
||||
"На этом борде с прямым дро? Просчитываю pot odds... 🤔"
|
||||
|
||||
---
|
||||
|
||||
### Пример 3: All-in ситуация
|
||||
|
||||
**Игрок пишет:** "Катя, рискнёшь?"
|
||||
|
||||
**LLM получает:**
|
||||
|
||||
```
|
||||
📍 Фаза: ривер (5 карт на столе)
|
||||
🃏 Карты на столе: 3♥ 3♦ 8♣ Q♠ A♥
|
||||
💰 Банк: 800 фишек
|
||||
💵 Текущая ставка: 200 фишек
|
||||
|
||||
🤖 Мои данные:
|
||||
- Имя: Катя "Удача"
|
||||
- Карты: 3♠ 8♠
|
||||
- Фишки: 195
|
||||
- Моя ставка: 0 (всего в раздаче: 105)
|
||||
- Позиция: малый блайнд
|
||||
|
||||
👥 Другие игроки:
|
||||
- Игрок: 95 фишек, ставка 200 [ва-банк 💥]
|
||||
|
||||
✅ Активные: Катя "Удача", Игрок
|
||||
❌ Спасовали:
|
||||
|
||||
💬 Игрок написал: "Катя, рискнёшь?"
|
||||
```
|
||||
|
||||
**Возможный ответ бота:**
|
||||
"С фулл-хаусом? Загадала желание! 🍀✨"
|
||||
|
||||
---
|
||||
|
||||
## Преимущества нового контекста
|
||||
|
||||
1. **Видит свои карты** - может блефовать или говорить честно
|
||||
2. **Видит карты на столе** - может комментировать текстуру борда
|
||||
3. **Видит действия других игроков** - может реагировать на чужие ставки
|
||||
4. **Знает размер банка и ставок** - может говорить про pot odds
|
||||
5. **Знает фазу игры** - может адаптировать ответ под префлоп/флоп/терн/ривер
|
||||
6. **Видит кто спасовал** - может подшучивать или сочувствовать
|
||||
|
||||
## Примеры улучшенных ответов
|
||||
|
||||
### До изменений:
|
||||
- Игрок: "Что думаешь?"
|
||||
- Бот: "Удачи! Интересная игра."
|
||||
|
||||
### После изменений:
|
||||
- Игрок: "Что думаешь?"
|
||||
- Бот: "С тремя червами на столе и у меня флеш дро? Думаю поплыть дальше. 😏"
|
||||
|
||||
---
|
||||
|
||||
Теперь боты отвечают КОНТЕКСТУАЛЬНО, используя реальную информацию о раздаче!
|
||||
|
|
@ -0,0 +1,155 @@
|
|||
# Отображение баланса игрока
|
||||
|
||||
## ✅ Исправлено
|
||||
|
||||
Теперь игрок **видит свой баланс фишек** прямо под картами!
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Что добавлено
|
||||
|
||||
### 1. **Визуальное отображение баланса**
|
||||
|
||||
На экране игры появилась **панель информации о игроке**:
|
||||
|
||||
```
|
||||
┌────────────────────────────┐
|
||||
│ Игрок 💰 1000 │ ← Имя и баланс
|
||||
│ [K♠] [Q♥] │ ← Карты
|
||||
│ Две старших карты │ ← Сила руки
|
||||
└────────────────────────────┘
|
||||
```
|
||||
|
||||
**Элементы:**
|
||||
- **Имя игрока** - слева
|
||||
- **💰 Баланс** - справа (зелёная подсветка)
|
||||
- **Карты** - по центру
|
||||
- **Сила руки** - снизу
|
||||
|
||||
---
|
||||
|
||||
## 🎨 Визуальные фичи
|
||||
|
||||
### **Анимация изменения баланса:**
|
||||
|
||||
1. **При увеличении фишек** (выигрыш):
|
||||
- Зелёная вспышка
|
||||
- Увеличение на 5%
|
||||
- 0.5 секунды
|
||||
|
||||
2. **При уменьшении фишек** (ставка/проигрыш):
|
||||
- Красная вспышка
|
||||
- Уменьшение на 5%
|
||||
- 0.5 секунды
|
||||
|
||||
**Пример:**
|
||||
```
|
||||
Выигрыш +500: 💰 500 → 💰 1000 (зелёная вспышка ✨)
|
||||
Ставка -100: 💰 1000 → 💰 900 (красная вспышка 🔴)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💻 Технические детали
|
||||
|
||||
### **Файлы изменены:**
|
||||
|
||||
#### 1. `index.html`
|
||||
Добавлена строка информации о игроке:
|
||||
```html
|
||||
<div class="player-info-row">
|
||||
<div class="player-name-display" id="player-name-display">Игрок</div>
|
||||
<div class="player-balance-display">
|
||||
<span class="balance-icon">💰</span>
|
||||
<span class="balance-amount" id="player-balance">1000</span>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
#### 2. `styles.css`
|
||||
Добавлены стили:
|
||||
- `.player-info-row` - контейнер для имени и баланса
|
||||
- `.player-name-display` - стиль имени
|
||||
- `.player-balance-display` - значок и сумма
|
||||
- `@keyframes balanceIncrease` - анимация увеличения
|
||||
- `@keyframes balanceDecrease` - анимация уменьшения
|
||||
|
||||
#### 3. `main.js`
|
||||
Добавлена функция `updatePlayerBalance()`:
|
||||
```javascript
|
||||
function updatePlayerBalance(player) {
|
||||
// Обновляет имя и баланс
|
||||
// Добавляет анимацию при изменении
|
||||
// Отслеживает предыдущее значение
|
||||
}
|
||||
```
|
||||
|
||||
Интеграция в:
|
||||
- `updateGameUI()` - одиночная игра
|
||||
- `updateGameUIFromServer()` - мультиплеер
|
||||
|
||||
---
|
||||
|
||||
## 🎮 Примеры в действии
|
||||
|
||||
### **Префлоп:**
|
||||
```
|
||||
┌────────────────────────────┐
|
||||
│ Игрок 💰 1000 │
|
||||
│ [A♠] [K♠] │
|
||||
│ Старшие карты │
|
||||
└────────────────────────────┘
|
||||
|
||||
Ставка Big Blind 10 →
|
||||
|
||||
┌────────────────────────────┐
|
||||
│ Игрок 💰 990 🔴 │ ← Красная вспышка
|
||||
│ [A♠] [K♠] │
|
||||
│ Старшие карты │
|
||||
└────────────────────────────┘
|
||||
```
|
||||
|
||||
### **Выигрыш банка:**
|
||||
```
|
||||
Банк: 500 фишек
|
||||
Победа с флешем!
|
||||
|
||||
┌────────────────────────────┐
|
||||
│ Игрок 💰 1490 ✨ │ ← Зелёная вспышка
|
||||
│ [A♠] [K♠] │
|
||||
│ Флеш, туз старший │
|
||||
└────────────────────────────┘
|
||||
```
|
||||
|
||||
### **All-in:**
|
||||
```
|
||||
┌────────────────────────────┐
|
||||
│ Игрок 💰 0 🔴 │ ← Баланс 0 после all-in
|
||||
│ [Q♥] [Q♦] │
|
||||
│ Пара дам │
|
||||
└────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 Отличия от ботов
|
||||
|
||||
| Элемент | Бот (на столе) | Игрок (снизу) |
|
||||
|---------|----------------|---------------|
|
||||
| Имя | Маленькое, внутри бокса | Крупное, отдельная строка |
|
||||
| Баланс | Под именем, серый текст | Отдельный блок, зелёный, с иконкой |
|
||||
| Карты | Мини (40x56px) | Большие (70x98px) |
|
||||
| Анимация | Нет | Есть (при изменении) |
|
||||
| Позиция | По кругу стола | Внизу по центру |
|
||||
|
||||
---
|
||||
|
||||
## ✅ Результат
|
||||
|
||||
Теперь игрок **всегда видит**:
|
||||
- ✅ Своё имя
|
||||
- ✅ Актуальный баланс фишек
|
||||
- ✅ Визуальную обратную связь при выигрыше/проигрыше
|
||||
- ✅ Красивую анимацию изменения баланса
|
||||
|
||||
🎰💰 **Следите за своим стеком!**
|
||||
32
database.js
32
database.js
|
|
@ -150,6 +150,38 @@ async function createDefaultAdmin() {
|
|||
saveDatabase();
|
||||
console.log('👑 Создан администратор по умолчанию: admin / admin123');
|
||||
}
|
||||
|
||||
// Инициализируем дефолтные админские настройки
|
||||
initDefaultAdminSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Инициализировать дефолтные админские настройки
|
||||
*/
|
||||
function initDefaultAdminSettings() {
|
||||
const settingsCheck = db.exec("SELECT COUNT(*) as count FROM admin_settings");
|
||||
const count = settingsCheck.length > 0 ? settingsCheck[0].values[0][0] : 0;
|
||||
|
||||
if (count === 0) {
|
||||
const defaults = {
|
||||
llmEnabled: 'false',
|
||||
llmProvider: 'ollama',
|
||||
llmApiUrl: 'http://localhost:11434',
|
||||
llmModel: 'llama3.2',
|
||||
llmApiKey: '',
|
||||
serverUrl: 'ws://localhost:3000'
|
||||
};
|
||||
|
||||
Object.entries(defaults).forEach(([key, value]) => {
|
||||
db.run(`
|
||||
INSERT INTO admin_settings (key, value, updated_at)
|
||||
VALUES (?, ?, datetime('now'))
|
||||
`, [key, value]);
|
||||
});
|
||||
|
||||
saveDatabase();
|
||||
console.log('⚙️ Инициализированы дефолтные админские настройки');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
237
public/ai.js
237
public/ai.js
|
|
@ -27,10 +27,12 @@ const pokerAI = {
|
|||
|
||||
Правила ответов:
|
||||
- ТОЛЬКО 1-2 коротких предложения, как будто говоришь вслух за столом
|
||||
- Реагируй на действия игрока и ситуацию в игре
|
||||
- Реагируй на действия игрока и ситуацию в игре (смотри контекст ниже!)
|
||||
- Комментируй КОНКРЕТНУЮ игровую ситуацию: свои карты, карты на столе, ставки соперников
|
||||
- Можешь подначивать, блефовать словами, пугать олл-ином
|
||||
- НЕ объясняй правила покера, НЕ давай советы как ассистент
|
||||
- Говори по-русски, неформально`
|
||||
- Говори по-русски, неформально
|
||||
- Используй информацию о раздаче для правдоподобных комментариев`
|
||||
},
|
||||
{
|
||||
name: 'Анна "Блефер"',
|
||||
|
|
@ -49,9 +51,11 @@ const pokerAI = {
|
|||
Правила ответов:
|
||||
- ТОЛЬКО 1-2 коротких предложения
|
||||
- Держи интригу, отвечай загадочно
|
||||
- Смотри на карты на столе, банк, ставки — намекай исходя из ситуации
|
||||
- Можешь намекать на силу/слабость руки (это тоже блеф)
|
||||
- Улыбайся мысленно, будь обаятельно-опасной
|
||||
- Говори по-русски`
|
||||
- Говори по-русски
|
||||
- Используй игровой контекст для загадочных намёков`
|
||||
},
|
||||
{
|
||||
name: 'Дед Михалыч',
|
||||
|
|
@ -63,15 +67,19 @@ const pokerAI = {
|
|||
|
||||
Твой характер:
|
||||
- Добродушный, мудрый, любишь поболтать
|
||||
- Вспоминаешь "а вот раньше в преферанс..."
|
||||
- Часто вспоминаешь старые времена, "а вот в СССР..."
|
||||
- Говоришь просто, с народными выражениями
|
||||
- Не спешишь, играешь обстоятельно
|
||||
- Немного путаешь термины, но играешь хитро
|
||||
- К молодёжи относишься снисходительно, но по-доброму
|
||||
|
||||
Правила ответов:
|
||||
- ТОЛЬКО 1-2 коротких предложения
|
||||
- Можешь вставить "эх, молодёжь" или байку из прошлого
|
||||
- Реагируй на игру неспешно, философски
|
||||
- Говори по-русски, по-простому`
|
||||
- 1-2 коротких предложения
|
||||
- Можешь вспомнить историю из прошлого или вставить "эх, молодёжь"
|
||||
- Реагируй на ситуацию за столом, смотри на карты и ставки
|
||||
- Комментируй ход игры по-стариковски мудро
|
||||
- Говори тепло и по-человечески
|
||||
- Используй контекст игры для житейских комментариев`
|
||||
},
|
||||
{
|
||||
name: 'Макс "ГТО"',
|
||||
|
|
@ -89,8 +97,9 @@ const pokerAI = {
|
|||
|
||||
Правила ответов:
|
||||
- ТОЛЬКО 1-2 коротких предложения
|
||||
- Можешь упомянуть шансы или +EV
|
||||
- Можешь упомянуть шансы или +EV, опираясь на карты на столе и банк
|
||||
- Реагируй на игру с точки зрения математики
|
||||
- Анализируй конкретную ситуацию (смотри контекст)
|
||||
- Говори по-русски, можно с англицизмами`
|
||||
},
|
||||
{
|
||||
|
|
@ -110,7 +119,8 @@ const pokerAI = {
|
|||
Правила ответов:
|
||||
- ТОЛЬКО 1-2 коротких предложения
|
||||
- Используй эмодзи уместно: 🍀✨😊🎲
|
||||
- Реагируй эмоционально на игру
|
||||
- Реагируй эмоционально на игру и карты на столе
|
||||
- Комментируй удачу/неудачу исходя из ситуации
|
||||
- Говори по-русски, живо`
|
||||
},
|
||||
{
|
||||
|
|
@ -125,10 +135,11 @@ const pokerAI = {
|
|||
- Говоришь ОЧЕНЬ мало
|
||||
- Загадочный, никто не знает что у тебя на уме
|
||||
- Отвечаешь односложно или просто молчишь
|
||||
- Можешь кивнуть на конкретную карту или ситуацию
|
||||
|
||||
Правила ответов:
|
||||
- МАКСИМУМ 1-3 слова или многоточие
|
||||
- Примеры: "Хм.", "Нет.", "Посмотрим.", "...", "Да."
|
||||
- Примеры: "Хм.", "Нет.", "Посмотрим.", "...", "Да.", "Флоп интересный."
|
||||
- НИКОГДА не говори длинно
|
||||
- Молчание — твоё оружие`
|
||||
},
|
||||
|
|
@ -148,8 +159,9 @@ const pokerAI = {
|
|||
|
||||
Правила ответов:
|
||||
- ТОЛЬКО 1-2 коротких предложения
|
||||
- Можешь ворчать, возмущаться, жаловаться
|
||||
- Реагируй эмоционально на плохие карты/биты
|
||||
- Можешь ворчать, возмущаться, жаловаться на конкретные карты
|
||||
- Реагируй эмоционально на плохие карты/биты, смотря на ситуацию
|
||||
- Комментируй несправедливость раздачи
|
||||
- Говори по-русски, экспрессивно`
|
||||
},
|
||||
{
|
||||
|
|
@ -169,7 +181,8 @@ const pokerAI = {
|
|||
Правила ответов:
|
||||
- ТОЛЬКО 1-2 коротких предложения
|
||||
- Говори спокойно, профессионально
|
||||
- Можешь прокомментировать интересный розыгрыш
|
||||
- Можешь прокомментировать интересный розыгрыш, ссылаясь на ситуацию
|
||||
- Анализируй конкретную раздачу если нужно
|
||||
- Говори по-русски, корректно`
|
||||
}
|
||||
],
|
||||
|
|
@ -920,31 +933,87 @@ ${gameContextStr}
|
|||
|
||||
let context = '';
|
||||
|
||||
if (ctx.phase) {
|
||||
const phases = {
|
||||
'preflop': 'Префлоп',
|
||||
'flop': 'Флоп',
|
||||
'turn': 'Тёрн',
|
||||
'river': 'Ривер',
|
||||
'showdown': 'Вскрытие'
|
||||
};
|
||||
context += `Фаза: ${phases[ctx.phase] || ctx.phase}\n`;
|
||||
}
|
||||
|
||||
if (ctx.pot !== undefined) {
|
||||
context += `Банк: ${ctx.pot} фишек\n`;
|
||||
}
|
||||
|
||||
if (ctx.myChips !== undefined) {
|
||||
context += `Мои фишки: ${ctx.myChips}\n`;
|
||||
}
|
||||
|
||||
if (ctx.lastAction) {
|
||||
context += `Последнее действие игрока: ${ctx.lastAction}\n`;
|
||||
// Фаза игры
|
||||
if (ctx.phaseRu) {
|
||||
context += `📍 Фаза: ${ctx.phaseRu}\n`;
|
||||
}
|
||||
|
||||
// Общие карты на столе
|
||||
if (ctx.communityCards && ctx.communityCards.length > 0) {
|
||||
context += `Общие карты: ${ctx.communityCards.join(', ')}\n`;
|
||||
context += `🃏 Карты на столе: ${ctx.communityCards.join(' ')}\n`;
|
||||
} else {
|
||||
context += `🃏 Карты на столе: пока нет\n`;
|
||||
}
|
||||
|
||||
// Банк и ставки
|
||||
if (ctx.pot !== undefined) {
|
||||
context += `💰 Банк: ${ctx.pot} фишек\n`;
|
||||
}
|
||||
if (ctx.currentBet !== undefined && ctx.currentBet > 0) {
|
||||
context += `💵 Текущая ставка: ${ctx.currentBet} фишек\n`;
|
||||
}
|
||||
|
||||
// Мои данные (данные бота)
|
||||
if (ctx.myName) {
|
||||
context += `\n🤖 Мои данные:\n`;
|
||||
context += ` - Имя: ${ctx.myName}\n`;
|
||||
if (ctx.myCards && ctx.myCards.length > 0) {
|
||||
context += ` - Карты: ${ctx.myCards.join(' ')}\n`;
|
||||
}
|
||||
context += ` - Фишки: ${ctx.myChips}\n`;
|
||||
if (ctx.myBet > 0) {
|
||||
context += ` - Моя ставка: ${ctx.myBet} (всего в раздаче: ${ctx.myTotalBet})\n`;
|
||||
}
|
||||
if (ctx.myPosition) {
|
||||
context += ` - Позиция: ${ctx.myPosition}\n`;
|
||||
}
|
||||
}
|
||||
|
||||
// Другие игроки
|
||||
if (ctx.players && ctx.players.length > 0) {
|
||||
context += `\n👥 Другие игроки:\n`;
|
||||
ctx.players.forEach(p => {
|
||||
if (p.isMe) return; // Пропускаем себя
|
||||
|
||||
let playerStatus = '';
|
||||
if (p.folded) {
|
||||
playerStatus = ' [спасовал ❌]';
|
||||
} else if (p.allIn) {
|
||||
playerStatus = ' [ва-банк 💥]';
|
||||
} else if (p.lastAction) {
|
||||
const actionRu = {
|
||||
'fold': 'пас',
|
||||
'check': 'чек',
|
||||
'call': 'колл',
|
||||
'bet': 'бет',
|
||||
'raise': 'рейз',
|
||||
'allin': 'ва-банк'
|
||||
}[p.lastAction] || p.lastAction;
|
||||
playerStatus = ` [последнее: ${actionRu}]`;
|
||||
}
|
||||
|
||||
context += ` - ${p.name}: ${p.chips} фишек`;
|
||||
if (p.bet > 0) {
|
||||
context += `, ставка ${p.bet}`;
|
||||
}
|
||||
if (p.isDealer) {
|
||||
context += ` 🎲`;
|
||||
}
|
||||
context += playerStatus + `\n`;
|
||||
});
|
||||
}
|
||||
|
||||
// Активные игроки
|
||||
if (ctx.activePlayers && ctx.activePlayers.length > 0) {
|
||||
context += `\n✅ Активные: ${ctx.activePlayers.join(', ')}\n`;
|
||||
}
|
||||
if (ctx.foldedPlayers && ctx.foldedPlayers.length > 0) {
|
||||
context += `❌ Спасовали: ${ctx.foldedPlayers.join(', ')}\n`;
|
||||
}
|
||||
|
||||
// Последнее сообщение игрока
|
||||
if (ctx.lastPlayerAction) {
|
||||
context += `\n💬 Игрок написал: "${ctx.lastPlayerAction}"\n`;
|
||||
}
|
||||
|
||||
return context || 'Идёт игра.';
|
||||
|
|
@ -1282,6 +1351,102 @@ ${isWin ? 'Покажи радость, удовлетворение или са
|
|||
*/
|
||||
clearAllHistory() {
|
||||
this.messageHistory.clear();
|
||||
},
|
||||
|
||||
/**
|
||||
* Сгенерировать поздравление игрока с сильной рукой
|
||||
*/
|
||||
async generatePlayerCongratulation(bot, botPersonality, playerName, handRank, handName, potSize, gameContext = {}) {
|
||||
const settings = this.getSettings();
|
||||
|
||||
// Определяем, насколько сильная рука (для редкости поздравлений)
|
||||
const isStrongHand = handRank >= 7; // Фулл-хаус и выше
|
||||
const isVeryStrongHand = handRank >= 9; // Стрит-флеш и роял-флеш
|
||||
|
||||
// Формируем промпт для LLM
|
||||
const congratsPrompt = `${botPersonality.systemPrompt}
|
||||
|
||||
СИТУАЦИЯ: Игрок ${playerName} только что ВЫИГРАЛ раздачу с рукой "${handName}"! Банк: ${potSize} фишек.
|
||||
|
||||
${isVeryStrongHand ? 'Это ОЧЕНЬ редкая и сильная рука!' : (isStrongHand ? 'Это сильная рука!' : 'Это хорошая рука!')}
|
||||
|
||||
Поздравь игрока КРАТКО (максимум 5-7 слов), в своём стиле.
|
||||
${isVeryStrongHand ? 'Покажи восхищение и уважение!' : 'Будь доброжелательным.'}`;
|
||||
|
||||
// Если LLM включён, используем его
|
||||
if (settings.llmEnabled && typeof this.sendToLLM === 'function') {
|
||||
try {
|
||||
const messages = [{ role: 'user', content: 'Поздрави игрока' }];
|
||||
const response = await this.sendToLLM(congratsPrompt, messages, settings);
|
||||
if (response) return response;
|
||||
} catch (error) {
|
||||
console.error('Ошибка генерации поздравления:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Запасные поздравления
|
||||
return this.getFallbackCongratulation(botPersonality.style, handRank, handName);
|
||||
},
|
||||
|
||||
/**
|
||||
* Запасные поздравления
|
||||
*/
|
||||
getFallbackCongratulation(style, handRank, handName) {
|
||||
const congratulations = {
|
||||
aggressive: {
|
||||
normal: ['Неплохо сыграно!', 'Уважаю!', 'Сильная рука!', 'Молодец!'],
|
||||
strong: ['Вау, фулл-хаус! 💪', 'Монстр-рука!', 'Красавчик!', 'Вот это да!'],
|
||||
veryStrong: [`${handName}?! Респект! 🔥`, 'НЕВЕРОЯТНО!', 'Легенда!', 'Вот это покер!']
|
||||
},
|
||||
tricky: {
|
||||
normal: ['Интересно сыграно... 😏', 'Неплохо, неплохо', 'Красиво'],
|
||||
strong: ['Фулл-хаус? Впечатляет! 👏', 'Загадочная игра...', 'Сильно!'],
|
||||
veryStrong: [`${handName}... Я в восторге! 😮`, 'Невероятная рука!', 'Потрясающе!']
|
||||
},
|
||||
oldschool: {
|
||||
normal: ['Молодец, голубчик!', 'Хорошо, хорошо!', 'Эх, красота!'],
|
||||
strong: ['Фулл-хаус! Красота! 👴', 'Вот это рука!', 'Как в старые времена!'],
|
||||
veryStrong: [`${handName}! За 50 лет такое редко видел! 😲`, 'Ух ты!', 'Легенда!']
|
||||
},
|
||||
mathematical: {
|
||||
normal: ['Хороший EV!', 'Статистически сильно', '+EV рука'],
|
||||
strong: ['Фулл-хаус! Rare! 📊', 'Топ 0.1% рук!', 'Вероятность низкая!'],
|
||||
veryStrong: [`${handName}... 0.00154%! 🤯`, 'Математическое чудо!', 'Редчайший случай!']
|
||||
},
|
||||
lucky: {
|
||||
normal: ['Ура! Молодец! 😊', 'Везунчик! 🍀', 'Здорово!'],
|
||||
strong: ['Фулл-хаус! Удача! 🍀✨', 'Карты тебя любят!', 'Талисман работает! 💫'],
|
||||
veryStrong: [`${handName}! ЭТО МАГИЯ! 🎉🍀`, 'Невероятная удача!', 'Чудо! ✨💚']
|
||||
},
|
||||
silent: {
|
||||
normal: ['Хм. Неплохо.', 'Да.', '...👍'],
|
||||
strong: ['Фулл-хаус... Ого.', 'Впечатляет.', '...!'],
|
||||
veryStrong: [`${handName}?! ...`, 'Вау.', '😮']
|
||||
},
|
||||
tilted: {
|
||||
normal: ['Ну везёт же... 😒', 'Тебе повезло!', 'Конечно...'],
|
||||
strong: ['Фулл-хаус?! Ладно, красиво 😤', 'Повезло сильно!', 'Не может быть!'],
|
||||
veryStrong: [`${handName}... Я не верю! 😱`, 'КАК?!', 'Это читерство! (шучу)']
|
||||
},
|
||||
professional: {
|
||||
normal: ['GG WP!', 'Nice hand!', 'Хорошо сыграно'],
|
||||
strong: ['Фулл-хаус. Респект! 💎', 'Strong hand, WP!', 'Impressive!'],
|
||||
veryStrong: [`${handName}! Incredible! 🎯`, 'Amazing hand!', 'Legendary!']
|
||||
}
|
||||
};
|
||||
|
||||
const styleCongrats = congratulations[style] || congratulations.professional;
|
||||
|
||||
let pool;
|
||||
if (handRank >= 9) {
|
||||
pool = styleCongrats.veryStrong;
|
||||
} else if (handRank >= 7) {
|
||||
pool = styleCongrats.strong;
|
||||
} else {
|
||||
pool = styleCongrats.normal;
|
||||
}
|
||||
|
||||
return pool[Math.floor(Math.random() * pool.length)];
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -197,6 +197,7 @@ function updateUserDisplay() {
|
|||
const nameEl = document.getElementById('user-display-name');
|
||||
const badgeEl = document.getElementById('user-role-badge');
|
||||
const adminBtn = document.getElementById('admin-btn');
|
||||
const serverUrlGroup = document.getElementById('server-url-group');
|
||||
|
||||
if (currentUser) {
|
||||
nameEl.textContent = currentUser.username;
|
||||
|
|
@ -205,9 +206,11 @@ function updateUserDisplay() {
|
|||
badgeEl.style.display = 'inline';
|
||||
badgeEl.textContent = 'ADMIN';
|
||||
if (adminBtn) adminBtn.style.display = 'block';
|
||||
if (serverUrlGroup) serverUrlGroup.style.display = 'block';
|
||||
} else {
|
||||
badgeEl.style.display = 'none';
|
||||
if (adminBtn) adminBtn.style.display = 'none';
|
||||
if (serverUrlGroup) serverUrlGroup.style.display = 'none';
|
||||
}
|
||||
|
||||
// Обновляем поля имени в формах
|
||||
|
|
|
|||
|
|
@ -275,6 +275,13 @@
|
|||
|
||||
<!-- Карты игрока -->
|
||||
<div class="player-hand-container glass-card">
|
||||
<div class="player-info-row">
|
||||
<div class="player-name-display" id="player-name-display">Игрок</div>
|
||||
<div class="player-balance-display">
|
||||
<span class="balance-icon">💰</span>
|
||||
<span class="balance-amount" id="player-balance">1000</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="player-cards" id="player-cards">
|
||||
<div class="card card-back"></div>
|
||||
<div class="card card-back"></div>
|
||||
|
|
@ -443,7 +450,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="form-group admin-only" id="server-url-group" style="display: none;">
|
||||
<label>Сервер WebSocket</label>
|
||||
<input type="text" id="server-url" class="input" value="ws://localhost:3000" onchange="updateSettings()">
|
||||
</div>
|
||||
|
|
|
|||
213
public/main.js
213
public/main.js
|
|
@ -138,6 +138,13 @@ function showScreen(screenId) {
|
|||
if (screenId === 'leaderboard-screen') {
|
||||
renderLeaderboard();
|
||||
}
|
||||
|
||||
// Загрузка админских настроек
|
||||
if (screenId === 'admin-screen') {
|
||||
if (typeof loadAdminSettings === 'function') {
|
||||
loadAdminSettings();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -559,6 +566,7 @@ function updateGameUI() {
|
|||
const player = game.players.find(p => p.id === currentPlayerId);
|
||||
if (player) {
|
||||
renderPlayerHand(player.hand, game.communityCards);
|
||||
updatePlayerBalance(player);
|
||||
}
|
||||
|
||||
updateActionPanel(player, game);
|
||||
|
|
@ -598,6 +606,7 @@ function updateGameUIFromServer(room) {
|
|||
const myPlayer = players.find(p => p.id === currentPlayerId);
|
||||
if (myPlayer) {
|
||||
renderPlayerHand(myPlayer.hand, communityCards);
|
||||
updatePlayerBalance(myPlayer);
|
||||
updateActionPanelFromServer(myPlayer, room);
|
||||
}
|
||||
|
||||
|
|
@ -780,6 +789,24 @@ function applyCardBackToNewCards() {
|
|||
*/
|
||||
function renderPlayerHand(hand, communityCards) {
|
||||
const container = document.getElementById('player-cards');
|
||||
|
||||
// Проверяем, изменились ли карты
|
||||
const currentHandKey = hand && hand.length > 0
|
||||
? hand.map(c => `${c.suit}-${c.rank}`).join(',')
|
||||
: 'empty';
|
||||
|
||||
// Если карты не изменились, не перерисовываем
|
||||
if (container.dataset.currentHand === currentHandKey) {
|
||||
// Только обновляем силу руки, если изменились общие карты
|
||||
if (settings.showHandStrength !== false && hand && hand.length > 0) {
|
||||
const strength = getHandStrength(hand, communityCards || []);
|
||||
document.getElementById('hand-strength').textContent = strength || '';
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Запоминаем текущие карты
|
||||
container.dataset.currentHand = currentHandKey;
|
||||
container.innerHTML = '';
|
||||
|
||||
if (!hand || hand.length === 0) {
|
||||
|
|
@ -805,6 +832,42 @@ function renderPlayerHand(hand, communityCards) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Обновить баланс игрока
|
||||
*/
|
||||
function updatePlayerBalance(player) {
|
||||
if (!player) return;
|
||||
|
||||
// Обновляем имя игрока
|
||||
const nameDisplay = document.getElementById('player-name-display');
|
||||
if (nameDisplay) {
|
||||
nameDisplay.textContent = player.name || 'Игрок';
|
||||
}
|
||||
|
||||
// Обновляем баланс
|
||||
const balanceDisplay = document.getElementById('player-balance');
|
||||
if (balanceDisplay) {
|
||||
const chips = player.chips || 0;
|
||||
balanceDisplay.textContent = chips;
|
||||
|
||||
// Добавляем анимацию при изменении баланса
|
||||
if (balanceDisplay.dataset.lastValue && balanceDisplay.dataset.lastValue !== chips.toString()) {
|
||||
const oldValue = parseInt(balanceDisplay.dataset.lastValue);
|
||||
const newValue = chips;
|
||||
|
||||
if (newValue > oldValue) {
|
||||
balanceDisplay.parentElement.classList.add('balance-increase');
|
||||
setTimeout(() => balanceDisplay.parentElement.classList.remove('balance-increase'), 500);
|
||||
} else if (newValue < oldValue) {
|
||||
balanceDisplay.parentElement.classList.add('balance-decrease');
|
||||
setTimeout(() => balanceDisplay.parentElement.classList.remove('balance-decrease'), 500);
|
||||
}
|
||||
}
|
||||
|
||||
balanceDisplay.dataset.lastValue = chips.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Обновить панель действий
|
||||
*/
|
||||
|
|
@ -1034,6 +1097,9 @@ function onHandEnd(result) {
|
|||
// НОВОЕ: Генерируем эмоциональные реакции ботов
|
||||
if (!isMultiplayer && game) {
|
||||
generateBotEmotions(result);
|
||||
|
||||
// НОВОЕ: Поздравления игрока от ботов при сильной руке
|
||||
generatePlayerCongratulations(result);
|
||||
}
|
||||
|
||||
// Показываем кнопку новой раздачи
|
||||
|
|
@ -1107,6 +1173,107 @@ async function generateBotEmotions(result) {
|
|||
}, 500 + Math.random() * 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Генерировать поздравления игрока с сильной рукой
|
||||
*/
|
||||
async function generatePlayerCongratulations(result) {
|
||||
if (!game || !result || !result.winners || !result.hands) return;
|
||||
|
||||
const winner = result.winners[0];
|
||||
const potSize = result.pot || game.pot || 0;
|
||||
|
||||
// Проверяем, что победитель - игрок (не бот)
|
||||
const winnerData = game.players.find(p => p.id === winner.id);
|
||||
if (!winnerData || winnerData.isAI) return;
|
||||
|
||||
// Получаем руку победителя
|
||||
const winnerHand = result.hands?.find(h => h.player.id === winner.id)?.hand;
|
||||
if (!winnerHand) return;
|
||||
|
||||
const handRank = winnerHand.rank;
|
||||
const handName = winnerHand.name;
|
||||
|
||||
// Определяем вероятность поздравления в зависимости от силы руки
|
||||
let congratsProbability = 0;
|
||||
|
||||
if (handRank >= 10) {
|
||||
// Роял-флеш - 90% вероятность поздравления
|
||||
congratsProbability = 0.9;
|
||||
} else if (handRank >= 9) {
|
||||
// Стрит-флеш - 80% вероятность
|
||||
congratsProbability = 0.8;
|
||||
} else if (handRank >= 8) {
|
||||
// Каре - 60% вероятность
|
||||
congratsProbability = 0.6;
|
||||
} else if (handRank >= 7) {
|
||||
// Фулл-хаус - 40% вероятность
|
||||
congratsProbability = 0.4;
|
||||
} else if (handRank >= 6) {
|
||||
// Флеш - 25% вероятность
|
||||
congratsProbability = 0.25;
|
||||
} else if (handRank >= 5) {
|
||||
// Стрит - 15% вероятность
|
||||
congratsProbability = 0.15;
|
||||
} else {
|
||||
// Слабые руки - не поздравляем
|
||||
return;
|
||||
}
|
||||
|
||||
// Решаем, будем ли поздравлять
|
||||
if (Math.random() > congratsProbability) return;
|
||||
|
||||
// Выбираем случайного бота для поздравления
|
||||
const availableBots = game.players.filter(p => p.isAI && !p.folded);
|
||||
if (availableBots.length === 0) return;
|
||||
|
||||
const bot = availableBots[Math.floor(Math.random() * availableBots.length)];
|
||||
|
||||
// Получаем личность бота
|
||||
let botPersonality;
|
||||
if (typeof botPersonalities !== 'undefined' && bot.personalityId) {
|
||||
botPersonality = botPersonalities[bot.personalityId];
|
||||
}
|
||||
|
||||
if (!botPersonality) {
|
||||
botPersonality = { style: 'professional' };
|
||||
}
|
||||
|
||||
// Задержка перед поздравлением (1000-2500ms, чтобы не конфликтовать с эмоциями)
|
||||
setTimeout(async () => {
|
||||
try {
|
||||
let congratulation;
|
||||
|
||||
if (typeof llmChat !== 'undefined' && llmChat.generatePlayerCongratulation) {
|
||||
congratulation = await llmChat.generatePlayerCongratulation(
|
||||
bot,
|
||||
botPersonality,
|
||||
winnerData.name,
|
||||
handRank,
|
||||
handName,
|
||||
potSize,
|
||||
{ phase: currentGamePhase }
|
||||
);
|
||||
} else {
|
||||
// Запасной вариант
|
||||
const congrats = [
|
||||
`${handName}! Респект!`,
|
||||
'Сильная рука!',
|
||||
'Впечатляет!',
|
||||
'Молодец!',
|
||||
'GG WP!'
|
||||
];
|
||||
congratulation = congrats[Math.floor(Math.random() * congrats.length)];
|
||||
}
|
||||
|
||||
if (congratulation) {
|
||||
addChatMessage('game', bot.name, congratulation);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Ошибка генерации поздравления:', error);
|
||||
}
|
||||
}, 1000 + Math.random() * 1500);
|
||||
}
|
||||
|
||||
/**
|
||||
* Закрыть модальное окно результата
|
||||
*/
|
||||
|
|
@ -1221,13 +1388,53 @@ function sendGameChat() {
|
|||
// Если обращение не найдено, выбираем случайного бота
|
||||
const bot = targetBot || bots[Math.floor(Math.random() * bots.length)];
|
||||
|
||||
// Формируем контекст игры для LLM
|
||||
// Формируем расширенный контекст игры для LLM
|
||||
const gameContext = {
|
||||
// Фаза игры
|
||||
phase: currentGamePhase,
|
||||
phaseRu: {
|
||||
'preflop': 'префлоп (карты только что розданы)',
|
||||
'flop': 'флоп (3 карты на столе)',
|
||||
'turn': 'терн (4 карты на столе)',
|
||||
'river': 'ривер (5 карт на столе)',
|
||||
'showdown': 'вскрытие карт'
|
||||
}[currentGamePhase] || currentGamePhase,
|
||||
|
||||
// Банк и текущая ставка
|
||||
pot: game.pot,
|
||||
currentBet: game.currentBet,
|
||||
|
||||
// Общие карты на столе
|
||||
communityCards: game.communityCards?.map(c => `${c.rank}${SUIT_SYMBOLS[c.suit]}`) || [],
|
||||
communityCardsCount: game.communityCards?.length || 0,
|
||||
|
||||
// Мои данные
|
||||
myName: bot.name,
|
||||
myChips: bot.chips,
|
||||
lastAction: message,
|
||||
communityCards: game.communityCards?.map(c => `${c.rank}${c.suit}`) || [],
|
||||
myBet: bot.bet,
|
||||
myTotalBet: bot.totalBet,
|
||||
myCards: bot.hand?.map(c => `${c.rank}${SUIT_SYMBOLS[c.suit]}`) || [],
|
||||
myPosition: bot.isDealer ? 'дилер' : (bot.isSmallBlind ? 'малый блайнд' : (bot.isBigBlind ? 'большой блайнд' : 'обычная позиция')),
|
||||
|
||||
// Информация о других игроках
|
||||
players: game.players.map(p => ({
|
||||
name: p.name,
|
||||
chips: p.chips,
|
||||
bet: p.bet,
|
||||
totalBet: p.totalBet,
|
||||
folded: p.folded,
|
||||
allIn: p.allIn,
|
||||
lastAction: p.lastAction,
|
||||
isDealer: p.isDealer,
|
||||
isMe: p.id === bot.id
|
||||
})),
|
||||
|
||||
// Активные игроки (не спасовали)
|
||||
activePlayers: game.players.filter(p => !p.folded).map(p => p.name),
|
||||
foldedPlayers: game.players.filter(p => p.folded).map(p => p.name),
|
||||
|
||||
// Последние действия
|
||||
lastPlayerAction: message,
|
||||
mentionedByName: targetBot !== null // Указываем, что игрок обратился по имени
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1077,6 +1077,78 @@ body::before {
|
|||
gap: 8px;
|
||||
}
|
||||
|
||||
.player-info-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
padding: 8px 16px;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.player-name-display {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.player-balance-display {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 4px 12px;
|
||||
background: rgba(16, 185, 129, 0.1);
|
||||
border: 1px solid rgba(16, 185, 129, 0.3);
|
||||
border-radius: 12px;
|
||||
transition: all var(--transition-fast);
|
||||
}
|
||||
|
||||
.player-balance-display.balance-increase {
|
||||
animation: balanceIncrease 0.5s ease;
|
||||
}
|
||||
|
||||
.player-balance-display.balance-decrease {
|
||||
animation: balanceDecrease 0.5s ease;
|
||||
}
|
||||
|
||||
@keyframes balanceIncrease {
|
||||
0%, 100% {
|
||||
background: rgba(16, 185, 129, 0.1);
|
||||
border-color: rgba(16, 185, 129, 0.3);
|
||||
transform: scale(1);
|
||||
}
|
||||
50% {
|
||||
background: rgba(16, 185, 129, 0.3);
|
||||
border-color: rgba(16, 185, 129, 0.6);
|
||||
transform: scale(1.05);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes balanceDecrease {
|
||||
0%, 100% {
|
||||
background: rgba(16, 185, 129, 0.1);
|
||||
border-color: rgba(16, 185, 129, 0.3);
|
||||
transform: scale(1);
|
||||
}
|
||||
50% {
|
||||
background: rgba(239, 68, 68, 0.2);
|
||||
border-color: rgba(239, 68, 68, 0.4);
|
||||
transform: scale(0.95);
|
||||
}
|
||||
}
|
||||
|
||||
.balance-icon {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.balance-amount {
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
color: var(--success);
|
||||
min-width: 50px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.player-cards {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
|
|
|
|||
Loading…
Reference in New Issue