Κατασκευάστε παιχνίδια και μάθετε Πληροφορική.
Οι δομές δεδομένων αποτελούν τη βάση της αποτελεσματικής ανάπτυξης λογισμικού, ωστόσο οι παραδοσιακές μέθοδοι μάθησης συχνά τις κάνουν να αισθάνονται αφηρημένες και αποκομμένες από τις εφαρμογές του πραγματικού κόσμου. Αυτό το άρθρο ακολουθεί μια διαφορετική προσέγγιση — συνδυάζοντας τη θεωρία με την πρακτική εφαρμογή στο Flutter για να κάνει τη μάθηση τόσο ελκυστική όσο και πρακτική .
Εμπνευσμένη από το Applied CS της Google με Android , αυτή η προσαρμογή για το Flutter παρέχει έναν διαδραστικό τρόπο κατανόησης των Arrays, των HashSets και των HashMaps . Σε μόλις 3–4 ώρες , θα αποκτήσετε μια βαθύτερη κατανόηση αυτών των θεμελιωδών δομών δεδομένων, ενώ θα τις εφαρμόσετε σε ένα ουσιαστικό πλαίσιο.
Είτε είστε αρχάριος που θέλει να ενισχύσει τις βασικές αρχές του CS σας είτε έμπειρος προγραμματιστής που στοχεύει να βελτιώσει τις δεξιότητές σας, αυτός ο οδηγός προσφέρει έναν αποτελεσματικό και ευχάριστο τρόπο για να κατακτήσετε βασικές δομές δεδομένων. Ας ξεκινήσουμε.
Θα χρησιμοποιήσουμε μερικές δομές δεδομένων στη δραστηριότητα του εργαστηρίου, επομένως ανατρέξτε στις Λίστες , τα HashSets και τους HashMaps . Θα πρέπει να μπορείτε να εισάγετε, να διαγράφετε, να έχετε πρόσβαση και να ελέγχετε την ύπαρξη στοιχείων χρησιμοποιώντας αυτές τις δομές δεδομένων στο Dart.
Αυτή είναι μια μικρή εισαγωγή στις δομές δεδομένων, HashSets και HashMap.
Ως παράδειγμα δραστηριότητας χρησιμοποιώντας το HashMaps, δημιουργήστε ένα πρόγραμμα (όχι απαραίτητα μια εφαρμογή Flutter — η γραμμή εντολών είναι μια χαρά) που θα λάβει έναν κωδικό χώρας τριών γραμμάτων (δείτε ISO-3166 ) και θα επιστρέψει το πλήρες όνομα της χώρας στην οποία ανήκει.
Για παράδειγμα:
Input | Output ----- | ---------------------------------------------------- GBR | United Kingdom of Great Britain and Northern Ireland IDN | Indonesia IND | India
Ως επέκταση, εάν η εισαγωγή είναι μεγαλύτερη από 3 γράμματα, θεωρήστε την ως το όνομα μιας χώρας και επιστρέψτε τον κωδικό τριών γραμμάτων για αυτήν. Γράψτε ένα χρήσιμο μήνυμα σφάλματος εάν η εισαγωγή δεν είναι ούτε έγκυρος κωδικός ούτε όνομα χώρας.
Ας ξεκινήσουμε.
Ο αναγραμματισμός είναι μια λέξη που σχηματίζεται με την αναδιάταξη των γραμμάτων μιας άλλης λέξης. Για παράδειγμα, ο κινηματογράφος είναι ένας αναγραμματισμός του παγανθρώπου .
Το παιχνίδι παρέχει στον χρήστη μια λέξη από το λεξικό.
Ο χρήστης προσπαθεί να δημιουργήσει όσο το δυνατόν περισσότερες λέξεις που περιέχουν όλα τα γράμματα της συγκεκριμένης λέξης συν ένα επιπλέον γράμμα. Λάβετε υπόψη ότι η προσθήκη του επιπλέον γράμματος στην αρχή ή στο τέλος χωρίς να αναδιατάξετε τα άλλα γράμματα δεν είναι έγκυρη. Για παράδειγμα, εάν το παιχνίδι επιλέξει τη λέξη «μετάλλευμα» ως αρχή, ο χρήστης μπορεί να μαντέψει «ροζ» ή «μηδέν» αλλά όχι «πόνεο».
Ο χρήστης μπορεί να τα παρατήσει και να δει τις λέξεις που δεν μάντεψε.
Σας παρέχουμε έναν αρχικό κώδικα που περιέχει ένα λεξικό 10.000 λέξεων και χειρίζεται τα τμήματα διεπαφής χρήστη αυτού του παιχνιδιού και θα είστε υπεύθυνοι για τη σύνταξη του AnagramBlocclass που χειρίζεται όλους τους χειρισμούς λέξεων.
Ο κωδικός εκκίνησης αποτελείται από τρεις κύριες κατηγορίες βελών:
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
: Επιλέξτε τυχαία μια λέξη με τουλάχιστον τον επιθυμητό αριθμό αναγραμμάτων.Το πρώτο ορόσημο εστιάζει στη δημιουργία ενός πολύ απλού προγράμματος εργασίας. Θα εφαρμόσετε τα θεμέλια που θα χτιστούν με τη σειρά τους στα Milestones 2 και 3.
Θα εργαστούμε στο anagram_bloc.dart
.
Υλοποιήστε getAnagrams
που παίρνει μια συμβολοσειρά και βρίσκει όλους τους αναγραμματισμούς αυτής της συμβολοσειράς στην εισαγωγή μας. Η στρατηγική μας προς το παρόν θα είναι απλή: απλώς συγκρίνετε κάθε συμβολοσειρά σε words
Λίστα με τη λέξη εισόδου για να προσδιορίσετε εάν είναι αναγραμματισμοί. Αλλά πώς θα το κάνουμε αυτό;
Υπάρχουν διαφορετικές στρατηγικές που θα μπορούσατε να χρησιμοποιήσετε για να προσδιορίσετε εάν δύο συμβολοσειρές είναι αναγράμματα μεταξύ τους (όπως η μέτρηση του αριθμού των εμφανίσεων κάθε γράμματος), αλλά για το σκοπό μας θα δημιουργήσετε μια βοηθητική συνάρτηση (την ονομάζουμε sortLetters
) που παίρνει μια String
και επιστρέφει μια άλλη String
με τα ίδια γράμματα με αλφαβητική σειρά (π.χ. "post" -> "post").
Ο προσδιορισμός του αν δύο συμβολοσειρές είναι αναγραμματισμοί είναι ένα απλό θέμα να ελέγξουμε ότι έχουν το ίδιο μήκος (για λόγους ταχύτητας) και να ελέγξουμε ότι οι ταξινομημένες εκδόσεις των γραμμάτων τους είναι ίσες.
Δυστυχώς, η απλή στρατηγική θα είναι πολύ αργή για να μπορέσουμε να εφαρμόσουμε το υπόλοιπο αυτού του παιχνιδιού. Επομένως, θα χρειαστεί να επανεξετάσουμε onSetupAnagrams
μας και να βρούμε κάποιες δομές δεδομένων που αποθηκεύουν τις λέξεις με τρόπους που είναι βολικοί για τους σκοπούς μας. Θα δημιουργήσουμε δύο νέες δομές δεδομένων (επιπλέον των words
):
HashSet
(που ονομάζεται wordSet
) που θα μας επιτρέψει να επαληθεύσουμε γρήγορα (στο O(1)) εάν μια λέξη είναι έγκυρη.
HashMap
(ονομάζεται anagramMap
) που θα μας επιτρέψει να ομαδοποιήσουμε αναγραμματισμούς. Θα το κάνουμε αυτό χρησιμοποιώντας την έκδοση sortLetters
μιας συμβολοσειράς ως κλειδί και αποθηκεύοντας μια λίστα με τις λέξεις που αντιστοιχούν σε αυτό το κλειδί ως τιμή μας. Για παράδειγμα, μπορεί να έχουμε μια καταχώρηση της μορφής: κλειδί: "opst" τιμή: ["post", "spot", "pots", "tops", ...].
Καθώς επεξεργάζεστε τις λέξεις εισαγωγής, καλέστε sortLetters
σε καθεμία από αυτές και, στη συνέχεια, ελέγξτε εάν anagramMap
περιέχει ήδη μια καταχώρηση για αυτό το κλειδί. Εάν ναι, προσθέστε την τρέχουσα λέξη στη List
σε αυτό το πλήκτρο. Διαφορετικά, δημιουργήστε μια νέα, προσθέστε τη λέξη σε αυτήν και αποθηκεύστε τη στο HashMap
με το αντίστοιχο κλειδί.
Μόλις ολοκληρώσετε αυτό, έχετε φτάσει στο τέλος του Milestone 1! Τώρα είστε έτοιμοι να προχωρήσετε στο δεύτερο ορόσημο, όπου θα προσθέσετε περισσότερη πολυπλοκότητα στο πρόγραμμά σας.
Το Milestone 2 έχει να κάνει με τη διασφάλιση ότι οι λέξεις που επιλέγονται είναι κατάλληλες για το παιχνίδι αναγραμματισμού. Διαφορετικός. Το προηγούμενο ορόσημο, αυτό χωρίζεται σε τρεις ενότητες.
isGoodWord
Η επόμενη εργασία σας είναι να εφαρμόσετε isGoodWord
το οποίο ελέγχει:
wordSet
) και
Ο έλεγχος εάν μια λέξη είναι έγκυρη λέξη λεξικού μπορεί να επιτευχθεί κοιτάζοντας το wordSet
για να δείτε αν περιέχει τη λέξη. Ο έλεγχος ότι η λέξη δεν περιέχει τη βασική λέξη ως υποσυμβολοσειρά παραμένει ως πρόκληση!
getAnagramsWithOneMoreLetter
Τέλος, εφαρμόστε getAnagramsWithOneMoreLetter
που παίρνει μια συμβολοσειρά και βρίσκει όλους τους αναγραμματισμούς που μπορούν να σχηματιστούν προσθέτοντας ένα γράμμα σε αυτήν τη λέξη.
Βεβαιωθείτε ότι έχετε δημιουργήσει μια νέα List
ως τιμή επιστροφής και, στη συνέχεια, ελέγξτε τη δεδομένη λέξη + κάθε γράμμα του αλφαβήτου ένα προς ένα σε σχέση με τις εγγραφές στο anagramMap
.
Επίσης, ενημερώστε το onRestartGame
για να καλέσετε getAnagramsWithOneMoreLetter
αντί για getAnagrams
.
pickGoodStarterWord
Εάν το παιχνίδι σας λειτουργεί, προχωρήστε στην εφαρμογή pickGoodStarterWord
για να κάνετε το παιχνίδι πιο ενδιαφέρον. Επιλέξτε ένα τυχαίο σημείο εκκίνησης στη λέξη Λίστα και ελέγξτε κάθε λέξη στον πίνακα μέχρι να βρείτε μία που έχει τουλάχιστον minNumAnagrams
. Φροντίστε να χειριστείτε το τύλιγμα μέχρι την αρχή της συστοιχίας, εάν χρειάζεται.
Τα δύο τρίτα της διαδρομής! Μόνο ένα ορόσημο και η επέκταση πριν τελειώσετε.
Σε αυτό το σημείο, το παιχνίδι είναι λειτουργικό, αλλά μπορεί να είναι αρκετά δύσκολο να παίξετε αν ξεκινήσετε με μια μεγάλη βασική λέξη. Για να αποφευχθεί αυτό, ας αναπαράγουμε onSetupGame
για να δώσουμε λέξεις αυξανόμενης έκτασης.
Αυτό το refactor ξεκινά στο onSetupGame
όπου εκτός από τη συμπλήρωση της λίστας word
, θα πρέπει επίσης να αποθηκεύσετε κάθε λέξη σε έναν HashMap
(ας το ονομάσουμε sizeToWords
) που αντιστοιχίζει το μήκος λέξης σε μια List
με όλες τις λέξεις αυτού του μήκους. Αυτό σημαίνει, για παράδειγμα, θα πρέπει να μπορείτε να λαμβάνετε και τις τέσσερις λέξεις στο λεξικό καλώντας sizeToWords.get(4)
.
Στο pickGoodStarterWord
, περιορίστε την αναζήτησή σας στις λέξεις μήκους wordLength
και μόλις τελειώσετε, αυξήστε wordLength
(εκτός αν είναι ήδη στο axWordLength
) έτσι ώστε η επόμενη επίκληση να επιστρέψει μια μεγαλύτερη λέξη.
Αυτή η δραστηριότητα (όπως όλες οι μελλοντικές δραστηριότητες) περιέχει ορισμένες προαιρετικές επεκτάσεις. Εάν το επιτρέπει ο χρόνος, δοκιμάστε τουλάχιστον μία επέκταση από την παρακάτω λίστα ή μία που έχετε εφεύρει μόνοι σας.
wordSet
, καθώς μπορούν να χρησιμοποιηθούν ως αναγραμματισμοί με άλλες λέξεις.
Συγχαρητήρια που φτάσατε μέχρι το τέλος! Εξερευνήσατε πώς οι λίστες, τα HashSets και οι HashMaps παρέχουν αποτελεσματικό χειρισμό δεδομένων στο Flutter, όπως ακριβώς κάνουν σε κάθε καλά βελτιστοποιημένη εφαρμογή. Η κατανόηση αυτών των δομών σας δίνει ένα πλεονέκτημα στη σύνταξη επεκτάσιμου και αποδοτικού κώδικα. Προχωρήστε λοιπόν και δώστε στον εαυτό σας ένα χτύπημα στην πλάτη που σας αξίζει! Τώρα, βάλτε αυτή τη γνώση σε πράξη και συνεχίστε να χτίζετε εκπληκτικά πράγματα!