Skip to content

Commit 9c2fcf7

Browse files
committed
Merge branch 'develop' into release/2
2 parents f1e3a72 + 781fd7c commit 9c2fcf7

28 files changed

+1098
-258
lines changed

README.md

Lines changed: 128 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,53 @@
1-
# プロダクト名
1+
# プロダクト名
2+
あれちゃう?知らんけど。
23
<!-- プロダクト名に変更してください -->
34

45
![プロダクト名](https://kc3.me/cms/wp-content/uploads/2026/02/444e7120d5cdd74aa75f7a94bf8821a5-scaled.png)
5-
<!-- プロダクト名・イメージ画像を差し変えてください -->
66

7+
<!-- プロダクト名・イメージ画像を差し変えてください -->
78

89
## チーム名
9-
チーム10 XXXX
10+
11+
チーム10 一汁三菜
12+
1013
<!-- チームIDとチーム名を入力してください -->
11-
# プロダクト名
1214

15+
# プロダクト名
16+
<img width="935" height="446" alt="image" src="https://github.com/user-attachments/assets/82e88e43-ad45-463a-9ff5-22e25bf20ccd" />
1317
> キャッチコピー(1行で「何を誰のために解決するか」)
1418
1519
# 1. プロダクト概要
1620

1721
## どんな「本当に欲しい」に着目したか
1822

19-
<!--
23+
<!--
2024
誰の、どんな "欲しい" に焦点を当てたかおしえてください。
2125
-->
22-
関東から関西に来る人におすすめor関西から関東に行く人におすすめ
23-
関西人の関西ノリは他の人に
26+
27+
関東から関西に来る人におすすめor関西から関東に行く人におすすめ
28+
関西人の関西ノリは他の人に
2429

2530
## どんなアプローチで「本当に欲しい」を実現しましたか?
2631

2732
<!--
2833
プロダクトが実現したアプローチや価値についておしえてください。
2934
-->
30-
関西人ノリをクイズで体験し、関西人を理解
35+
36+
関西人ノリをクイズで体験し、関西人を理解
37+
3138
---
3239

3340
# 2. プロダクトの機能
3441

3542
<!--
36-
このプロダクトが持つ機能について、できるだけ詳しくおしえてください。
43+
このプロダクトが持つ機能について、できるだけ詳しくおしえてください。
3744
-->
38-
関西人がチャット形式で関西ノリであるお題について説明する
39-
40-
ユーザはお題を推測し、解答を送信して正誤判定を行う
41-
---
45+
46+
関西人がチャット形式で関西ノリであるお題について説明する
47+
48+
ユーザはお題を推測し、解答を送信して正誤判定を行う
49+
50+
## 過去の面白かったチャットを見ることができるので、クイズに参加しなくても、関西人ノリを体験することができる
4251

4352
# 3. 使い方・デモ
4453

@@ -47,6 +56,17 @@
4756
必要であれば、画像・動画・GIFなどを利用しても構いません。
4857
-->
4958

59+
クイズモードと過去のチャット閲覧モードがある
60+
61+
クイズモード
62+
ローディング画面では関西人あるあるが表示されている
63+
64+
関西人が何かの話題について話し合っている
65+
この会話に参加したユーザは2人の関西人が何について話しているのかをチャットで送信する
66+
67+
正解なら、お気に入り登録をするのか、もう一度遊ぶかを選択することができる
68+
不正解なら関西人の会話がそのまま続くので、お題が何かユーザが考え続ける
69+
5070
---
5171

5272
# 4. 技術面でのこだわり
@@ -56,9 +76,38 @@
5676
技術面でのこだわりや挑戦の内容をおしえてください。
5777
名前は最後に消した方がいいかも
5878
-->
59-
Geminiのプロンプト調整(久保田)
60-
クイズとしても面白くなるために難易度の調整を行った
61-
関西人(京都人と大阪人の話し方や考え方)を再現する
79+
80+
## バックエンド
81+
82+
### 🏗️ 3層アーキテクチャ(Controller / Service / Repository)
83+
84+
- **責務の分離とDI**:
85+
各層の責務を明確に分離し、インターフェースで接続することで依存性を注入(DI)しています。Controller層はHTTPリクエストの受付・レスポンス返却、Service層はビジネスロジック、Repository層はDBアクセスを担当しています。
86+
87+
### 🤖 Gemini API 連携
88+
89+
- **プロンプト調整**:
90+
京都人と大阪人のキャラ付け、前半→中盤→終盤で難易度が段階的に下がるヒント構成など、クイズとして成立するよう細かく指示を設計しました。
91+
- **JSON構造化出力**:
92+
レスポンスのMIMEタイプとJSONスキーマを指定することで、Gemini の出力を確実にパース可能な JSON 形式に制約しています。
93+
- **生成パラメータの調整**:
94+
毎回異なるお題・会話が生成されやすいように生成パラメータを設定し、ランダム性を高めつつ日本語として破綻しないラインを狙いました。
95+
96+
### ✅ 答え合わせの仕組み
97+
98+
- **表記ゆれを網羅した正解判定**:
99+
事前に想定される正解を、漢字・ひらがな・カタカナ・英語・略称など考えうる表記ゆれを含めて複数パターン用意し、いずれかに一致すれば正解と判定します。ただしユーザに提示する正解表示は代表的な1つのみとし、シンプルな体験を保っています。
100+
101+
### 🔒 簡易的な不正対策
102+
103+
- **答え取得までの時間制限**:
104+
ログイン機能を実装しない構成の中で、ゲーム開始から一定時間(90秒)が経過するまでは答えを取得できないようにしました。APIを直接叩いて即座に答えを見るような行為を防ぐ、簡易的なセキュリティ対策として実装しています。
105+
106+
### ⚠️ エラーハンドリング
107+
108+
- **型によるエラー判定**:
109+
エラーを独自の型として定義し、型によってエラーの種別を判定しています。文字列比較に頼らないため、エラーメッセージの変更に対応しやすくしました。Controller層ではエラー種別に応じて適切なHTTPステータスコード(400 / 403 / 404 / 500)を返し分けています。
110+
62111
---
63112

64113
# 5. UI/UX面でのこだわり
@@ -67,18 +116,58 @@
67116
UIやUXのデザインの面で、工夫したこと、挑戦したことなどがあればおしえてください。
68117
板垣君or丹羽君
69118
-->
70-
・新しくヒント(チャット)が追加された際に,画面の最下部にいる場合のみ自動スクロールを適応.
71-
⇒古いヒントを見返しているときに勝手にスクロールされてしまう問題を解決
72-
・ゲームスタートのボタンを目立ちやすいオレンジ色に,高評価のストーリーボタンを白色に変更
73-
⇒ゲーム本編を強調
74-
・Material Designを利用
75-
⇒統一感のあるデザインの作成
76-
・登場人物ごとにアイコンの色が違う
77-
⇒登場人物が変わったことが分かりやすい
78-
・レスポンシブデザイン
79-
⇒スマホでもPCでもできる(メインはスマホ)
80-
・ロード画面を関西あるあるに
81-
⇒待機中も飽きさせない
119+
120+
### 🏎️ ロード画面
121+
122+
- **関西あるあるのランダム表示**:
123+
APIからのデータ取得待ち時間を、関西にまつわる「あるあるネタ」を表示することで、ユーザーを飽きさせない工夫をしています。
124+
125+
### 🎮 ゲーム中
126+
127+
- **チャット形式の直感的なUI**:
128+
親しみやすいメッセージバブルを採用し、京都人と大阪人が交互にヒントを出す演出をアイコンの使い分けで表現しています。
129+
- **ヒントの自動配信とタイマー**:
130+
一定時間ごとに新しいヒントが流れてくる臨場感を演出。タイマーにより次のヒントまでの時間を可視化しています。
131+
- **スマート自動スクロール**:
132+
新しいメッセージ追加時、画面下部にいる時のみ自動スクロール。過去のやり取りを見返している最中に勝手にスクロールされないよう配慮しました。
133+
- **自然なギブアップ誘導**:
134+
全てのヒントが出切った後、チャットの流れの中に「答えを見る?」というボタンを表示。ゲームの流れを止めずに次のアクションへ誘導します。
135+
136+
### ✨ 正解後の挙動
137+
138+
- **物語の完全公開(アコーディオン)**:
139+
正解後、「物語の続きを見る」をクリックすることで、クイズ中に出てこなかった残りの会話を全て読むことができ、ストーリーを最後まで楽しめます。
140+
- **「茶しばき(お気に入り)」機能**:
141+
面白かったヒントには、関西ならではの「茶しばき(お茶しに行こう)」という言葉でお気に入り登録が可能。心地よいハートのアニメーションで達成感を高めています。
142+
- **𝕏(Twitter)でのシェア**:
143+
クイズの結果や面白い会話を、𝕏(旧Twitter)ですぐにシェアできる機能を搭載しています。
144+
- **状況に応じたアイコンの変化**:
145+
正解なら「大阪」、不正解なら「京都」のアイコンを表示するなど、判定結果が視覚的に即座に伝わるよう工夫しました。
146+
147+
### 📑 高評価のストーリー
148+
149+
- **チャット形式のカードプレビュー**:
150+
一覧画面(掲示板)では、各物語の冒頭部分をチャット形式のままプレビュー表示。クリックする前から内容の雰囲気が直感的に伝わるデザインにしました。
151+
- **「タップで回答確認」の段階的公開**:
152+
詳細画面では、あえて答えを最初から表示せず、ユーザーが自らタップして確認する仕様を採用。過去の良問を自分でも解いてみるという楽しみを残しています。
153+
- **没入感のあるフルスクリーン閲覧**:
154+
余計な UI を排除し、会話の流れに集中できるレイアウトを採用。チャットアプリを使っているかのような感覚でストーリーを読み進められます。
155+
156+
### 🎨 その他
157+
158+
えて答えを最初から表示せず、ユーザーが自らタップして確認する仕様を採用。過去の良問を自分でも解いてみるという楽しみを残しています。
159+
160+
- **没入感のあるフルスクリーン閲覧**:
161+
余計な UI を排除し、会話の流れに集中できるレイアウトを採用。チャットアプリを使っているかのような感覚でストーリーを読み進められます。
162+
163+
### 🎨 その他
164+
165+
- **モバイルファーストのレスポンシブデザイン**:
166+
スマートフォンでの操作をメインに想定しつつ、PCでも快適にプレイできる柔軟なレイアウトを実現しています。
167+
- **視覚的な導線設計**:
168+
「ゲームスタート」などの主要ボタンは目立つオレンジ色、サブボタンは白など、機能の重要度に応じたカラー設計を行っています。
169+
- **Material Designによる統一感**:
170+
一貫性のあるデザインシステム(MUI)を採用し、全体として清潔感とプレミアム感のある外観を実現しました。
82171

83172
---
84173

@@ -89,29 +178,32 @@
89178
今後の展望があればおしえてください。
90179
-->
91180

92-
対戦機能を付けた方がユーザ同士でも楽しめる?
181+
対戦機能を付けた方がユーザ同士でも楽しめる?
93182

94-
--------------------------------
183+
---
95184

185+
<!-- この下は元から記入されていたもの
186+
上の分は今年の評価基準らしいので、基本的には上の部分を修正した方がいいと思う
187+
-->
96188

97189
## 背景・課題・解決されること
98190

99191
<!-- テーマ「関西をいい感じに」に対して、考案するプロダクトがどういった(Why)背景から思いついたのか、どのよう(What)な課題があり、どのよう(How)に解決するのかを入力してください -->
100192

101-
102193
## プロダクト説明
103194

104195
<!-- 開発したプロダクトの説明を入力してください -->
105196

106-
107197
## 操作説明・デモ動画
198+
108199
[デモ動画はこちら](https://www.youtube.com/watch?v=fbzGp0XJGq8)
109-
<!-- 開発したプロダクトの操作説明について入力してください。また、操作説明デモ動画があれば、埋め込みやリンクを記載してください -->
110200

201+
<!-- 開発したプロダクトの操作説明について入力してください。また、操作説明デモ動画があれば、埋め込みやリンクを記載してください -->
111202

112203
## 注力したポイント
113204

114205
<!-- 開発したプロダクトの中で、特に注力して作成した箇所・ポイントについて入力してください -->
206+
115207
### アイデア面
116208

117209
### デザイン面
@@ -122,8 +214,10 @@
122214

123215
<!-- 使用技術を入力してください -->
124216

217+
### AWS構成図
218+
<img width="686" height="435" alt="image" src="https://github.com/user-attachments/assets/f7286ff6-ac04-488c-9a17-c0975677ab8e" />
125219

126220
<!--
127221
markdownの記法はこちらを参照してください!
128222
https://docs.github.com/ja/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax
129-
-->
223+
-->

backend/controllers/hint_controller.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ type IHintController interface {
1717
GetFinishedRoundByID(ctx *gin.Context)
1818
BookmarkRound(ctx *gin.Context)
1919
GetRandomBookmark(ctx *gin.Context)
20+
GetBookmarkedList(ctx *gin.Context)
2021
}
2122

2223
type HintController struct {
@@ -143,3 +144,13 @@ func (c *HintController) GetRandomBookmark(ctx *gin.Context) {
143144
}
144145
ctx.JSON(http.StatusOK, gin.H{"result": result})
145146
}
147+
148+
func (c *HintController) GetBookmarkedList(ctx *gin.Context) {
149+
result, err := c.service.GetBookmarkedList()
150+
if err != nil {
151+
_ = ctx.Error(err)
152+
ctx.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get bookmarked list"})
153+
return
154+
}
155+
ctx.JSON(http.StatusOK, gin.H{"results": result})
156+
}

backend/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,6 @@ func main() {
3434
r.GET("/solo/board/:id", hintController.GetFinishedRoundByID)
3535
r.POST("/solo/:id/bookmark", hintController.BookmarkRound)
3636
r.GET("/solo/bookmark/random", hintController.GetRandomBookmark)
37+
r.GET("/solo/bookmark/all", hintController.GetBookmarkedList)
3738
r.Run() // デフォルトで0.0.0.0:8080で待機します
3839
}

backend/repositories/hint_repository.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ type IHintRepository interface {
1414
FinishRound(id uint) error
1515
BookmarkRound(id uint) error
1616
GetRandomBookmarkedRound() (*models.Round, error)
17+
GetAllBookmarkedRounds() ([]models.Round, error)
1718
}
1819

1920
type HintRepository struct {
@@ -70,3 +71,11 @@ func (r *HintRepository) GetRandomBookmarkedRound() (*models.Round, error) {
7071
}
7172
return &rounds[rand.Intn(len(rounds))], nil
7273
}
74+
75+
func (r *HintRepository) GetAllBookmarkedRounds() ([]models.Round, error) {
76+
var rounds []models.Round
77+
if err := r.db.Where("is_bookmarked = ?", true).Find(&rounds).Error; err != nil {
78+
return nil, err
79+
}
80+
return rounds, nil
81+
}

backend/services/hint_service.go

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ type IHintService interface {
3131
GetFinishedRoundByID(id uint) (*dto.RoundResponse, error)
3232
BookmarkRound(id uint) (*dto.RoundResponse, error)
3333
GetRandomBookmark() (*dto.RoundResponse, error)
34+
GetBookmarkedList() ([]dto.RoundResponse, error)
3435
}
3536

3637
type HintService struct {
@@ -149,8 +150,11 @@ func (s *HintService) StartGame() (*dto.StartGameResult, error) {
149150
"answers": map[string]any{
150151
"type": "array",
151152
"items": map[string]any{
152-
"type": "string",
153-
"description": "正解のリスト。表記ゆれを考慮して複数入れる。漢字、ひらがな、カタカナ、英語(小文字)、および一般的な略称(例:自販機、スマホ)など、考えられえるものを全て含めること。",
153+
"type": "string",
154+
"description": `正解のリスト。
155+
- 一番最初に最も一般的な答えが入るようにしてください
156+
- 表記ゆれを考慮して複数入れる。漢字、ひらがな、カタカナ、英語(小文字)、
157+
- および一般的な略称(例:自販機、スマホ)など、考えられえるものを全て含めること。`,
154158
},
155159
},
156160
"hints": map[string]any{
@@ -298,3 +302,28 @@ func (s *HintService) GetRandomBookmark() (*dto.RoundResponse, error) {
298302
UpdatedAt: round.UpdatedAt,
299303
}, nil
300304
}
305+
306+
func (s *HintService) GetBookmarkedList() ([]dto.RoundResponse, error) {
307+
rounds, err := s.repository.GetAllBookmarkedRounds()
308+
if err != nil {
309+
return nil, err
310+
}
311+
312+
response := []dto.RoundResponse{}
313+
314+
for _, r := range rounds {
315+
limit := 4
316+
if len(r.Hints) < limit {
317+
limit = len(r.Hints)
318+
}
319+
displayHints := r.Hints[:limit]
320+
321+
response = append(response, dto.RoundResponse{
322+
ID: r.ID,
323+
Answer: r.Answers[0],
324+
Hints: displayHints,
325+
UpdatedAt: r.UpdatedAt,
326+
})
327+
}
328+
return response, nil
329+
}

frontend/index.html

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22
<html lang="en">
33
<head>
44
<meta charset="UTF-8" />
5-
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
5+
<link rel="icon" type="image/svg+xml" href="/chashibaki.png" />
66
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7-
<title>frontend</title>
7+
<title>あれちゃう?知らんけど。</title>
8+
<meta name="description" content="関西人がなんかをめっちゃ関西っぽく説明してくれるで!それが何か当ててみてや〜!ちょっと茶しばきにきてな!" />
89
</head>
910
<body>
1011
<div id="root"></div>
14.9 KB
Loading

frontend/public/chaShibaki.gif

638 KB
Loading

frontend/public/chashibaki.png

342 KB
Loading

frontend/public/vite.svg

Lines changed: 0 additions & 1 deletion
This file was deleted.

0 commit comments

Comments
 (0)