exercism

Exercism - High Scores

This post shows you how to get High Scores exercise of Exercism.

Stevinator Stevinator
7 min read
SHARE
exercism dart flutter high-scores

Preparation

Before we click on our next exercise, let’s see what concepts of DART we need to consider

High Scores Exercise

So we need to use the following concepts.

Lists

Lists are ordered collections of items. You can access elements by index, get the last element, and perform various operations on them.

void main() {
  List<int> scores = [30, 50, 20, 70];
  
  // Access last element
  print(scores.last); // 70
  
  // Access by index
  print(scores[0]); // 30
  
  // Get length
  print(scores.length); // 4
}

Reduce Method

The reduce method combines all elements of a collection into a single value using a provided function. It’s useful for finding maximum or minimum values.

void main() {
  List<int> numbers = [3, 1, 4, 1, 5, 9];
  
  // Find maximum
  int max = numbers.reduce((a, b) => a > b ? a : b);
  print(max); // 9
  
  // Find minimum
  int min = numbers.reduce((a, b) => a < b ? a : b);
  print(min); // 1
  
  // Sum all elements
  int sum = numbers.reduce((a, b) => a + b);
  print(sum); // 23
}

Sort Method

The sort method sorts a list in place. You can provide a custom comparator function to control the sort order.

void main() {
  List<int> scores = [30, 50, 20, 70];
  
  // Sort ascending (default)
  scores.sort();
  print(scores); // [20, 30, 50, 70]
  
  // Sort descending using comparator
  scores.sort((a, b) => b.compareTo(a));
  print(scores); // [70, 50, 30, 20]
  
  // Alternative: using compareTo directly
  List<int> scores2 = [30, 50, 20, 70];
  scores2.sort((a, b) => b.compareTo(a));
  print(scores2); // [70, 50, 30, 20]
}

Take Method

The take method returns the first n elements of a collection. It’s useful for getting the top items from a sorted list.

void main() {
  List<int> scores = [70, 50, 30, 20, 10];
  
  // Get top 3
  List<int> topThree = scores.take(3).toList();
  print(topThree); // [70, 50, 30]
  
  // Works with any iterable
  var topTwo = scores.take(2);
  print(topTwo.toList()); // [70, 50]
}

Cascade Operator

The cascade operator (..) allows you to perform multiple operations on the same object in sequence. It’s useful for method chaining.

void main() {
  List<int> scores = [30, 50, 20, 70];
  
  // Create a copy and sort it
  List<int> sorted = scores.toList()..sort();
  print(scores); // [30, 50, 20, 70] (original unchanged)
  print(sorted); // [20, 30, 50, 70] (sorted copy)
  
  // Multiple operations
  List<int> result = scores.toList()
    ..sort()
    ..reversed;
  print(result); // [20, 30, 50, 70]
}

List Properties vs Methods

In Dart, some operations are properties (like last) while others are methods (like sort()). Properties don’t use parentheses, while methods do.

void main() {
  List<int> scores = [30, 50, 20, 70];
  
  // Property (no parentheses)
  int lastScore = scores.last; // 70
  
  // Method (with parentheses)
  scores.sort(); // Sorts in place
  
  // Getting a property vs calling a method
  print(scores.length); // Property
  print(scores.isEmpty); // Property
  scores.add(80); // Method
}

Introduction

Manage a game player’s High Score list.

Your task is to build a high-score component of the classic Frogger game, one of the highest selling and most addictive games of all time, and a classic of the arcade era. Your task is to write methods that return the highest score from the list, the last added score and the three highest scores.

What is Frogger?

Frogger is a 1981 arcade game developed by Konami and originally published by Sega. The object of the game is to direct frogs to their homes one by one by crossing a busy road and navigating a river full of hazards. Frogger was one of the most popular games during the golden age of arcade video games.

— Wikipedia

How can we manage high scores?

To manage high scores, you need to:

  1. Get the latest score - Return the most recently added score (the last element in the list)
  2. Get the personal best - Return the highest score from all scores
  3. Get the top three - Return the three highest scores in descending order

For example, with scores [30, 50, 20, 70, 10]:

  • Latest score: 10 (the last one added)
  • Personal best: 70 (the highest score)
  • Top three: [70, 50, 30] (the three highest scores)

⚠️ Old Solution (No Longer Works)

Previously, the solution used a getter property for personalTopThree and a helper method sortList(). Here’s what the old solution looked like:

class HighScores {
  List<int> scores;
  HighScores(this.scores);
  
  int latest() => scores.last;
  
  List<int> sortList() => scores.toList()..sort();
  
  int personalBest() => sortList().last;
  
  List<int> personalTopThree => sortList().reversed.take(3).toList();
}

Why This Solution Doesn’t Work Anymore

The old solution has several issues:

  1. Getter vs Method: personalTopThree was defined as a getter property (without parentheses), but the exercise now requires it to be a method. This is a breaking change in the API design.

  2. Inefficient sorting: The solution sorts the entire list in ascending order, then reverses it to get descending order. This is less efficient than sorting directly in descending order.

  3. Unnecessary helper method: The sortList() method creates a copy and sorts it, but this pattern is repeated and can be simplified.

  4. Inefficient personal best: Using sortList().last sorts the entire list just to find the maximum value, which is inefficient. Using reduce() is more efficient for finding a single maximum value.

The exercise now requires:

  • personalTopThree() to be a method (with parentheses) rather than a property
  • More efficient algorithms: using reduce() for finding the maximum instead of sorting
  • Direct descending sort using a custom comparator instead of sorting and reversing
  • Cleaner, more direct code without unnecessary helper methods

Solution

class HighScores {
  List<int> scores;
  HighScores(this.scores);
  
  int latest() => scores.last;
  
  int personalBest() => scores.reduce((a, b) => a > b ? a : b);
  
  List<int> personalTopThree() {
    final sorted = scores.toList()..sort((a, b) => b.compareTo(a));
    return sorted.take(3).toList();
  }
}

Let’s break down the solution:

  1. int latest() - Returns the most recently added score:

    • Uses scores.last to get the last element in the list
    • This is the simplest and most efficient way to get the latest score
  2. int personalBest() - Returns the highest score:

    • Uses reduce() with a comparison function (a, b) => a > b ? a : b
    • This efficiently finds the maximum value without sorting the entire list
    • Much more efficient than sorting and taking the last element
  3. List<int> personalTopThree() - Returns the three highest scores:

    • Creates a copy of the scores list using toList()
    • Uses the cascade operator .. to sort the copy in place (without affecting the original)
    • Sorts in descending order using (a, b) => b.compareTo(a) - this compares b to a, so larger values come first
    • Uses take(3) to get the first three elements (which are the top three after descending sort)
    • Converts to a list and returns

The solution is more efficient and follows modern Dart best practices by using reduce() for finding the maximum and direct descending sort for the top three scores.


You can watch this tutorial on YouTube. So don’t forget to like and subscribe. 😉

Watch on YouTube
Stevinator

Stevinator

Stevinator is a software engineer passionate about clean code and best practices. Loves sharing knowledge with the developer community.