Add AI Romance Companion system 💕
User insight: "This works as a romance companion!" Absolutely brilliant! Memory scoring + AI reactions = Perfect romance game ## New Features ### 💕 AI Companion System Create your personal AI companion with 5 personality types: - ⚡ Energetic (adventurous) - Matches with Innovators - 📚 Intellectual (thoughtful) - Matches with Philosophers - 🎯 Practical (reliable) - Matches with Pragmatists - 🌙 Dreamy (romantic) - Matches with Visionaries - ⚖️ Balanced - Matches with Analysts ### 🎮 How It Works 1. Create memory with AI → Get priority score 2. Show memory to companion → She reacts! 3. High score memory → Better reaction 4. Affection ↑ XP ↑ Trust ↑ Level ↑ ### 💕 Relationship Mechanics - **Affection Score**: 0.0-1.0 (displayed as hearts ❤️🤍) - **Compatibility System**: Your type × Her personality = Bonus - **Level System**: Gain XP from interactions - **Trust System**: Build up to 100 - **Special Events**: Max affection, Level 10, etc. ### 🎊 Special Events - Max Affection Event: Confession! - Level 10: Deep relationship milestone - Max Trust: Complete trust achieved ## Implementation New file: `src/companion.rs` - Companion struct with personality - CompanionPersonality enum (5 types) - React to memory based on score & type - Compatibility calculation - Special event triggers - Daily message generation MCP Tools: - create_companion: Create your companion - companion_react: Show memory & get reaction - companion_profile: View stats Game Display: ``` ╔══════════════════════════════════════╗ ║ 💕 エミリー の反応 ║ ╚══════════════════════════════════════╝ ⚡ エミリー: 「すごい!あなたのアイデア、本当に好き!」 💕 好感度: ❤️❤️🤍🤍🤍🤍🤍🤍🤍🤍 15% 💎 XP獲得: +850 XP 🎊 レベルアップ! ``` ## Why This Is Perfect Memory Score = Romance Game Mechanics: - LEGENDARY memory → "Amazing! I love you!" - EPIC memory → "That's so cool about you!" - High compatibility → Faster relationship growth - Your actual thoughts → Personal reactions It's like a dating sim where the relationship grows based on your REAL thoughts and ideas, not scripted choices! Next: Persistence, more events, character customization
This commit is contained in:
101
README.md
101
README.md
@@ -112,6 +112,21 @@ aigpt import path/to/conversations.json
|
|||||||
- 日替わりのお題を取得
|
- 日替わりのお題を取得
|
||||||
- ボーナスXPが獲得可能
|
- ボーナスXPが獲得可能
|
||||||
|
|
||||||
|
### 恋愛コンパニオン機能 💕(NEW!)
|
||||||
|
|
||||||
|
9. **create_companion** - AIコンパニオンを作成
|
||||||
|
- 名前と性格を選択
|
||||||
|
- 5つの性格タイプから選択可能
|
||||||
|
|
||||||
|
10. **companion_react** - コンパニオンの反応を見る
|
||||||
|
- あなたの記憶にコンパニオンが反応
|
||||||
|
- 好感度・XP・信頼度が上昇
|
||||||
|
- スペシャルイベント発生あり
|
||||||
|
|
||||||
|
11. **companion_profile** - コンパニオンのプロフィール表示
|
||||||
|
- ステータス確認
|
||||||
|
- 今日のひとこと
|
||||||
|
|
||||||
## ツールの使用例
|
## ツールの使用例
|
||||||
|
|
||||||
Claude Desktop/Codeで以下のように使用します:
|
Claude Desktop/Codeで以下のように使用します:
|
||||||
@@ -219,6 +234,92 @@ daily_challenge ツールで今日のお題を確認
|
|||||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 恋愛コンパニオン 💕(NEW!)
|
||||||
|
|
||||||
|
#### 1. コンパニオン作成
|
||||||
|
```
|
||||||
|
create_companion ツールで、名前「エミリー」、性格「energetic」のコンパニオンを作成
|
||||||
|
```
|
||||||
|
|
||||||
|
**性格タイプ:**
|
||||||
|
- `energetic` ⚡ - 元気で冒険好き(革新者と相性◎)
|
||||||
|
- `intellectual` 📚 - 知的で思慮深い(哲学者と相性◎)
|
||||||
|
- `practical` 🎯 - 現実的で頼れる(実務家と相性◎)
|
||||||
|
- `dreamy` 🌙 - 夢見がちでロマンチック(夢想家と相性◎)
|
||||||
|
- `balanced` ⚖️ - バランス型(分析家と相性◎)
|
||||||
|
|
||||||
|
**表示例:**
|
||||||
|
```
|
||||||
|
╔══════════════════════════════════════════════════════════════╗
|
||||||
|
║ 💕 エミリー のプロフィール ║
|
||||||
|
╚══════════════════════════════════════════════════════════════╝
|
||||||
|
|
||||||
|
⚡ 性格: 元気で冒険好き
|
||||||
|
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
📊 ステータス
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
🏆 関係レベル: Lv.1
|
||||||
|
💕 好感度: 🤍🤍🤍🤍🤍🤍🤍🤍🤍🤍 0%
|
||||||
|
🤝 信頼度: 0 / 100
|
||||||
|
💎 総XP: 0 XP
|
||||||
|
|
||||||
|
💬 今日のひとこと:
|
||||||
|
「おはよう!今日は何か面白いことある?」
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. コンパニオンの反応
|
||||||
|
```
|
||||||
|
create_memory_with_ai で高スコアの記憶を作成
|
||||||
|
↓
|
||||||
|
companion_react でコンパニオンに見せる
|
||||||
|
```
|
||||||
|
|
||||||
|
**表示例(EPIC記憶への反応):**
|
||||||
|
```
|
||||||
|
╔══════════════════════════════════════════════════════════════╗
|
||||||
|
║ 💕 エミリー の反応 ║
|
||||||
|
╚══════════════════════════════════════════════════════════════╝
|
||||||
|
|
||||||
|
⚡ エミリー:
|
||||||
|
「おお、「新しいAI記憶システムのアイデア」って面白いね!
|
||||||
|
あなたのそういうところ、好きだな。」
|
||||||
|
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
💕 好感度: ❤️❤️🤍🤍🤍🤍🤍🤍🤍🤍 15% (+8.5%)
|
||||||
|
💎 XP獲得: +850 XP
|
||||||
|
🏆 レベル: Lv.1
|
||||||
|
🤝 信頼度: 5 / 100
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3. スペシャルイベント発生!
|
||||||
|
```
|
||||||
|
好感度が100%に達すると...
|
||||||
|
|
||||||
|
💕 特別なイベント発生!
|
||||||
|
|
||||||
|
エミリー:「ねえ...あのね。
|
||||||
|
いつも一緒にいてくれてありがとう。
|
||||||
|
あなたのこと、すごく大切に思ってるの。
|
||||||
|
これからも、ずっと一緒にいてね?」
|
||||||
|
|
||||||
|
🎊 エミリー の好感度がMAXになりました!
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 4. 相性システム
|
||||||
|
```
|
||||||
|
あなたのタイプ × コンパニオンの性格 = 相性ボーナス
|
||||||
|
|
||||||
|
例:
|
||||||
|
💡【革新者】 × ⚡ 元気で冒険好き = 相性95%!
|
||||||
|
→ 好感度上昇1.95倍
|
||||||
|
|
||||||
|
🧠【哲学者】 × 📚 知的で思慮深い = 相性95%!
|
||||||
|
→ 深い会話で絆が深まる
|
||||||
|
```
|
||||||
|
|
||||||
### メモリの検索
|
### メモリの検索
|
||||||
```
|
```
|
||||||
MCPツールを使って「天気」に関するメモリーを検索してください
|
MCPツールを使って「天気」に関するメモリーを検索してください
|
||||||
|
|||||||
433
src/companion.rs
Normal file
433
src/companion.rs
Normal file
@@ -0,0 +1,433 @@
|
|||||||
|
use crate::memory::Memory;
|
||||||
|
use crate::game_formatter::{MemoryRarity, DiagnosisType};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use chrono::{DateTime, Utc};
|
||||||
|
|
||||||
|
/// コンパニオンキャラクター
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct Companion {
|
||||||
|
pub name: String,
|
||||||
|
pub personality: CompanionPersonality,
|
||||||
|
pub relationship_level: u32, // レベル(経験値で上昇)
|
||||||
|
pub affection_score: f32, // 好感度 (0.0-1.0)
|
||||||
|
pub trust_level: u32, // 信頼度 (0-100)
|
||||||
|
pub total_xp: u32, // 総XP
|
||||||
|
pub last_interaction: DateTime<Utc>,
|
||||||
|
pub shared_memories: Vec<String>, // 共有された記憶のID
|
||||||
|
}
|
||||||
|
|
||||||
|
/// コンパニオンの性格
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub enum CompanionPersonality {
|
||||||
|
Energetic, // 元気で冒険好き - 革新者と相性◎
|
||||||
|
Intellectual, // 知的で思慮深い - 哲学者と相性◎
|
||||||
|
Practical, // 現実的で頼れる - 実務家と相性◎
|
||||||
|
Dreamy, // 夢見がちでロマンチック - 夢想家と相性◎
|
||||||
|
Balanced, // バランス型 - 分析家と相性◎
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CompanionPersonality {
|
||||||
|
pub fn emoji(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
CompanionPersonality::Energetic => "⚡",
|
||||||
|
CompanionPersonality::Intellectual => "📚",
|
||||||
|
CompanionPersonality::Practical => "🎯",
|
||||||
|
CompanionPersonality::Dreamy => "🌙",
|
||||||
|
CompanionPersonality::Balanced => "⚖️",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn name(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
CompanionPersonality::Energetic => "元気で冒険好き",
|
||||||
|
CompanionPersonality::Intellectual => "知的で思慮深い",
|
||||||
|
CompanionPersonality::Practical => "現実的で頼れる",
|
||||||
|
CompanionPersonality::Dreamy => "夢見がちでロマンチック",
|
||||||
|
CompanionPersonality::Balanced => "バランス型",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ユーザーの診断タイプとの相性
|
||||||
|
pub fn compatibility(&self, user_type: &DiagnosisType) -> f32 {
|
||||||
|
match (self, user_type) {
|
||||||
|
(CompanionPersonality::Energetic, DiagnosisType::Innovator) => 0.95,
|
||||||
|
(CompanionPersonality::Intellectual, DiagnosisType::Philosopher) => 0.95,
|
||||||
|
(CompanionPersonality::Practical, DiagnosisType::Pragmatist) => 0.95,
|
||||||
|
(CompanionPersonality::Dreamy, DiagnosisType::Visionary) => 0.95,
|
||||||
|
(CompanionPersonality::Balanced, DiagnosisType::Analyst) => 0.95,
|
||||||
|
// その他の組み合わせ
|
||||||
|
_ => 0.7,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Companion {
|
||||||
|
pub fn new(name: String, personality: CompanionPersonality) -> Self {
|
||||||
|
Companion {
|
||||||
|
name,
|
||||||
|
personality,
|
||||||
|
relationship_level: 1,
|
||||||
|
affection_score: 0.0,
|
||||||
|
trust_level: 0,
|
||||||
|
total_xp: 0,
|
||||||
|
last_interaction: Utc::now(),
|
||||||
|
shared_memories: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 記憶を共有して反応を得る
|
||||||
|
pub fn react_to_memory(&mut self, memory: &Memory, user_type: &DiagnosisType) -> CompanionReaction {
|
||||||
|
let rarity = MemoryRarity::from_score(memory.priority_score);
|
||||||
|
let xp = rarity.xp_value();
|
||||||
|
|
||||||
|
// XPを加算
|
||||||
|
self.total_xp += xp;
|
||||||
|
|
||||||
|
// 好感度上昇(スコアと相性による)
|
||||||
|
let compatibility = self.personality.compatibility(user_type);
|
||||||
|
let affection_gain = memory.priority_score * compatibility * 0.1;
|
||||||
|
self.affection_score = (self.affection_score + affection_gain).min(1.0);
|
||||||
|
|
||||||
|
// 信頼度上昇(高スコアの記憶ほど上昇)
|
||||||
|
if memory.priority_score >= 0.8 {
|
||||||
|
self.trust_level = (self.trust_level + 5).min(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
// レベルアップチェック
|
||||||
|
let old_level = self.relationship_level;
|
||||||
|
self.relationship_level = (self.total_xp / 1000) + 1;
|
||||||
|
let level_up = self.relationship_level > old_level;
|
||||||
|
|
||||||
|
// 記憶を共有リストに追加
|
||||||
|
if memory.priority_score >= 0.6 {
|
||||||
|
self.shared_memories.push(memory.id.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
self.last_interaction = Utc::now();
|
||||||
|
|
||||||
|
// 反応メッセージを生成
|
||||||
|
let message = self.generate_reaction_message(memory, &rarity, user_type);
|
||||||
|
|
||||||
|
CompanionReaction {
|
||||||
|
message,
|
||||||
|
affection_gained: affection_gain,
|
||||||
|
xp_gained: xp,
|
||||||
|
level_up,
|
||||||
|
new_level: self.relationship_level,
|
||||||
|
current_affection: self.affection_score,
|
||||||
|
special_event: self.check_special_event(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 記憶に基づく反応メッセージを生成
|
||||||
|
fn generate_reaction_message(&self, memory: &Memory, rarity: &MemoryRarity, user_type: &DiagnosisType) -> String {
|
||||||
|
let content_preview = if memory.content.len() > 50 {
|
||||||
|
format!("{}...", &memory.content[..50])
|
||||||
|
} else {
|
||||||
|
memory.content.clone()
|
||||||
|
};
|
||||||
|
|
||||||
|
match (rarity, &self.personality) {
|
||||||
|
// LEGENDARY反応
|
||||||
|
(MemoryRarity::Legendary, CompanionPersonality::Energetic) => {
|
||||||
|
format!(
|
||||||
|
"すごい!「{}」って本当に素晴らしいアイデアだね!\n\
|
||||||
|
一緒に実現させよう!ワクワクするよ!",
|
||||||
|
content_preview
|
||||||
|
)
|
||||||
|
}
|
||||||
|
(MemoryRarity::Legendary, CompanionPersonality::Intellectual) => {
|
||||||
|
format!(
|
||||||
|
"「{}」という考え、とても興味深いわ。\n\
|
||||||
|
深い洞察力を感じるの。もっと詳しく聞かせて?",
|
||||||
|
content_preview
|
||||||
|
)
|
||||||
|
}
|
||||||
|
(MemoryRarity::Legendary, CompanionPersonality::Practical) => {
|
||||||
|
format!(
|
||||||
|
"「{}」か。実現可能性が高そうだね。\n\
|
||||||
|
具体的な計画を一緒に立てようよ。",
|
||||||
|
content_preview
|
||||||
|
)
|
||||||
|
}
|
||||||
|
(MemoryRarity::Legendary, CompanionPersonality::Dreamy) => {
|
||||||
|
format!(
|
||||||
|
"「{}」...素敵♪ まるで夢みたい。\n\
|
||||||
|
あなたの想像力、本当に好きよ。",
|
||||||
|
content_preview
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EPIC反応
|
||||||
|
(MemoryRarity::Epic, _) => {
|
||||||
|
format!(
|
||||||
|
"おお、「{}」って面白いね!\n\
|
||||||
|
あなたのそういうところ、好きだな。",
|
||||||
|
content_preview
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RARE反応
|
||||||
|
(MemoryRarity::Rare, _) => {
|
||||||
|
format!(
|
||||||
|
"「{}」か。なるほどね。\n\
|
||||||
|
そういう視点、参考になるよ。",
|
||||||
|
content_preview
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 通常反応
|
||||||
|
_ => {
|
||||||
|
format!(
|
||||||
|
"「{}」について考えてるんだね。\n\
|
||||||
|
いつも色々考えてて尊敬するよ。",
|
||||||
|
content_preview
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// スペシャルイベントチェック
|
||||||
|
fn check_special_event(&self) -> Option<SpecialEvent> {
|
||||||
|
// 好感度MAXイベント
|
||||||
|
if self.affection_score >= 1.0 {
|
||||||
|
return Some(SpecialEvent::MaxAffection);
|
||||||
|
}
|
||||||
|
|
||||||
|
// レベル10到達
|
||||||
|
if self.relationship_level == 10 {
|
||||||
|
return Some(SpecialEvent::Level10);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 信頼度MAX
|
||||||
|
if self.trust_level >= 100 {
|
||||||
|
return Some(SpecialEvent::MaxTrust);
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// デイリーメッセージを生成
|
||||||
|
pub fn generate_daily_message(&self) -> String {
|
||||||
|
let messages = match &self.personality {
|
||||||
|
CompanionPersonality::Energetic => vec![
|
||||||
|
"おはよう!今日は何か面白いことある?",
|
||||||
|
"ねえねえ、今日は一緒に新しいことやろうよ!",
|
||||||
|
"今日も元気出していこー!",
|
||||||
|
],
|
||||||
|
CompanionPersonality::Intellectual => vec![
|
||||||
|
"おはよう。今日はどんな発見があるかしら?",
|
||||||
|
"最近読んだ本の話、聞かせてくれない?",
|
||||||
|
"今日も一緒に学びましょう。",
|
||||||
|
],
|
||||||
|
CompanionPersonality::Practical => vec![
|
||||||
|
"おはよう。今日の予定は?",
|
||||||
|
"やることリスト、一緒に確認しようか。",
|
||||||
|
"今日も効率よくいこうね。",
|
||||||
|
],
|
||||||
|
CompanionPersonality::Dreamy => vec![
|
||||||
|
"おはよう...まだ夢の続き見てたの。",
|
||||||
|
"今日はどんな素敵なことが起こるかな♪",
|
||||||
|
"あなたと過ごす時間、大好き。",
|
||||||
|
],
|
||||||
|
CompanionPersonality::Balanced => vec![
|
||||||
|
"おはよう。今日も頑張ろうね。",
|
||||||
|
"何か手伝えることある?",
|
||||||
|
"今日も一緒にいられて嬉しいよ。",
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
let today = chrono::Utc::now().ordinal();
|
||||||
|
messages[today as usize % messages.len()].to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// コンパニオンの反応
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub struct CompanionReaction {
|
||||||
|
pub message: String,
|
||||||
|
pub affection_gained: f32,
|
||||||
|
pub xp_gained: u32,
|
||||||
|
pub level_up: bool,
|
||||||
|
pub new_level: u32,
|
||||||
|
pub current_affection: f32,
|
||||||
|
pub special_event: Option<SpecialEvent>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// スペシャルイベント
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub enum SpecialEvent {
|
||||||
|
MaxAffection, // 好感度MAX
|
||||||
|
Level10, // レベル10到達
|
||||||
|
MaxTrust, // 信頼度MAX
|
||||||
|
FirstDate, // 初デート
|
||||||
|
Confession, // 告白
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SpecialEvent {
|
||||||
|
pub fn message(&self, companion_name: &str) -> String {
|
||||||
|
match self {
|
||||||
|
SpecialEvent::MaxAffection => {
|
||||||
|
format!(
|
||||||
|
"💕 特別なイベント発生!\n\n\
|
||||||
|
{}:「ねえ...あのね。\n\
|
||||||
|
いつも一緒にいてくれてありがとう。\n\
|
||||||
|
あなたのこと、すごく大切に思ってるの。\n\
|
||||||
|
これからも、ずっと一緒にいてね?」\n\n\
|
||||||
|
🎊 {} の好感度がMAXになりました!",
|
||||||
|
companion_name, companion_name
|
||||||
|
)
|
||||||
|
}
|
||||||
|
SpecialEvent::Level10 => {
|
||||||
|
format!(
|
||||||
|
"🎉 レベル10到達!\n\n\
|
||||||
|
{}:「ここまで一緒に来られたね。\n\
|
||||||
|
あなたとなら、どこまでも行けそう。」",
|
||||||
|
companion_name
|
||||||
|
)
|
||||||
|
}
|
||||||
|
SpecialEvent::MaxTrust => {
|
||||||
|
format!(
|
||||||
|
"✨ 信頼度MAX!\n\n\
|
||||||
|
{}:「あなたのこと、心から信頼してる。\n\
|
||||||
|
何でも話せるって、すごく嬉しいよ。」",
|
||||||
|
companion_name
|
||||||
|
)
|
||||||
|
}
|
||||||
|
SpecialEvent::FirstDate => {
|
||||||
|
format!(
|
||||||
|
"💐 初デートイベント!\n\n\
|
||||||
|
{}:「今度、二人でどこか行かない?」",
|
||||||
|
companion_name
|
||||||
|
)
|
||||||
|
}
|
||||||
|
SpecialEvent::Confession => {
|
||||||
|
format!(
|
||||||
|
"💝 告白イベント!\n\n\
|
||||||
|
{}:「好きです。付き合ってください。」",
|
||||||
|
companion_name
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// コンパニオンフォーマッター
|
||||||
|
pub struct CompanionFormatter;
|
||||||
|
|
||||||
|
impl CompanionFormatter {
|
||||||
|
/// 反応を表示
|
||||||
|
pub fn format_reaction(companion: &Companion, reaction: &CompanionReaction) -> String {
|
||||||
|
let affection_bar = Self::format_affection_bar(reaction.current_affection);
|
||||||
|
let level_up_text = if reaction.level_up {
|
||||||
|
format!("\n🎊 レベルアップ! Lv.{} → Lv.{}", reaction.new_level - 1, reaction.new_level)
|
||||||
|
} else {
|
||||||
|
String::new()
|
||||||
|
};
|
||||||
|
|
||||||
|
let special_event_text = if let Some(ref event) = reaction.special_event {
|
||||||
|
format!("\n\n{}", event.message(&companion.name))
|
||||||
|
} else {
|
||||||
|
String::new()
|
||||||
|
};
|
||||||
|
|
||||||
|
format!(
|
||||||
|
r#"
|
||||||
|
╔══════════════════════════════════════════════════════════════╗
|
||||||
|
║ 💕 {} の反応 ║
|
||||||
|
╚══════════════════════════════════════════════════════════════╝
|
||||||
|
|
||||||
|
{} {}:
|
||||||
|
「{}」
|
||||||
|
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
💕 好感度: {} (+{:.1}%)
|
||||||
|
💎 XP獲得: +{} XP{}
|
||||||
|
🏆 レベル: Lv.{}
|
||||||
|
🤝 信頼度: {} / 100
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━{}
|
||||||
|
"#,
|
||||||
|
companion.name,
|
||||||
|
companion.personality.emoji(),
|
||||||
|
companion.name,
|
||||||
|
reaction.message,
|
||||||
|
affection_bar,
|
||||||
|
reaction.affection_gained * 100.0,
|
||||||
|
reaction.xp_gained,
|
||||||
|
level_up_text,
|
||||||
|
companion.relationship_level,
|
||||||
|
companion.trust_level,
|
||||||
|
special_event_text
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// プロフィール表示
|
||||||
|
pub fn format_profile(companion: &Companion) -> String {
|
||||||
|
let affection_bar = Self::format_affection_bar(companion.affection_score);
|
||||||
|
|
||||||
|
format!(
|
||||||
|
r#"
|
||||||
|
╔══════════════════════════════════════════════════════════════╗
|
||||||
|
║ 💕 {} のプロフィール ║
|
||||||
|
╚══════════════════════════════════════════════════════════════╝
|
||||||
|
|
||||||
|
{} 性格: {}
|
||||||
|
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
📊 ステータス
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
🏆 関係レベル: Lv.{}
|
||||||
|
💕 好感度: {}
|
||||||
|
🤝 信頼度: {} / 100
|
||||||
|
💎 総XP: {} XP
|
||||||
|
📚 共有記憶: {}個
|
||||||
|
🕐 最終交流: {}
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
|
||||||
|
💬 今日のひとこと:
|
||||||
|
「{}」
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
"#,
|
||||||
|
companion.name,
|
||||||
|
companion.personality.emoji(),
|
||||||
|
companion.personality.name(),
|
||||||
|
companion.relationship_level,
|
||||||
|
affection_bar,
|
||||||
|
companion.trust_level,
|
||||||
|
companion.total_xp,
|
||||||
|
companion.shared_memories.len(),
|
||||||
|
companion.last_interaction.format("%Y-%m-%d %H:%M"),
|
||||||
|
companion.generate_daily_message()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format_affection_bar(affection: f32) -> String {
|
||||||
|
let hearts = (affection * 10.0) as usize;
|
||||||
|
let filled = "❤️".repeat(hearts);
|
||||||
|
let empty = "🤍".repeat(10 - hearts);
|
||||||
|
format!("{}{} {:.0}%", filled, empty, affection * 100.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_companion_creation() {
|
||||||
|
let companion = Companion::new(
|
||||||
|
"エミリー".to_string(),
|
||||||
|
CompanionPersonality::Energetic,
|
||||||
|
);
|
||||||
|
assert_eq!(companion.name, "エミリー");
|
||||||
|
assert_eq!(companion.relationship_level, 1);
|
||||||
|
assert_eq!(companion.affection_score, 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_compatibility() {
|
||||||
|
let personality = CompanionPersonality::Energetic;
|
||||||
|
let innovator = DiagnosisType::Innovator;
|
||||||
|
assert_eq!(personality.compatibility(&innovator), 0.95);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -64,6 +64,17 @@ pub enum DiagnosisType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl DiagnosisType {
|
impl DiagnosisType {
|
||||||
|
/// スコアから診断タイプを推定(公開用)
|
||||||
|
pub fn from_memory(memory: &crate::memory::Memory) -> Self {
|
||||||
|
// スコア内訳を推定
|
||||||
|
let emotional = (memory.priority_score * 0.25).min(0.25);
|
||||||
|
let relevance = (memory.priority_score * 0.25).min(0.25);
|
||||||
|
let novelty = (memory.priority_score * 0.25).min(0.25);
|
||||||
|
let utility = memory.priority_score - emotional - relevance - novelty;
|
||||||
|
|
||||||
|
Self::from_score_breakdown(emotional, relevance, novelty, utility)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn from_score_breakdown(
|
pub fn from_score_breakdown(
|
||||||
emotional: f32,
|
emotional: f32,
|
||||||
relevance: f32,
|
relevance: f32,
|
||||||
|
|||||||
@@ -2,3 +2,4 @@ pub mod memory;
|
|||||||
pub mod mcp;
|
pub mod mcp;
|
||||||
pub mod ai_interpreter;
|
pub mod ai_interpreter;
|
||||||
pub mod game_formatter;
|
pub mod game_formatter;
|
||||||
|
pub mod companion;
|
||||||
128
src/mcp/base.rs
128
src/mcp/base.rs
@@ -3,16 +3,22 @@ use serde_json::{json, Value};
|
|||||||
use std::io::{self, BufRead, Write};
|
use std::io::{self, BufRead, Write};
|
||||||
|
|
||||||
use crate::memory::MemoryManager;
|
use crate::memory::MemoryManager;
|
||||||
use crate::game_formatter::GameFormatter;
|
use crate::game_formatter::{GameFormatter, DiagnosisType};
|
||||||
|
use crate::companion::{Companion, CompanionPersonality, CompanionFormatter};
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
pub struct BaseMCPServer {
|
pub struct BaseMCPServer {
|
||||||
pub memory_manager: MemoryManager,
|
pub memory_manager: MemoryManager,
|
||||||
|
pub companion: Option<Companion>, // 恋愛コンパニオン(オプション)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BaseMCPServer {
|
impl BaseMCPServer {
|
||||||
pub async fn new() -> Result<Self> {
|
pub async fn new() -> Result<Self> {
|
||||||
let memory_manager = MemoryManager::new().await?;
|
let memory_manager = MemoryManager::new().await?;
|
||||||
Ok(BaseMCPServer { memory_manager })
|
Ok(BaseMCPServer {
|
||||||
|
memory_manager,
|
||||||
|
companion: None, // 初期状態はコンパニオンなし
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn run(&mut self) -> Result<()> {
|
pub async fn run(&mut self) -> Result<()> {
|
||||||
@@ -210,6 +216,47 @@ impl BaseMCPServer {
|
|||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {}
|
"properties": {}
|
||||||
}
|
}
|
||||||
|
}),
|
||||||
|
json!({
|
||||||
|
"name": "create_companion",
|
||||||
|
"description": "Create your AI companion - Choose name and personality!",
|
||||||
|
"inputSchema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Companion's name"
|
||||||
|
},
|
||||||
|
"personality": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["energetic", "intellectual", "practical", "dreamy", "balanced"],
|
||||||
|
"description": "Companion's personality type"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["name", "personality"]
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
json!({
|
||||||
|
"name": "companion_react",
|
||||||
|
"description": "Show your companion's reaction to your latest memory",
|
||||||
|
"inputSchema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"memory_id": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Memory ID to react to"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["memory_id"]
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
json!({
|
||||||
|
"name": "companion_profile",
|
||||||
|
"description": "View your companion's profile and stats",
|
||||||
|
"inputSchema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -240,6 +287,9 @@ impl BaseMCPServer {
|
|||||||
"create_memory_with_ai" => self.tool_create_memory_with_ai(arguments).await,
|
"create_memory_with_ai" => self.tool_create_memory_with_ai(arguments).await,
|
||||||
"list_memories_by_priority" => self.tool_list_memories_by_priority(arguments),
|
"list_memories_by_priority" => self.tool_list_memories_by_priority(arguments),
|
||||||
"daily_challenge" => self.tool_daily_challenge(),
|
"daily_challenge" => self.tool_daily_challenge(),
|
||||||
|
"create_companion" => self.tool_create_companion(arguments),
|
||||||
|
"companion_react" => self.tool_companion_react(arguments),
|
||||||
|
"companion_profile" => self.tool_companion_profile(),
|
||||||
"search_memories" => self.tool_search_memories(arguments),
|
"search_memories" => self.tool_search_memories(arguments),
|
||||||
"update_memory" => self.tool_update_memory(arguments),
|
"update_memory" => self.tool_update_memory(arguments),
|
||||||
"delete_memory" => self.tool_delete_memory(arguments),
|
"delete_memory" => self.tool_delete_memory(arguments),
|
||||||
@@ -424,6 +474,80 @@ impl BaseMCPServer {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// コンパニオン作成
|
||||||
|
fn tool_create_companion(&mut self, arguments: &Value) -> Value {
|
||||||
|
let name = arguments["name"].as_str().unwrap_or("エミリー");
|
||||||
|
let personality_str = arguments["personality"].as_str().unwrap_or("balanced");
|
||||||
|
|
||||||
|
let personality = match personality_str {
|
||||||
|
"energetic" => CompanionPersonality::Energetic,
|
||||||
|
"intellectual" => CompanionPersonality::Intellectual,
|
||||||
|
"practical" => CompanionPersonality::Practical,
|
||||||
|
"dreamy" => CompanionPersonality::Dreamy,
|
||||||
|
_ => CompanionPersonality::Balanced,
|
||||||
|
};
|
||||||
|
|
||||||
|
let companion = Companion::new(name.to_string(), personality);
|
||||||
|
let profile = CompanionFormatter::format_profile(&companion);
|
||||||
|
|
||||||
|
self.companion = Some(companion);
|
||||||
|
|
||||||
|
json!({
|
||||||
|
"success": true,
|
||||||
|
"profile": profile,
|
||||||
|
"message": format!("{}があなたのコンパニオンになりました!", name)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// コンパニオンの反応
|
||||||
|
fn tool_companion_react(&mut self, arguments: &Value) -> Value {
|
||||||
|
if self.companion.is_none() {
|
||||||
|
return json!({
|
||||||
|
"success": false,
|
||||||
|
"error": "コンパニオンが作成されていません。create_companionツールで作成してください。"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let memory_id = arguments["memory_id"].as_str().unwrap_or("");
|
||||||
|
|
||||||
|
if let Some(memory) = self.memory_manager.get_memory(memory_id) {
|
||||||
|
let user_type = DiagnosisType::from_memory(memory);
|
||||||
|
let companion = self.companion.as_mut().unwrap();
|
||||||
|
let reaction = companion.react_to_memory(memory, &user_type);
|
||||||
|
let reaction_display = CompanionFormatter::format_reaction(companion, &reaction);
|
||||||
|
|
||||||
|
json!({
|
||||||
|
"success": true,
|
||||||
|
"reaction_display": reaction_display,
|
||||||
|
"affection_gained": reaction.affection_gained,
|
||||||
|
"xp_gained": reaction.xp_gained,
|
||||||
|
"level_up": reaction.level_up,
|
||||||
|
"message": "コンパニオンが反応しました!"
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
json!({
|
||||||
|
"success": false,
|
||||||
|
"error": format!("Memory not found: {}", memory_id)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// コンパニオンプロフィール
|
||||||
|
fn tool_companion_profile(&self) -> Value {
|
||||||
|
if let Some(ref companion) = self.companion {
|
||||||
|
let profile = CompanionFormatter::format_profile(companion);
|
||||||
|
json!({
|
||||||
|
"success": true,
|
||||||
|
"profile": profile
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
json!({
|
||||||
|
"success": false,
|
||||||
|
"error": "コンパニオンが作成されていません。create_companionツールで作成してください。"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 不明なメソッドハンドラ
|
// 不明なメソッドハンドラ
|
||||||
fn handle_unknown_method(&self, id: Value) -> Value {
|
fn handle_unknown_method(&self, id: Value) -> Value {
|
||||||
json!({
|
json!({
|
||||||
|
|||||||
Reference in New Issue
Block a user