สร้างเกมและเรียนรู้วิทยาการคอมพิวเตอร์
โครงสร้างข้อมูลเป็นรากฐานของการพัฒนาซอฟต์แวร์ที่มีประสิทธิภาพ แต่อย่างไรก็ตาม วิธีการเรียนรู้แบบดั้งเดิมมักทำให้โครงสร้างข้อมูลดูเป็นนามธรรมและไม่เกี่ยวข้องกับการใช้งานจริง บทความนี้ใช้แนวทางที่แตกต่างออกไป โดยผสมผสานทฤษฎีเข้ากับการลงมือปฏิบัติจริงใน Flutter เพื่อให้การเรียนรู้ทั้ง น่าสนใจและใช้งานได้จริง
การปรับแต่งนี้ได้รับแรงบันดาลใจจาก Applied CS with Android ของ Google สำหรับ Flutter โดยนำเสนอวิธีการแบบโต้ตอบเพื่อทำความเข้าใจ อาร์เรย์ ชุดแฮช และแผนที่แฮช ในเวลาเพียง 3-4 ชั่วโมง คุณจะได้รับความเข้าใจที่ลึกซึ้งยิ่งขึ้นเกี่ยวกับโครงสร้างข้อมูลพื้นฐานเหล่านี้ในขณะที่นำไปใช้ในบริบทที่มีความหมาย
ไม่ว่าคุณจะเป็นมือใหม่ที่ต้องการเสริมพื้นฐาน CS หรือเป็นนักพัฒนาที่มีประสบการณ์ที่ต้องการพัฒนาทักษะ คู่มือนี้เป็นวิธีที่มีประสิทธิภาพและสนุกสนานในการฝึกฝนโครงสร้างข้อมูลที่จำเป็น มาเริ่มกันเลย
เราจะใช้โครงสร้างข้อมูลบางส่วนในการทำกิจกรรมเวิร์กช็อป ดังนั้นโปรดตรวจสอบ รายการ HashSet และ HashMaps คุณควรสามารถแทรก ลบ เข้าถึง และตรวจสอบการมีอยู่ขององค์ประกอบต่างๆ ได้อย่างมั่นใจโดยใช้โครงสร้างข้อมูลเหล่านี้ใน Dart
นี่คือคำแนะนำสั้นๆ เกี่ยวกับโครงสร้างข้อมูล HashSet และ HashMap
ตัวอย่างกิจกรรมที่ใช้ HashMaps ให้สร้างโปรแกรม (ไม่จำเป็นต้องเป็นแอป Flutter — ใช้บรรทัดคำสั่งได้) ที่จะรับรหัสประเทศสามตัวอักษร (ดู ISO-3166 ) และส่งคืนชื่อนามสกุลของประเทศที่โปรแกรมนั้นสังกัดอยู่
ตัวอย่างเช่น:
Input | Output ----- | ---------------------------------------------------- GBR | United Kingdom of Great Britain and Northern Ireland IDN | Indonesia IND | India
หากข้อมูลอินพุตมีความยาวมากกว่า 3 ตัวอักษร ให้ถือว่าเป็นชื่อประเทศ และส่งคืนรหัส 3 ตัวอักษรสำหรับข้อมูลนั้น เขียนข้อความแสดงข้อผิดพลาดที่เป็นประโยชน์หากข้อมูลอินพุตไม่ใช่รหัสที่ถูกต้องหรือชื่อประเทศ
มาเริ่มกันเลย
คำที่สลับอักษรคือคำที่สร้างขึ้นจากการเรียงลำดับตัวอักษรของคำอื่น ตัวอย่างเช่น cinema เป็นคำที่สลับอักษรของ iceman
เกมนี้ให้ผู้ใช้ค้นหาคำศัพท์จากพจนานุกรม
ผู้ใช้พยายามสร้างคำศัพท์ให้ได้มากที่สุดเท่าที่จะทำได้ โดยประกอบด้วยตัวอักษรทั้งหมดของคำศัพท์นั้นบวกกับตัวอักษรเพิ่มเติมอีกหนึ่งตัว โปรดทราบว่าการเพิ่มตัวอักษรพิเศษที่จุดเริ่มต้นหรือจุดสิ้นสุดโดยไม่เรียงลำดับตัวอักษรอื่นนั้นไม่ถูกต้อง ตัวอย่างเช่น หากเกมเลือกคำว่า 'ore' เป็นคำเริ่มต้น ผู้ใช้จะเดาว่า 'rose' หรือ 'zero' แต่เดาว่า 'sore' ไม่ได้
ผู้ใช้สามารถยอมแพ้และดูคำศัพท์ที่ไม่ได้เดา
เราได้ให้ โค้ดเริ่มต้น แก่คุณซึ่งประกอบด้วยพจนานุกรม 10,000 คำ และจัดการส่วน UI ของเกมนี้ และคุณจะรับผิดชอบในการเขียนคลาส AnagramBloc ที่จะจัดการคำศัพท์ทั้งหมด
โค้ดเริ่มต้นประกอบด้วยคลาสดาร์ทหลักสามคลาส:
anagrams_page.dart
นี่คือโค้ดง่ายๆ ที่แสดงหน้าจอที่เราเห็นด้านบน เราจะใช้ บล็อก สำหรับการจัดการสถานะสำหรับหน้าจอ เราจะเริ่มต้นด้วยการตั้งค่าเกมโดยส่งเหตุการณ์ไปยังบล็อกและกำหนดว่าจะเกิดอะไรขึ้นเมื่อหน้าจอตอบสนองต่อสถานะต่างๆ ของเกม
import 'package:anagrams/anagrams/bloc/anagram_bloc.dart'; import 'package:anagrams/anagrams/domain/word.dart'; import 'package:anagrams/l10n/l10n.dart'; import 'package:bloc_presentation/bloc_presentation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; class AnagramsPage extends StatelessWidget { const AnagramsPage({super.key}); @override Widget build(BuildContext context) { return BlocProvider( create: (_) => AnagramBloc() ..add( SetupAnagrams( DefaultAssetBundle.of(context), ), ), child: const AnagramsView(), ); } } class AnagramsView extends StatelessWidget { const AnagramsView({super.key}); @override Widget build(BuildContext context) { final l10n = context.l10n; return Scaffold( appBar: AppBar(title: Text(l10n.anagramAppBarTitle)), body: BlocBuilder<AnagramBloc, AnagramState>( builder: (context, state) { switch (state.status) { case AnagramGameStatus.gameError: return const Center( child: Text('An error occurred'), ); case AnagramGameStatus.loaded: return Padding( padding: const EdgeInsets.all(20), child: ListView( children: const [ _SelectedWord(), SizedBox(height: 20), _AnagramsTextField(), SizedBox(height: 10), _GuessListView(), ], ), ); case AnagramGameStatus.initial: return const Center( child: CircularProgressIndicator(), ); } }, ), floatingActionButton: const _NextWordButton(), ); } } class _SelectedWord extends StatelessWidget { const _SelectedWord(); @override Widget build(BuildContext context) { return BlocSelector<AnagramBloc, AnagramState, String>( selector: (state) => state.currentWord, builder: (context, currentWord) { return Text.rich( TextSpan( text: 'Find as many words as possible that can be ' 'formed by adding one letter to ', children: [ TextSpan( text: currentWord.toUpperCase(), style: const TextStyle(fontWeight: FontWeight.bold), ), TextSpan( text: ' (but that do not contain the substring' ' ${currentWord.toUpperCase()}).', ), ], ), ); }, ); } } class _AnagramsTextField extends StatelessWidget { const _AnagramsTextField(); @override Widget build(BuildContext context) { final controller = TextEditingController(); return TextField( controller: controller, decoration: const InputDecoration( hintText: 'Enter an anagram', border: OutlineInputBorder(), ), keyboardType: TextInputType.text, textInputAction: TextInputAction.done, onSubmitted: (value) { controller.clear(); context.read<AnagramBloc>().add(ProcessWord(value)); }, ); } } class _GuessListView extends StatelessWidget { const _GuessListView(); @override Widget build(BuildContext context) { return BlocSelector<AnagramBloc, AnagramState, List<Word>>( selector: (state) => state.guesses, builder: (context, guesses) { return Column( children: guesses.map((word) { return ListTile( minTileHeight: 0, contentPadding: EdgeInsets.zero, visualDensity: VisualDensity.compact, title: Text(word.value), leading: Icon( word.isAnagram ? Icons.check : Icons.close, color: word.isAnagram ? Colors.green : Colors.red, ), ); }).toList(), ); }, ); } } class _GameResult extends StatelessWidget { const _GameResult(this.currentWord, this.result); final List<Word> result; final String currentWord; @override Widget build(BuildContext context) { return ListView( shrinkWrap: true, children: [ const SizedBox(height: 20), Padding( padding: const EdgeInsets.symmetric( horizontal: 20, ), child: Text( 'Game Result for $currentWord', style: const TextStyle(fontSize: 20), ), ), Padding( padding: const EdgeInsets.all(20), child: SizedBox( width: double.infinity, child: DataTable( decoration: BoxDecoration( border: Border.all( color: Colors.grey.shade400, ), borderRadius: BorderRadius.circular(10), ), columns: const [ DataColumn(label: Text('Possible Anagrams')), DataColumn(label: Text('Your Guesses')), ], rows: result.map((word) { return DataRow( cells: [ DataCell(Text(word.value)), DataCell( Center( child: Icon( word.isAnagram ? Icons.check : Icons.close, color: word.isAnagram ? Colors.green : Colors.red, ), ), ), ], ); }).toList(), ), ), ), ], ); } } class _NextWordButton extends StatelessWidget { const _NextWordButton(); @override Widget build(BuildContext context) { final l10n = context.l10n; return BlocPresentationListener<AnagramBloc, AnagramPresenterEvent>( listener: (context, event) { if (event is FinishGuess) { // show a bottom sheet with the anagrams that were not guessed showModalBottomSheet<void>( context: context, useSafeArea: true, builder: (context) { return _GameResult(event.currentWord, event.result); }, ); } }, child: FloatingActionButton.extended( onPressed: () async { context.read<AnagramBloc>().add(ResetGame()); }, label: Text(l10n.nextWordButton), ), ); } }
_SelectWord
: แสดงคำที่เลือกสำหรับเกมเพื่อสร้างคำอักษรผสม
_AnagramsTextField
: รับคำและเรียกใช้เหตุการณ์เพื่อประมวลผลคำที่ผู้ใช้พิมพ์
_GuessListView
: แสดงการคาดเดาที่ผู้ใช้ป้อนและระบุว่าถูกต้องหรือไม่
_NextWordButton
: รีเซ็ตเกมและแสดงคำอักษรผสมทั้งหมดของคำปัจจุบันให้ผู้ใช้ทราบพร้อมทั้งแสดงคำที่ผู้ใช้เดาไว้ด้วย
anagram_states.dart
enum AnagramGameStatus { initial, loaded, gameError } const minNumAnagrams = 5; const defaultWordLength = 3; const maxDefaultWordLength = 7; @immutable final class AnagramState extends Equatable { factory AnagramState({ AnagramGameStatus status = AnagramGameStatus.initial, List<String> words = const [], String currentWord = '', List<String> anagrams = const [], List<Word> guesses = const [], HashSet<String>? wordSet, HashMap<String, List<String>>? anagramMap, HashMap<int, List<String>>? sizeToWords, int wordLength = defaultWordLength, }) { return AnagramState._( status: status, words: words, currentWord: currentWord, anagrams: anagrams, guesses: guesses, wordSet: wordSet ?? HashSet<String>(), anagramMap: anagramMap ?? HashMap<String, List<String>>(), sizeToWords: sizeToWords ?? HashMap<int, List<String>>(), wordLength: wordLength, ); } const AnagramState._({ required this.status, required this.words, required this.currentWord, required this.anagrams, required this.guesses, required this.wordSet, required this.anagramMap, required this.sizeToWords, this.wordLength = defaultWordLength, }); // The current status of the game final AnagramGameStatus status; // All the words in the game final List<String> words; // Currently chosen word of the game to form anagrams final String currentWord; // All the anagrams for the current word final List<String> anagrams; // All the guesses user has made final List<Word> guesses; // A set of all the words in the game final HashSet<String> wordSet; // A map of anagrams for each word final HashMap<String, List<String>> anagramMap; // Stores the words in increasing order of their length final HashMap<int, List<String>> sizeToWords; final int wordLength; AnagramState copyWith({ AnagramGameStatus? status, List<String>? words, String? currentWord, List<String>? anagrams, List<Word>? guesses, HashSet<String>? wordSet, HashMap<String, List<String>>? anagramMap, HashMap<int, List<String>>? sizeToWords, int? wordLength, }) { return AnagramState( status: status ?? this.status, words: words ?? this.words, currentWord: currentWord ?? this.currentWord, anagrams: anagrams ?? this.anagrams, guesses: guesses ?? this.guesses, wordSet: wordSet ?? this.wordSet, anagramMap: anagramMap ?? this.anagramMap, sizeToWords: sizeToWords ?? this.sizeToWords, wordLength: wordLength ?? this.wordLength, ); } @override List<Object?> get props => [ status, words, currentWord, anagrams, guesses, wordSet, anagramMap, sizeToWords, wordLength, ]; }
AnagramState เก็บตัวแปรสถานะทั้งหมดที่จำเป็นในการรันเกม
status
: ระบุว่าเกมกำลังโหลด (เริ่มต้น) โหลดแล้ว หรือมีข้อผิดพลาดเกิดขึ้นwords
: แสดงรายการคำทั้งหมดจากไฟล์ word.txt ที่โหลดจากไฟล์anagrams
: เก็บคำอักษรผสมทั้งหมดสำหรับคำที่เลือกcurrentword
: คำที่เลือกจากรายการคำและคำที่จะสร้างเป็นคำอักษรผสมguesses
: ทางเลือกทั้งหมดที่ผู้ใช้ป้อนและไม่ว่าทางเลือกนั้นจะถูกหรือผิด
เราจะมาสรุปรายละเอียดที่เหลือในบทความต่อไป
anagram_bloc.dart
import 'dart:async'; import 'dart:collection'; import 'dart:convert'; import 'package:anagrams/anagrams/domain/word.dart'; import 'package:bloc_presentation/bloc_presentation.dart'; import 'package:equatable/equatable.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; part 'anagram_events.dart'; part 'anagram_states.dart'; class AnagramBloc extends Bloc<AnagramEvent, AnagramState> with BlocPresentationMixin<AnagramState, AnagramPresenterEvent> { AnagramBloc() : super(AnagramState()) { on<SetupAnagrams>(_onSetupAnagrams); on<ProcessWord>(_onProcessWord); on<ResetGame>(_onResetGame); } Future<void> _onSetupAnagrams( SetupAnagrams event, Emitter<AnagramState> emit, ) async { try { // this should not be done here, // but for the sake of simplicity, we will do it here final wordsFile = await event.defaultAssetBundle.loadString('assets/words.txt'); // read each line in the file final words = const LineSplitter().convert(wordsFile); // change the state of the game emit( state.copyWith( status: AnagramGameStatus.loaded, words: words, ), ); // reset the game _onRestartGame(emit); } catch (e) { emit( state.copyWith( status: AnagramGameStatus.gameError, ), ); } } Future<void> _onProcessWord( ProcessWord event, Emitter<AnagramState> emit, ) async { try { final word = event.word.trim().toLowerCase(); if (word.isEmpty) { return; } if (_isGoodWord(word) && state.anagrams.contains(word)) { // remove the word from the list of anagrams // add the word to the list of guesses emit( state.copyWith( anagrams: state.anagrams..remove(word), guesses: [...state.guesses, Word(word, isAnagram: true)], ), ); // if there are no more anagrams, the game is over // call _onResetGame to reset the game if (state.anagrams.isEmpty) { add(ResetGame()); } } else { emit( state.copyWith( guesses: [...state.guesses, Word(word)], ), ); } } catch (e) { // show an error message } } FutureOr<void> _onResetGame(ResetGame event, Emitter<AnagramState> emit) { _onGameFinished(); _onRestartGame(emit); } void _onRestartGame(Emitter<AnagramState> emit) { final starterWord = _pickGoodStarterWord(emit); emit( state.copyWith( status: AnagramGameStatus.loaded, currentWord: starterWord, anagrams: _getAnagrams(starterWord), guesses: [], ), ); } void _onGameFinished() { emitPresentation(FinishGuess(_result, state.currentWord)); } List<Word> get _result { // All the anagrams that were not guessed final notGuessedAnagrams = state.anagrams.map(Word.new).toList(); // All the guesses that were made final guesses = state.guesses.where((word) => word.isAnagram).toList(); // return the list of anagrams that were not guessed return [...guesses, ...notGuessedAnagrams]; } /// create a function to find all the anagrams of the target word List<String> _getAnagrams(String targetWord) { // find all the anagrams of the target word final anagrams = <String>[]; // return the list of anagrams return anagrams; } // ignore: unused_element List<String> _getAnagramsWithOneMoreLetter(String targetWord) { final anagrams = HashSet<String>(); // return the list of anagrams return anagrams.toList(); } /// Picks a good starter word for the game. String _pickGoodStarterWord(Emitter<AnagramState> emit) { const word = 'skate'; return word; } /// Checks if the word is a good word. bool _isGoodWord(String word) { return true; } }
onSetupAnagrams
: อ่านไฟล์ แยกคำ และเพิ่มคำเหล่านั้นลงในรายการ นอกจากนี้ยังค้นหาคำปัจจุบัน ค้นหาคำที่อ่านสลับอักษรสำหรับคำที่เลือก และอัปเดตสถานะ
onProcessWord
: นี่คือตัวจัดการที่เรียกเมื่อผู้ใช้ป้อนการคาดเดาและอัปเดตสถานะonReset
: เรียกเมื่อคลิกปุ่มคำถัดไปและรีเซ็ตเกมisGoodWord
: ยืนยันว่าคำที่กำหนดไว้มีอยู่ในพจนานุกรมและไม่ถูกสร้างขึ้นโดยการเติมตัวอักษรลงที่จุดเริ่มต้นหรือจุดสิ้นสุดของคำฐานgetAnagrams
: สร้างรายการอักษรผสมที่เป็นไปได้ทั้งหมดของคำที่กำหนดgetAnagramsWithOneMoreLetter
: สร้างรายการคำที่เป็นไปได้ทั้งหมดที่สามารถสร้างขึ้นได้โดยการเพิ่มตัวอักษรหนึ่งตัวลงไปในคำที่กำหนดpickGoodStarterWord
: เลือกคำแบบสุ่มที่มีอักษรผสมอย่างน้อยตามจำนวนที่ต้องการก้าวแรกมุ่งเน้นไปที่การสร้างโปรแกรมทำงานที่เรียบง่ายมาก คุณจะต้องนำรากฐานที่ถูกสร้างขึ้นในก้าวที่ 2 และ 3 มาใช้
เราจะทำงานกับ anagram_bloc.dart
ใช้ getAnagrams
ซึ่งรับสตริงและค้นหาคำที่อ่านออกเสียงได้ทั้งหมดของสตริงนั้นในอินพุตของเรา กลยุทธ์ของเราในตอนนี้จะตรงไปตรงมา: เพียงเปรียบเทียบแต่ละสตริงในรายการ words
กับคำอินพุตเพื่อดูว่าเป็นคำที่อ่านออกเสียงได้หรือไม่ แต่เราจะทำอย่างไร?
มีกลยุทธ์ที่แตกต่างกันที่คุณสามารถนำมาใช้เพื่อระบุว่าสตริงสองตัวเป็นอักษรผสมกันหรือไม่ (เช่น นับจำนวนการปรากฏของแต่ละตัวอักษร) แต่สำหรับจุดประสงค์ของเรา คุณจะสร้างฟังก์ชันช่วยเหลือ (เรียกว่า sortLetters
) ที่รับ String
และส่งคืน String
อื่นที่มีตัวอักษรเหมือนกันในลำดับตัวอักษร (เช่น "post" -> "post")
การกำหนดว่าสตริงสองตัวเป็นแอนนาแกรมหรือไม่นั้นเป็นเรื่องง่ายๆ ของการตรวจสอบว่ามีความยาวเท่ากัน (เพื่อความเร็ว) และตรวจสอบว่าเวอร์ชันที่เรียงลำดับแล้วของตัวอักษรนั้นเท่ากันหรือไม่
น่าเสียดายที่กลยุทธ์ตรงไปตรงมานั้นช้าเกินไปสำหรับเราในการนำส่วนที่เหลือของเกมไปใช้ ดังนั้น เราจะต้องกลับไปดู onSetupAnagrams
อีกครั้ง และค้นหาโครงสร้างข้อมูลบางอย่างที่จัดเก็บคำศัพท์ในลักษณะที่สะดวกสำหรับจุดประสงค์ของเรา เราจะสร้างโครงสร้างข้อมูลใหม่สองแบบ (นอกเหนือจาก words
):
HashSet
(เรียกว่า wordSet
) ที่จะทำให้เราตรวจสอบได้อย่างรวดเร็ว (ใน O(1)) ว่าคำนั้นถูกต้องหรือไม่
HashMap
(เรียกว่า anagramMap
) ที่จะช่วยให้เราจัดกลุ่ม anagram ได้ เราจะทำเช่นนี้โดยใช้สตริงเวอร์ชัน sortLetters
เป็นคีย์ และจัดเก็บรายการคำที่สอดคล้องกับคีย์นั้นเป็นค่าของเรา ตัวอย่างเช่น เราอาจมีรายการในรูปแบบ: key: "opst" value: ["post", "spot", "pots", "tops", ...].
ขณะที่คุณประมวลผลคำอินพุต ให้เรียก sortLetters
บนคำแต่ละคำ จากนั้นตรวจสอบว่า anagramMap
มีรายการสำหรับคีย์นั้นอยู่แล้วหรือไม่ หากมี ให้เพิ่มคำปัจจุบันลงใน List
ที่คีย์นั้น มิฉะนั้น ให้สร้างคำใหม่ เพิ่มคำลงไป และเก็บไว้ใน HashMap
พร้อมคีย์ที่เกี่ยวข้อง
เมื่อคุณทำขั้นตอนนี้เสร็จเรียบร้อยแล้ว คุณก็มาถึงจุดสิ้นสุดของขั้นตอนที่ 1 แล้ว ตอนนี้คุณพร้อมที่จะไปยังขั้นตอนที่สอง ซึ่งคุณจะเพิ่มความซับซ้อนให้กับโปรแกรมของคุณมากขึ้น
Milestone 2 เป็นเรื่องของการทำให้แน่ใจว่าคำที่เลือกนั้นเหมาะสมกับเกมแอนนาแกรม แตกต่างจาก Milestone ก่อนหน้านี้ เกมนี้แบ่งออกเป็น 3 ส่วน
isGoodWord
งานถัดไปของคุณคือการใช้ isGoodWord
ซึ่งจะตรวจสอบ:
wordSet
) และ
การตรวจสอบว่าคำนั้นเป็นคำในพจนานุกรมที่ถูกต้องหรือไม่นั้นสามารถทำได้โดยดูที่ wordSet
เพื่อดูว่ามีคำนั้นอยู่หรือไม่ การตรวจสอบว่าคำนั้นไม่มีคำฐานเป็นสตริงย่อยถือเป็นความท้าทาย!
getAnagramsWithOneMoreLetter
ในที่สุด ให้ใช้ getAnagramsWithOneMoreLetter
ซึ่งใช้สตริงและค้นหาอักษรผสมทั้งหมดที่สามารถสร้างได้โดยการเพิ่มตัวอักษรหนึ่งตัวลงไปในคำนั้น
อย่าลืมสร้าง List
ใหม่เป็นค่าส่งคืนของคุณ จากนั้นตรวจสอบคำที่กำหนด + แต่ละตัวในตัวอักษรทีละตัวกับรายการใน anagramMap
นอกจากนี้ ให้อัปเดต onRestartGame
เพื่อเรียกใช้ getAnagramsWithOneMoreLetter
แทน getAnagrams
pickGoodStarterWord
หากเกมของคุณทำงานได้ ให้ดำเนินการใช้ pickGoodStarterWord
เพื่อให้เกมน่าสนใจยิ่งขึ้น เลือกจุดเริ่มต้นแบบสุ่มในรายการคำ และตรวจสอบแต่ละคำในอาร์เรย์จนกว่าคุณจะพบคำที่มีอย่างน้อย minNumAnagrams
อย่าลืมห่อรอบจุดเริ่มต้นของอาร์เรย์หากจำเป็น
ผ่านไปสองในสามแล้ว! อีกแค่ก้าวเดียวก็ถึงส่วนขยายแล้ว
ณ จุดนี้ เกมดังกล่าวสามารถใช้งานได้ แต่จะเล่นได้ยากพอสมควรหากคุณเริ่มด้วยคำฐานยาว เพื่อหลีกเลี่ยงปัญหานี้ ให้เราปรับโครงสร้าง onSetupGame
ใหม่เพื่อให้คำมีความยาวเพิ่มขึ้น
การรีแฟกเตอร์นี้เริ่มต้นใน onSetupGame
ซึ่งนอกจากการเติม word
ในรายการแล้ว คุณยังควรจัดเก็บคำแต่ละคำใน HashMap
(เรียกว่า sizeToWords
) ซึ่งจะแมปความยาวของคำไปยัง List
คำทั้งหมดที่มีความยาวเท่ากัน ซึ่งหมายความว่า ตัวอย่างเช่น คุณควรสามารถรับคำสี่ตัวอักษรทั้งหมดในพจนานุกรมได้โดยเรียก sizeToWords.get(4)
ใน pickGoodStarterWord
ให้จำกัดการค้นหาของคุณตามคำที่มีความยาว wordLength
และเมื่อทำเสร็จแล้ว ให้เพิ่ม wordLength
(ยกเว้นว่าจะมีระยะถึง axWordLength
แล้ว) เพื่อให้การเรียกครั้งต่อไปจะส่งคืนคำที่มีความยาวมากขึ้น
กิจกรรมนี้ (เช่นเดียวกับกิจกรรมในอนาคตทั้งหมด) มีส่วนขยายเพิ่มเติมที่เป็นทางเลือก หากเวลาเพียงพอ ให้ลองส่วนขยายอย่างน้อยหนึ่งรายการจากรายการด้านล่าง หรือส่วนขยายที่คุณคิดขึ้นเอง
wordSet
เนื่องจากยังคงใช้เป็นอักษรผสมในคำอื่นๆ ได้
ขอแสดงความยินดีที่คุณทำสำเร็จ! คุณได้ศึกษาแล้วว่า Lists, HashSets และ HashMaps ช่วยจัดการข้อมูลอย่างมีประสิทธิภาพใน Flutter ได้อย่างไร เช่นเดียวกับในแอปพลิเคชันที่ปรับให้เหมาะสมแล้ว การทำความเข้าใจโครงสร้างเหล่านี้จะช่วยให้คุณได้เปรียบในการเขียนโค้ดที่ปรับขนาดได้และมีประสิทธิภาพ ดังนั้น จงทำต่อไปและตบไหล่ตัวเองให้สมเกียรติ! ตอนนี้ จงนำความรู้เหล่านี้ไปใช้จริงและสร้างสิ่งที่น่าทึ่งต่อไป!