ChingCL commited on
Commit
253fa7c
·
verified ·
1 Parent(s): 61e3aa1

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +86 -125
app.py CHANGED
@@ -1,6 +1,6 @@
1
  import React, { useState, useEffect } from 'react';
2
 
3
- // 完整的成語資料庫(50組,之後擴充到200組)
4
  const idiomDatabase = [
5
  { mainIdiom: '急如星火', synonyms: ['迫在眉睫', '刻不容緩'], meaning: '形容事情非常緊急' },
6
  { mainIdiom: '一葉知秋', synonyms: ['見微知著', '月暈而風'], meaning: '從小徵兆可以預見未來的發展' },
@@ -12,190 +12,151 @@ const idiomDatabase = [
12
  { mainIdiom: '一擲千金', synonyms: ['揮金如土', '揮霍無度'], meaning: '形容揮霍錢財' },
13
  { mainIdiom: '生靈塗炭', synonyms: ['民不聊生', '民生凋敝'], meaning: '形容人民生活困苦' },
14
  { mainIdiom: '瓜熟蒂落', synonyms: ['水到渠成', '順理成章'], meaning: '形容事情自然成功' }
15
- ].map((item, index) => ({ ...item, id: index + 1 }));
16
 
17
- export default function ChineseIdiomGame() {
18
  const [currentQuestion, setCurrentQuestion] = useState(null);
19
  const [options, setOptions] = useState([]);
20
  const [selectedAnswers, setSelectedAnswers] = useState([]);
21
  const [score, setScore] = useState(0);
22
  const [message, setMessage] = useState('');
23
- const [errorMessage, setErrorMessage] = useState('');
24
  const [gameComplete, setGameComplete] = useState(false);
25
- const [incorrectOption, setIncorrectOption] = useState(null);
26
- const [availableQuestions, setAvailableQuestions] = useState([...idiomDatabase]);
27
 
 
28
  const shuffleArray = (array) => {
29
  return [...array].sort(() => Math.random() - 0.5);
30
  };
31
 
32
- const getRandomOptions = (currentQ, count) => {
33
- const otherOptions = idiomDatabase
34
- .filter(item => item.id !== currentQ.id)
35
- .flatMap(item => item.synonyms)
36
- .filter(opt => !currentQ.synonyms.includes(opt));
37
-
38
- return shuffleArray(otherOptions).slice(0, count);
39
- };
40
-
41
  const startNewQuestion = () => {
 
 
 
 
 
 
42
  if (availableQuestions.length === 0) {
43
- setAvailableQuestions([...idiomDatabase]);
 
44
  }
45
 
46
- const randomIndex = Math.floor(Math.random() * availableQuestions.length);
47
- const newQuestion = availableQuestions[randomIndex];
48
 
49
- setAvailableQuestions(prev => prev.filter(q => q.id !== newQuestion.id));
50
-
51
- const allOptions = [
52
- ...newQuestion.synonyms,
53
- ...getRandomOptions(newQuestion, 8)
54
- ].sort(() => Math.random() - 0.5);
 
 
55
 
56
  setCurrentQuestion(newQuestion);
57
  setOptions(allOptions);
58
  setSelectedAnswers([]);
59
  setMessage('');
60
- setErrorMessage('');
61
- };
62
-
63
- const initializeGame = () => {
64
- setScore(0);
65
- setGameComplete(false);
66
- setAvailableQuestions([...idiomDatabase]);
67
- startNewQuestion();
68
  };
69
 
 
70
  useEffect(() => {
71
- initializeGame();
72
  }, []);
73
 
 
74
  const handleOptionSelect = (option) => {
75
  if (selectedAnswers.includes(option)) return;
76
 
77
  if (currentQuestion.synonyms.includes(option)) {
78
  const newSelected = [...selectedAnswers, option];
79
  setSelectedAnswers(newSelected);
80
- setErrorMessage('');
81
 
82
- if (newSelected.length === 1) {
83
- setScore(prev => prev + 5);
84
- setMessage('答對了!還有一個答案');
85
- } else if (newSelected.length === 2) {
86
- const newScore = score + 5;
87
  setScore(newScore);
88
 
89
  if (newScore >= 100) {
90
  setGameComplete(true);
91
  } else {
92
- setMessage('太棒了!準備下一題...');
93
- setTimeout(startNewQuestion, 1500);
94
  }
95
  }
96
- } else {
97
- setIncorrectOption(option);
98
- setErrorMessage('答錯了,再試試看!');
99
- setTimeout(() => {
100
- setIncorrectOption(null);
101
- setErrorMessage('');
102
- }, 1000);
103
  }
104
  };
105
 
 
 
 
 
 
 
 
 
106
  if (gameComplete) {
107
  return (
108
- <div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center">
109
- <div className="bg-white rounded-lg p-8 max-w-xl w-full mx-4 flex flex-col items-center">
110
- <h2 className="text-3xl font-bold text-green-600 mb-12">
111
  太棒了!你是成語小天才!
112
  </h2>
113
- <div className="flex flex-col sm:flex-row gap-8 w-full max-w-md">
114
- <button
115
- onClick={initializeGame}
116
- className="bg-[#72BFA3] text-white text-xl font-bold px-8 py-4 rounded-lg hover:bg-green-600 transition-colors w-full"
117
- >
118
- 繼續挑戰
119
- </button>
120
- <button
121
- onClick={() => window.close()}
122
- className="bg-[#FFC54F] text-white text-xl font-bold px-8 py-4 rounded-lg hover:bg-yellow-600 transition-colors w-full"
123
- >
124
- 休息一下
125
- </button>
126
- </div>
127
  </div>
128
  </div>
129
  );
130
  }
131
 
132
  return (
133
- <div className="min-h-screen bg-[#F2F1EF] p-4 flex justify-center">
134
- <div className="w-full max-w-3xl">
135
- <div className="bg-white rounded-lg shadow-lg p-4 sm:p-8">
136
- <h1 className="text-2xl sm:text-3xl font-bold text-center mb-6">成語對對碰</h1>
137
-
138
- <div className="text-center mb-8">
139
- <div className="flex flex-wrap justify-center items-center gap-4 mb-3">
140
- <h2 className="text-xl sm:text-2xl font-bold text-blue-600">
141
- 題目:{currentQuestion?.mainIdiom}
142
  </h2>
143
- {selectedAnswers.map((idiom, index) => (
144
- <span key={index} className="text-xl sm:text-2xl font-bold text-green-600">
145
- = {idiom}
146
- </span>
147
- ))}
148
  </div>
149
- <p className="text-gray-600 mb-4">{currentQuestion?.meaning}</p>
150
- <p className="text-gray-700 bg-gray-100 p-4 rounded-lg">
151
- 玩法:這裡有兩個成語是和題目一樣的意思,請你把它找出來!
152
- </p>
153
- </div>
154
-
155
- {message && (
156
- <div className="text-center mb-6 text-green-600 font-bold">
157
- {message}
158
- </div>
159
- )}
160
 
161
- {errorMessage && (
162
- <div className="text-center mb-4 text-red-500 font-bold">
163
- {errorMessage}
 
 
 
 
 
 
 
 
 
 
 
 
164
  </div>
165
- )}
166
-
167
- <div className="grid grid-cols-2 md:grid-cols-5 gap-2 sm:gap-4 mb-6">
168
- {options.map((option, index) => (
169
- <button
170
- key={`${option}-${index}`}
171
- onClick={() => handleOptionSelect(option)}
172
- className={`
173
- p-2 sm:p-4 rounded-lg text-center transition-all
174
- ${selectedAnswers.includes(option) ? 'bg-[#72BFA3] text-white' : ''}
175
- ${incorrectOption === option ? 'bg-[#FFC54F]' : 'bg-white'}
176
- border-2 border-gray-200
177
- hover:bg-gray-50
178
- text-sm sm:text-base
179
- `}
180
- disabled={selectedAnswers.includes(option)}
181
- >
182
- {option}
183
- </button>
184
- ))}
185
- </div>
186
-
187
- <div className="mt-4 sm:mt-8">
188
- <div className="w-full bg-gray-200 rounded-full h-6">
189
- <div
190
- className="bg-green-500 h-full rounded-full transition-all duration-500 flex items-center justify-end pr-2"
191
  style={{ width: `${score}%` }}
192
- >
193
- <span className="text-white text-sm font-bold">{score}/100</span>
194
- </div>
195
  </div>
196
- </div>
197
- </div>
 
198
  </div>
199
  </div>
200
  );
201
- }
 
 
 
1
  import React, { useState, useEffect } from 'react';
2
 
3
+ // 成語資料庫
4
  const idiomDatabase = [
5
  { mainIdiom: '急如星火', synonyms: ['迫在眉睫', '刻不容緩'], meaning: '形容事情非常緊急' },
6
  { mainIdiom: '一葉知秋', synonyms: ['見微知著', '月暈而風'], meaning: '從小徵兆可以預見未來的發展' },
 
12
  { mainIdiom: '一擲千金', synonyms: ['揮金如土', '揮霍無度'], meaning: '形容揮霍錢財' },
13
  { mainIdiom: '生靈塗炭', synonyms: ['民不聊生', '民生凋敝'], meaning: '形容人民生活困苦' },
14
  { mainIdiom: '瓜熟蒂落', synonyms: ['水到渠成', '順理成章'], meaning: '形容事情自然成功' }
15
+ ];
16
 
17
+ function ChineseIdiomGame() {
18
  const [currentQuestion, setCurrentQuestion] = useState(null);
19
  const [options, setOptions] = useState([]);
20
  const [selectedAnswers, setSelectedAnswers] = useState([]);
21
  const [score, setScore] = useState(0);
22
  const [message, setMessage] = useState('');
 
23
  const [gameComplete, setGameComplete] = useState(false);
24
+ const [usedQuestions, setUsedQuestions] = useState([]);
 
25
 
26
+ // 打亂陣列順序的函數
27
  const shuffleArray = (array) => {
28
  return [...array].sort(() => Math.random() - 0.5);
29
  };
30
 
31
+ // 開始新的題目
 
 
 
 
 
 
 
 
32
  const startNewQuestion = () => {
33
+ // 過濾掉已使用的題目
34
+ const availableQuestions = idiomDatabase.filter(
35
+ q => !usedQuestions.includes(q.mainIdiom)
36
+ );
37
+
38
+ // 如果所有題目都用完了,重置使用記錄
39
  if (availableQuestions.length === 0) {
40
+ setUsedQuestions([]);
41
+ return startNewQuestion();
42
  }
43
 
44
+ // 隨機選擇一個新題目
45
+ const newQuestion = availableQuestions[Math.floor(Math.random() * availableQuestions.length)];
46
 
47
+ // 生成選項
48
+ const otherOptions = idiomDatabase
49
+ .filter(item => item.mainIdiom !== newQuestion.mainIdiom)
50
+ .flatMap(item => item.synonyms)
51
+ .filter(opt => !newQuestion.synonyms.includes(opt));
52
+
53
+ const shuffledOtherOptions = shuffleArray(otherOptions).slice(0, 8);
54
+ const allOptions = shuffleArray([...newQuestion.synonyms, ...shuffledOtherOptions]);
55
 
56
  setCurrentQuestion(newQuestion);
57
  setOptions(allOptions);
58
  setSelectedAnswers([]);
59
  setMessage('');
60
+ setUsedQuestions([...usedQuestions, newQuestion.mainIdiom]);
 
 
 
 
 
 
 
61
  };
62
 
63
+ // 初始化遊戲
64
  useEffect(() => {
65
+ startNewQuestion();
66
  }, []);
67
 
68
+ // 處理選項點擊
69
  const handleOptionSelect = (option) => {
70
  if (selectedAnswers.includes(option)) return;
71
 
72
  if (currentQuestion.synonyms.includes(option)) {
73
  const newSelected = [...selectedAnswers, option];
74
  setSelectedAnswers(newSelected);
 
75
 
76
+ if (newSelected.length === 2) {
77
+ const newScore = score + 10;
 
 
 
78
  setScore(newScore);
79
 
80
  if (newScore >= 100) {
81
  setGameComplete(true);
82
  } else {
83
+ setTimeout(startNewQuestion, 1000);
 
84
  }
85
  }
 
 
 
 
 
 
 
86
  }
87
  };
88
 
89
+ // 重新開始遊戲
90
+ const restartGame = () => {
91
+ setScore(0);
92
+ setGameComplete(false);
93
+ setUsedQuestions([]);
94
+ startNewQuestion();
95
+ };
96
+
97
  if (gameComplete) {
98
  return (
99
+ <div className="min-h-screen bg-gray-100 flex items-center justify-center p-4">
100
+ <div className="bg-white rounded-lg p-8 text-center max-w-md w-full">
101
+ <h2 className="text-2xl font-bold text-green-600 mb-6">
102
  太棒了!你是成語小天才!
103
  </h2>
104
+ <button
105
+ onClick={restartGame}
106
+ className="bg-green-500 text-white px-6 py-2 rounded-lg hover:bg-green-600"
107
+ >
108
+ 再玩一次
109
+ </button>
 
 
 
 
 
 
 
 
110
  </div>
111
  </div>
112
  );
113
  }
114
 
115
  return (
116
+ <div className="min-h-screen bg-gray-100 p-4">
117
+ <div className="max-w-2xl mx-auto bg-white rounded-lg shadow p-6">
118
+ <h1 className="text-2xl font-bold text-center mb-6">成語對對碰</h1>
119
+
120
+ {currentQuestion && (
121
+ <>
122
+ <div className="bg-blue-100 p-4 rounded-lg mb-6">
123
+ <h2 className="text-xl font-bold text-center mb-2">
124
+ {currentQuestion.mainIdiom}
125
  </h2>
126
+ <p className="text-gray-600 text-center">
127
+ {currentQuestion.meaning}
128
+ </p>
 
 
129
  </div>
 
 
 
 
 
 
 
 
 
 
 
130
 
131
+ <div className="grid grid-cols-2 sm:grid-cols-3 gap-4 mb-6">
132
+ {options.map((option, index) => (
133
+ <button
134
+ key={index}
135
+ onClick={() => handleOptionSelect(option)}
136
+ className={`
137
+ p-3 rounded-lg text-center
138
+ ${selectedAnswers.includes(option) ? 'bg-green-500 text-white' : 'bg-gray-100'}
139
+ hover:bg-gray-200
140
+ `}
141
+ disabled={selectedAnswers.includes(option)}
142
+ >
143
+ {option}
144
+ </button>
145
+ ))}
146
  </div>
147
+
148
+ <div className="bg-gray-200 rounded-full h-4 mb-4">
149
+ <div
150
+ className="bg-green-500 h-full rounded-full transition-all duration-500"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151
  style={{ width: `${score}%` }}
152
+ />
 
 
153
  </div>
154
+ <p className="text-center text-gray-600">分數:{score}/100</p>
155
+ </>
156
+ )}
157
  </div>
158
  </div>
159
  );
160
+ }
161
+
162
+ export default ChineseIdiomGame;