LukasBe commited on
Commit
febd64a
·
verified ·
1 Parent(s): 0429a15

Add 3 files

Browse files
Files changed (3) hide show
  1. README.md +7 -5
  2. index.html +315 -19
  3. prompts.txt +1 -0
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Tower Text Twist
3
- emoji: 👁
4
- colorFrom: gray
5
- colorTo: blue
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: tower-text-twist
3
+ emoji: 🐳
4
+ colorFrom: pink
5
+ colorTo: pink
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,315 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Tower Text Twist</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
+ <style>
10
+ .letter-tile {
11
+ transition: all 0.2s ease;
12
+ }
13
+ .letter-tile.selected {
14
+ transform: translateY(-5px);
15
+ box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
16
+ }
17
+ .feedback-message {
18
+ animation: fadeOut 1.5s ease-out 1.5s forwards;
19
+ }
20
+ @keyframes fadeOut {
21
+ to { opacity: 0; }
22
+ }
23
+ .timer-pulse {
24
+ animation: pulse 1s infinite;
25
+ }
26
+ @keyframes pulse {
27
+ 0%, 100% { transform: scale(1); }
28
+ 50% { transform: scale(1.05); }
29
+ }
30
+ </style>
31
+ </head>
32
+ <body class="bg-gray-100 min-h-screen flex items-center justify-center p-4">
33
+ <div class="max-w-md w-full bg-white rounded-2xl shadow-xl overflow-hidden">
34
+ <!-- Game Header -->
35
+ <div class="bg-indigo-600 p-4 text-white">
36
+ <h1 class="text-2xl font-bold text-center">Tower Text Twist</h1>
37
+ <p class="text-center text-indigo-100">Make words to build your tower!</p>
38
+ </div>
39
+
40
+ <!-- Game Info -->
41
+ <div class="p-4 bg-indigo-50 flex justify-between items-center">
42
+ <div class="text-center">
43
+ <p class="text-xs text-indigo-600 font-semibold">SCORE</p>
44
+ <p id="score" class="text-2xl font-bold text-indigo-800">0</p>
45
+ </div>
46
+ <div class="text-center">
47
+ <p class="text-xs text-indigo-600 font-semibold">HEIGHT</p>
48
+ <p id="height" class="text-2xl font-bold text-indigo-800">0</p>
49
+ </div>
50
+ <div class="text-center">
51
+ <p class="text-xs text-indigo-600 font-semibold">TIME</p>
52
+ <p id="timer" class="text-2xl font-bold text-red-600 timer-pulse">60</p>
53
+ </div>
54
+ </div>
55
+
56
+ <!-- Feedback Message -->
57
+ <div id="feedback" class="h-10 flex items-center justify-center">
58
+ <p id="feedback-message" class="text-lg font-semibold opacity-0"></p>
59
+ </div>
60
+
61
+ <!-- Letter Tiles -->
62
+ <div id="letter-area" class="p-4 grid grid-cols-6 gap-2">
63
+ <!-- Letter tiles will be generated here -->
64
+ </div>
65
+
66
+ <!-- Current Word -->
67
+ <div class="px-4 py-2 bg-gray-100">
68
+ <div class="bg-white rounded-lg p-3 shadow-inner">
69
+ <p class="text-xs text-gray-500">Current Word:</p>
70
+ <p id="current-word" class="text-2xl font-mono text-center min-h-8">-</p>
71
+ </div>
72
+ </div>
73
+
74
+ <!-- Controls -->
75
+ <div class="p-4 flex gap-2">
76
+ <button id="submit-btn" class="flex-1 bg-green-600 hover:bg-green-700 text-white font-bold py-3 px-4 rounded-lg transition disabled:opacity-50 disabled:cursor-not-allowed" disabled>
77
+ <i class="fas fa-check mr-2"></i> Submit
78
+ </button>
79
+ <button id="clear-btn" class="flex-1 bg-gray-200 hover:bg-gray-300 text-gray-800 font-bold py-3 px-4 rounded-lg transition">
80
+ <i class="fas fa-eraser mr-2"></i> Clear
81
+ </button>
82
+ </div>
83
+
84
+ <!-- Found Words -->
85
+ <div class="p-4 bg-gray-50 border-t">
86
+ <p class="text-xs text-gray-500 mb-1">Found Words (<span id="found-count">0</span>):</p>
87
+ <div id="found-words" class="flex flex-wrap gap-1">
88
+ <!-- Found words will appear here -->
89
+ </div>
90
+ </div>
91
+
92
+ <!-- Start Button -->
93
+ <div class="p-4 bg-white">
94
+ <button id="start-btn" class="w-full bg-indigo-600 hover:bg-indigo-700 text-white font-bold py-3 px-4 rounded-lg transition">
95
+ <i class="fas fa-play mr-2"></i> Start Game
96
+ </button>
97
+ </div>
98
+ </div>
99
+
100
+ <script>
101
+ // Game configuration
102
+ const config = {
103
+ roundTime: 60,
104
+ availableLetters: ['A', 'E', 'S', 'T', 'R', 'N'],
105
+ validWords: [
106
+ "ART", "EAT", "NET", "RAT", "RENT", "STAR", "START", "TAN", "TEA", "TEN",
107
+ "NEST", "RATE", "REST", "SAT", "SEA", "SENT", "SET", "TERN", "EARN", "EAST",
108
+ "EATS", "NEAT", "RANT", "SEAT", "STAR", "TEAR", "TENS", "ANTS", "ARTS", "ERAS",
109
+ "NATS", "NEAR", "NEST", "RATS", "SANE", "TARE", "TARN", "TARS", "TEAS", "TENS"
110
+ ]
111
+ };
112
+
113
+ // Game state
114
+ const state = {
115
+ gameActive: false,
116
+ timer: config.roundTime,
117
+ score: 0,
118
+ height: 0,
119
+ selectedLetters: [],
120
+ foundWords: [],
121
+ timerInterval: null
122
+ };
123
+
124
+ // DOM elements
125
+ const elements = {
126
+ letterArea: document.getElementById('letter-area'),
127
+ currentWord: document.getElementById('current-word'),
128
+ score: document.getElementById('score'),
129
+ height: document.getElementById('height'),
130
+ timer: document.getElementById('timer'),
131
+ feedback: document.getElementById('feedback-message'),
132
+ submitBtn: document.getElementById('submit-btn'),
133
+ clearBtn: document.getElementById('clear-btn'),
134
+ startBtn: document.getElementById('start-btn'),
135
+ foundWords: document.getElementById('found-words'),
136
+ foundCount: document.getElementById('found-count')
137
+ };
138
+
139
+ // Initialize the game
140
+ function initGame() {
141
+ // Create letter tiles
142
+ elements.letterArea.innerHTML = '';
143
+ config.availableLetters.forEach(letter => {
144
+ const tile = document.createElement('div');
145
+ tile.className = 'letter-tile bg-white rounded-lg shadow-md flex items-center justify-center text-2xl font-bold cursor-pointer h-12';
146
+ tile.textContent = letter;
147
+ tile.dataset.letter = letter;
148
+ tile.addEventListener('click', () => toggleLetter(letter, tile));
149
+ elements.letterArea.appendChild(tile);
150
+ });
151
+
152
+ // Reset game state
153
+ state.gameActive = false;
154
+ state.timer = config.roundTime;
155
+ state.score = 0;
156
+ state.height = 0;
157
+ state.selectedLetters = [];
158
+ state.foundWords = [];
159
+
160
+ // Update UI
161
+ updateUI();
162
+ }
163
+
164
+ // Start a new round
165
+ function startRound() {
166
+ if (state.timerInterval) clearInterval(state.timerInterval);
167
+
168
+ initGame();
169
+ state.gameActive = true;
170
+
171
+ // Start timer
172
+ state.timerInterval = setInterval(() => {
173
+ state.timer--;
174
+ updateUI();
175
+
176
+ if (state.timer <= 0) {
177
+ endRound();
178
+ }
179
+ }, 1000);
180
+
181
+ showFeedback("Make words with the letters!", 2000);
182
+ }
183
+
184
+ // End the current round
185
+ function endRound() {
186
+ state.gameActive = false;
187
+ clearInterval(state.timerInterval);
188
+ showFeedback(`Round over! Score: ${state.score}`, 3000);
189
+ }
190
+
191
+ // Toggle letter selection
192
+ function toggleLetter(letter, tile) {
193
+ if (!state.gameActive) return;
194
+
195
+ const index = state.selectedLetters.indexOf(letter);
196
+
197
+ if (index === -1) {
198
+ // Select the letter
199
+ state.selectedLetters.push(letter);
200
+ tile.classList.add('selected', 'bg-indigo-100', 'text-indigo-800');
201
+ } else {
202
+ // Deselect the letter (remove last occurrence)
203
+ const lastIndex = state.selectedLetters.lastIndexOf(letter);
204
+ if (lastIndex !== -1) {
205
+ state.selectedLetters.splice(lastIndex, 1);
206
+ }
207
+ tile.classList.remove('selected', 'bg-indigo-100', 'text-indigo-800');
208
+ }
209
+
210
+ updateUI();
211
+ }
212
+
213
+ // Clear selected letters
214
+ function clearSelection() {
215
+ if (!state.gameActive) return;
216
+
217
+ state.selectedLetters = [];
218
+ document.querySelectorAll('.letter-tile').forEach(tile => {
219
+ tile.classList.remove('selected', 'bg-indigo-100', 'text-indigo-800');
220
+ });
221
+
222
+ updateUI();
223
+ }
224
+
225
+ // Submit current word
226
+ function submitWord() {
227
+ if (!state.gameActive || state.selectedLetters.length < 3) {
228
+ showFeedback(state.selectedLetters.length < 3 ? "Word too short!" : "Game not active", 1500);
229
+ return;
230
+ }
231
+
232
+ const word = state.selectedLetters.join('');
233
+
234
+ if (state.foundWords.includes(word)) {
235
+ showFeedback("Already found!", 1500);
236
+ } else if (config.validWords.includes(word)) {
237
+ // Valid word
238
+ state.foundWords.push(word);
239
+ state.score += word.length * 10;
240
+ state.height += word.length;
241
+
242
+ // Add to found words display
243
+ const wordBadge = document.createElement('span');
244
+ wordBadge.className = 'bg-green-100 text-green-800 text-xs font-medium px-2.5 py-0.5 rounded';
245
+ wordBadge.textContent = word;
246
+ elements.foundWords.appendChild(wordBadge);
247
+
248
+ showFeedback(`+${word.length * 10} points!`, 1500);
249
+ } else {
250
+ showFeedback("Invalid word!", 1500);
251
+ }
252
+
253
+ clearSelection();
254
+ updateUI();
255
+ }
256
+
257
+ // Show feedback message
258
+ function showFeedback(message, duration) {
259
+ elements.feedback.textContent = message;
260
+ elements.feedback.className = 'text-lg font-semibold';
261
+
262
+ // Set color based on message type
263
+ if (message.includes("Invalid") || message.includes("Already") || message.includes("short")) {
264
+ elements.feedback.classList.add('text-red-500');
265
+ } else if (message.includes("points")) {
266
+ elements.feedback.classList.add('text-green-500');
267
+ } else {
268
+ elements.feedback.classList.add('text-indigo-500');
269
+ }
270
+
271
+ // Reset animation
272
+ elements.feedback.style.animation = 'none';
273
+ elements.feedback.offsetHeight; // Trigger reflow
274
+ elements.feedback.style.animation = null;
275
+
276
+ // Fade out
277
+ setTimeout(() => {
278
+ elements.feedback.classList.add('opacity-0');
279
+ }, duration);
280
+ }
281
+
282
+ // Update UI elements
283
+ function updateUI() {
284
+ // Update current word display
285
+ elements.currentWord.textContent = state.selectedLetters.length > 0
286
+ ? state.selectedLetters.join('')
287
+ : '-';
288
+
289
+ // Update score and height
290
+ elements.score.textContent = state.score;
291
+ elements.height.textContent = state.height;
292
+ elements.timer.textContent = state.timer;
293
+ elements.foundCount.textContent = state.foundWords.length;
294
+
295
+ // Update timer color when low
296
+ if (state.timer <= 10) {
297
+ elements.timer.classList.add('text-red-600', 'timer-pulse');
298
+ } else {
299
+ elements.timer.classList.remove('text-red-600', 'timer-pulse');
300
+ }
301
+
302
+ // Enable/disable submit button
303
+ elements.submitBtn.disabled = !state.gameActive || state.selectedLetters.length < 3;
304
+ }
305
+
306
+ // Event listeners
307
+ elements.startBtn.addEventListener('click', startRound);
308
+ elements.clearBtn.addEventListener('click', clearSelection);
309
+ elements.submitBtn.addEventListener('click', submitWord);
310
+
311
+ // Initialize the game on load
312
+ initGame();
313
+ </script>
314
+ <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=LukasBe/tower-text-twist" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
315
+ </html>
prompts.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ Okay, here is an implementation-grade detailed plan for an interactive demonstrator focusing *only* on the **core game mechanics** of **Tower Text Twist**. The goal is to create a minimal, functional prototype suitable for gathering user feedback on the core loop's feel and clarity, using a limited codebase (e.g., achievable by 1 developer in a short timeframe). **I. Demonstrator Overview & Goal** * **Purpose:** Validate the core word-finding loop: presenting letters, user input, validation, scoring, height point generation, and timer pressure. * **Target Experience:** Allow a user to play one or more timed rounds, understand how to form words, see immediate feedback on validity, and observe how score and height points accumulate. * **Scope Limitations (Crucial for Limited Codebase):** * **NO Meta Game:** No visual tower, no milestones, no themes, no long-term progression saved. * **NO Idle Element:** No passive point generation. * **NO Complex Letter Generation:** Use a fixed, predefined set of letters for all rounds of the demo (e.g., 'A', 'E', 'S', 'T', 'R', 'N'). * **NO Advanced UI/Polish:** Basic, functional UI only. Minimal animations or effects. * **NO Sound (Optional):** Sound effects are secondary; can be added if time permits but are not core to the mechanic validation. * **NO Monetization Hooks:** No ads, no IAP prompts. * **Simplified Dictionary:** Use a relatively small, curated word list focusing on common English words of 3-6 letters derivable from the chosen letter set. * **Platform:** PC Build or WebGL build (preferred for easy distribution for feedback). **II. Technology Stack** * **Engine:** Unity (Recommended for ease of UI, component structure, and potential future expansion). * **Language:** C# * **IDE:** Visual Studio / VS Code / Rider **III. Key Components & Scene Structure (Unity)** 1. **Scene:** `CoreMechanicsDemo` 2. **Main Camera:** Standard setup. 3. **Canvas (UI Root):** * **Panel\_LetterArea:** Holds the letter tiles. * **LetterTile (Prefab):** Represents one available letter. Contains: * `Image` (background) * `Text` (displaying the letter) * `Button` (for interaction) * `LetterTile.cs` (Script) * **Panel\_InputDisplay:** Shows the currently selected word. * `Text_CurrentWord`: Displays the sequence of selected letters. * **Button\_SubmitWord:** Button to submit the selected word. * **Panel\_GameInfo:** Displays scores and timer. * `Text_Timer`: Shows remaining time. * `Text_Score`: Shows current round score. * `Text_HeightPoints`: Shows current round height points earned. * `Text_FoundWordsCount`: (Optional) Shows how many words found this round. * **Panel\_Feedback:** Displays messages to the user. * `Text_FeedbackMessage`: Shows "Valid Word!", "Invalid Word", "Already Found", "Time's Up!". Fades out after a short duration. * **Panel\_Control:** Holds start/reset buttons. * `Button_StartRound`: Initiates a new round. * `Text_Instructions`: (Optional) Simple text: "Find words using the letters above. Click letters to select, click Submit." 4. **GameManager (Empty GameObject):** * `GameManager.cs` (Script - Singleton potentially useful) * `WordValidator.cs` (Script - Can be separate or part of GameManager) **IV. Core Scripting Details** **1. `GameManager.cs`** ```csharp using UnityEngine; using UnityEngine.UI; using System.Collections; using System.Collections.Generic; // For Lists public class GameManager : MonoBehaviour { // --- Inspector References --- [Header("UI Elements")] [SerializeField] private Text timerText; [SerializeField] private Text scoreText; [SerializeField] private Text heightPointsText; [SerializeField] private Text currentWordText; // Reference to InputDisplay's text [SerializeField] private Text feedbackText; [SerializeField] private Button startRoundButton; [SerializeField] private Button submitWordButton; [SerializeField] private GameObject letterTilePrefab; [SerializeField] private Transform letterAreaPanel; // Parent for letter tiles [Header("Game Settings")] [SerializeField] private float roundTimeSeconds = 60f; [SerializeField] private char[] availableLetters = {'A', 'E', 'S', 'T', 'R', 'N'}; // Fixed for demo // --- Game State --- private enum GameState { Idle, Playing, RoundOver } private GameState currentState = GameState.Idle; private float currentTimer; private int currentScore; private int currentHeightPoints; private List<string> foundWordsThisRound = new List<string>(); private List<LetterTile> currentLetterTiles = new List<LetterTile>(); private string selectedWord = ""; // --- Dependencies --- private WordValidator wordValidator; void Start() { wordValidator = GetComponent<WordValidator>(); // Assumes WordValidator is on the same GameObject if (wordValidator == null) { Debug.LogError("WordValidator component not found on GameManager!"); return; } wordValidator.LoadDictionary(); // Load words on start // Initial UI State startRoundButton.onClick.AddListener(StartRound); submitWordButton.onClick.AddListener(TrySubmitWord); submitWordButton.interactable = false; feedbackText.text = ""; UpdateUI(); } void Update() { if (currentState == GameState.Playing) { currentTimer -= Time.deltaTime; if (currentTimer <= 0f) { EndRound(); } UpdateUI(); // Update timer display constantly } } void StartRound() { currentState = GameState.Playing; currentTimer = roundTimeSeconds; currentScore = 0; currentHeightPoints = 0; foundWordsThisRound.Clear(); selectedWord = ""; // Clear previous tiles and create new ones foreach (Transform child in letterAreaPanel) { Destroy(child.gameObject); } currentLetterTiles.Clear(); foreach (char letter in availableLetters) { GameObject tileGO = Instantiate(letterTilePrefab, letterAreaPanel); LetterTile tile = tileGO.GetComponent<LetterTile>(); tile.Setup(letter, this); currentLetterTiles.Add(tile); } startRoundButton.interactable = false; submitWordButton.interactable = true; // Enable submit when playing ShowFeedback("", 0f); // Clear feedback UpdateUI(); } void EndRound() { currentState = GameState.RoundOver; currentTimer = 0f; startRoundButton.interactable = true; submitWordButton.interactable = false; DeselectAllTiles(); ShowFeedback("Time's Up!", 3.0f); // Show feedback longer UpdateUI(); // Final UI update } public void LetterTileClicked(LetterTile tile) { if (currentState != GameState.Playing) return; if (tile.IsSelected) { // Deselect - Remove last occurrence of the letter int index = selectedWord.LastIndexOf(tile.Letter); if(index != -1) { // Should always find if selected, but check anyway selectedWord = selectedWord.Remove(index, 1); } tile.SetSelected(false); } else { // Select - Add letter selectedWord += tile.Letter; tile.SetSelected(true); } UpdateUI(); } private void DeselectAllTiles() { selectedWord = ""; foreach(LetterTile tile in currentLetterTiles) { tile.SetSelected(false); } UpdateUI(); } void TrySubmitWord() { if (currentState != GameState.Playing || selectedWord.Length < 3) // Min word length 3 { ShowFeedback(selectedWord.Length < 3 ? "Word too short!" : "Invalid action", 1.5f); DeselectAllTiles(); return; // Don't submit if too short or not playing } string wordToCheck = selectedWord; // Keep a copy before clearing if (foundWordsThisRound.Contains(wordToCheck)) { ShowFeedback("Already Found!", 1.5f); } else if (wordValidator.IsWordValid(wordToCheck, availableLetters)) { foundWordsThisRound.Add(wordToCheck); currentScore += CalculateScore(wordToCheck); currentHeightPoints += wordToCheck.Length; // 1 point per letter ShowFeedback("Valid Word!", 1.5f); } else { ShowFeedback("Invalid Word!", 1.5f); } DeselectAllTiles(); // Clear selection after every submission attempt UpdateUI(); // Update score/height points display } int CalculateScore(string word) { // Simple scoring: Length * 10 (e.g.) return word.Length * 10; } void UpdateUI() { timerText.text = $"Time: {Mathf.CeilToInt(currentTimer)}"; scoreText.text = $"Score: {currentScore}"; heightPointsText.text = $"Height: {currentHeightPoints}"; currentWordText.text = selectedWord; // Display selected letters } void ShowFeedback(string message, float duration) { StopCoroutine("FadeFeedback"); // Stop previous fade if any feedbackText.text = message; if (duration > 0) { StartCoroutine(FadeFeedback(duration)); } } IEnumerator FadeFeedback(float duration) { yield return new WaitForSeconds(duration); feedbackText.text = ""; } } ``` **2. `LetterTile.cs` (Attach to LetterTile Prefab)** ```csharp using UnityEngine; using UnityEngine.UI; public class LetterTile : MonoBehaviour { [SerializeField] private Text letterText; [SerializeField] private Image backgroundImage; // To change color on select [SerializeField] private Color baseColor = Color.white; [SerializeField] private Color selectedColor = Color.cyan; private Button button; private GameManager gameManager; private char myLetter; private bool isSelected = false; public char Letter => myLetter; public bool IsSelected => isSelected; void Awake() { button = GetComponent<Button>(); button.onClick.AddListener(OnTileClicked); } public void Setup(char letter, GameManager manager) { myLetter = letter; letterText.text = myLetter.ToString(); gameManager = manager; SetSelected(false); // Ensure default state } void OnTileClicked() { if (gameManager != null) { gameManager.LetterTileClicked(this); } } public void SetSelected(bool selected) { isSelected = selected; backgroundImage.color = isSelected ? selectedColor : baseColor; // Optional: Add visual feedback like slight scale change } } ``` **3. `WordValidator.cs` (Attach to GameManager GameObject)** ```csharp using UnityEngine; using System.Collections.Generic; // For HashSet using System.Linq; // For Linq operations like Count() public class WordValidator : MonoBehaviour { [SerializeField] private TextAsset dictionaryFile; // Assign your simple TXT file in Inspector private HashSet<string> validWords = new HashSet<string>(); public void LoadDictionary() { if (dictionaryFile == null) { Debug.LogError("Dictionary file not assigned!"); return; } string[] words = dictionaryFile.text.Split(new[] { "\r\n", "\n" }, System.StringSplitOptions.RemoveEmptyEntries); foreach (string word in words) { validWords.Add(word.ToUpper()); // Store in uppercase for case-insensitive comparison } Debug.Log($"Loaded {validWords.Count} words into dictionary."); } public bool IsWordValid(string word, char[] availableLetters) { string upperWord = word.ToUpper(); // Compare in uppercase // 1. Check if it's in the dictionary if (!validWords.Contains(upperWord)) { // Debug.Log($"Word '{upperWord}' not in dictionary."); return false; } // 2. Check if the word can be formed from available letters (simple check for demo) // This check ensures you don't use a letter more times than it's available var availableLetterCounts = availableLetters.GroupBy(c => c).ToDictionary(g => g.Key, g => g.Count()); var wordLetterCounts = upperWord.GroupBy(c => c).ToDictionary(g => g.Key, g => g.Count()); foreach (var kvp in wordLetterCounts) { char letter = kvp.Key; int countNeeded = kvp.Value; if (!availableLetterCounts.ContainsKey(letter) || availableLetterCounts[letter] < countNeeded) { // Debug.Log($"Word '{upperWord}' needs {countNeeded} of '{letter}', only { (availableLetterCounts.ContainsKey(letter) ? availableLetterCounts[letter] : 0) } available."); return false; // Letter not available or not enough instances } } // Debug.Log($"Word '{upperWord}' is valid."); return true; } } ``` **V. Data & Assets** * **Dictionary File (`dictionary.txt`):** Create a simple text file. Place it in a `Resources` folder within your Unity `Assets`. Populate it with valid English words (UPPERCASE recommended for simpler C# logic, 3-6 letters long) that can be formed *only* using your chosen `availableLetters` (e.g., "ART", "EAT", "NET", "RAT", "RENT", "STAR", "START", "TAN", "TEA", "TEN", "NEST", "RATE", "REST", "SAT", "SEA", "SENT", "SET", "TAN", "TEA", "TEN", "TERN", "EARN", "EAST", "EATS", "RENT", "REST", "RATE"). * **LetterTile Prefab:** Create a prefab from a UI Button, customize its Image and Text components as needed. Attach `LetterTile.cs`. **VI. Scene Setup Steps** 1. Create the UI elements as described in Section III. Anchor them appropriately. 2. Assign UI Text and Button components to the corresponding `[SerializeField]` fields in the `GameManager` script in the Inspector. 3. Assign the `dictionary.txt` file to the `dictionaryFile` field on the `WordValidator` component (which is on the GameManager object). 4. Assign the LetterTile prefab to the `letterTilePrefab` field in `GameManager`. 5. Assign the `Panel_LetterArea` Transform to the `letterAreaPanel` field in `GameManager`. 6. Ensure `WordValidator.cs` is attached to the same GameObject as `GameManager.cs`. **VII. User Feedback Goals for Demonstrator** * **Clarity:** Is it immediately clear how to select letters and form words? Is the Submit action obvious? * **Feedback:** Is the feedback for valid/invalid/found words clear and timely? * **Timer:** Does the timer create a sense of urgency? Is the duration appropriate for finding a few words? * **Scoring/Height:** Is it clear how score and height points are awarded? Does seeing the height points increment feel rewarding, even without the visual tower? * **Input Feel:** Does tapping the letters feel responsive? Is selecting/deselecting intuitive? * **Frustration Points:** Was anything confusing or annoying during the core loop? (e.g., difficulty deselecting, unclear validation rules). This detailed plan provides a functional core loop demonstrator. It intentionally omits many features of the full game to keep the codebase limited and focused on validating the fundamental player interaction before investing in the more complex meta-game systems.