Skip to content

Commit

Permalink
Implement high-scores exercise (#287)
Browse files Browse the repository at this point in the history
* Implement high-scores

* Fix dot to a comma in config.json
  • Loading branch information
0xNeshi authored Oct 2, 2024
1 parent 597bf0a commit 8f9565b
Show file tree
Hide file tree
Showing 8 changed files with 290 additions and 0 deletions.
8 changes: 8 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -748,6 +748,14 @@
"prerequisites": [],
"difficulty": 2
},
{
"slug": "high-scores",
"name": "High Scores",
"uuid": "993e43d8-e31b-4dd5-a73d-f5e99b499207",
"practices": [],
"prerequisites": [],
"difficulty": 3
},
{
"slug": "sum-of-multiples",
"name": "Sum of Multiples",
Expand Down
6 changes: 6 additions & 0 deletions exercises/practice/high-scores/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Instructions

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.
21 changes: 21 additions & 0 deletions exercises/practice/high-scores/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"authors": [
"0xNeshi"
],
"files": {
"solution": [
"src/lib.cairo"
],
"test": [
"tests/high_scores.cairo"
],
"example": [
".meta/example.cairo"
],
"invalidator": [
"Scarb.toml"
]
},
"blurb": "Manage a player's High Score list.",
"source": "Tribute to the eighties' arcade game Frogger"
}
77 changes: 77 additions & 0 deletions exercises/practice/high-scores/.meta/example.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
use core::dict::Felt252Dict;

#[derive(Drop,)]
pub struct HighScores {
scores: Array<u32>,
}

#[generate_trait]
pub impl HighScoresImpl of HighScoresTrait {
fn new(scores: Array<u32>) -> HighScores {
HighScores { scores }
}

fn scores(self: @HighScores) -> Span<u32> {
self.scores.span()
}

fn latest(self: @HighScores) -> Option<u32> {
match self.scores.get(self.scores.len() - 1) {
Option::Some(boxed) => Option::Some(*boxed.unbox()),
_ => Option::None
}
}

fn personal_best(self: @HighScores) -> Option<u32> {
if self.scores.is_empty() {
return Option::None;
};
let mut max = 0;
for score in self.scores.span() {
if score > @max {
max = *score;
}
};
Option::Some(max)
}

fn personal_top_three(self: @HighScores) -> Span<u32> {
let sorted = insertion_sort(self.scores.span());
let take = if sorted.len() > 3 {
3
} else {
sorted.len()
};
sorted.span().slice(0, take)
}
}

fn insertion_sort(span: Span<u32>) -> Array<u32> {
if span.is_empty() {
return array![];
}

// Felt252Dict enables swapping and moving values
let mut sorted: Felt252Dict<u32> = Default::default();
sorted.insert(0, *span[0]);

// insert all elements in their sorted position
for i in 1
..span
.len() {
let elem = *span[i];
let mut j: felt252 = i.into();
while j != 0 && elem > sorted.get(j - 1) {
sorted.insert(j, sorted.get(j - 1));
j -= 1;
};
sorted.insert(j.into(), elem);
};

// collect all elements into an array
let mut sorted_arr: Array<u32> = array![];
for i in 0..span.len() {
sorted_arr.append(sorted.get(i.into()));
};
sorted_arr
}
46 changes: 46 additions & 0 deletions exercises/practice/high-scores/.meta/tests.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# This is an auto-generated file.
#
# Regenerating this file via `configlet sync` will:
# - Recreate every `description` key/value pair
# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
# - Preserve any other key/value pair
#
# As user-added comments (using the # character) will be removed when this file
# is regenerated, comments can be added via a `comment` key.

[1035eb93-2208-4c22-bab8-fef06769a73c]
description = "List of scores"

[6aa5dbf5-78fa-4375-b22c-ffaa989732d2]
description = "Latest score"

[b661a2e1-aebf-4f50-9139-0fb817dd12c6]
description = "Personal best"

[3d996a97-c81c-4642-9afc-80b80dc14015]
description = "Top 3 scores -> Personal top three from a list of scores"

[1084ecb5-3eb4-46fe-a816-e40331a4e83a]
description = "Top 3 scores -> Personal top highest to lowest"

[e6465b6b-5a11-4936-bfe3-35241c4f4f16]
description = "Top 3 scores -> Personal top when there is a tie"

[f73b02af-c8fd-41c9-91b9-c86eaa86bce2]
description = "Top 3 scores -> Personal top when there are less than 3"

[16608eae-f60f-4a88-800e-aabce5df2865]
description = "Top 3 scores -> Personal top when there is only one"

[2df075f9-fec9-4756-8f40-98c52a11504f]
description = "Top 3 scores -> Latest score after personal top scores"

[809c4058-7eb1-4206-b01e-79238b9b71bc]
description = "Top 3 scores -> Scores after personal top scores"

[ddb0efc0-9a86-4f82-bc30-21ae0bdc6418]
description = "Top 3 scores -> Latest score after personal best"

[6a0fd2d1-4cc4-46b9-a5bb-2fb667ca2364]
description = "Top 3 scores -> Scores after personal best"
7 changes: 7 additions & 0 deletions exercises/practice/high-scores/Scarb.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[package]
name = "high_scores"
version = "0.1.0"
edition = "2024_07"

[dev-dependencies]
cairo_test = "2.8.2"
25 changes: 25 additions & 0 deletions exercises/practice/high-scores/src/lib.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#[derive(Drop,)]
pub struct HighScores {}

#[generate_trait]
pub impl HighScoresImpl of HighScoresTrait {
fn new(scores: Array<u32>) -> HighScores {
panic!("implement `new`")
}

fn scores(self: @HighScores) -> Span<u32> {
panic!("implement `scores`")
}

fn latest(self: @HighScores) -> Option<u32> {
panic!("implement `latest`")
}

fn personal_best(self: @HighScores) -> Option<u32> {
panic!("implement `personal_best`")
}

fn personal_top_three(self: @HighScores) -> Span<u32> {
panic!("implement `personal_top_three`")
}
}
100 changes: 100 additions & 0 deletions exercises/practice/high-scores/tests/high_scores.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
use high_scores::HighScoresTrait as HighScores;

#[test]
fn list_of_scores() {
let expected: Array<u32> = array![30, 50, 20, 70];
let high_scores = HighScores::new(expected.clone());
assert_eq!(high_scores.scores(), expected.span());
}

#[test]
#[ignore]
fn latest_score() {
let high_scores = HighScores::new(array![100, 0, 90, 30]);
let expected = Option::Some(30);
assert_eq!(high_scores.latest(), expected);
}

#[test]
#[ignore]
fn personal_best() {
let high_scores = HighScores::new(array![40, 100, 70]);
let expected = Option::Some(100);
assert_eq!(high_scores.personal_best(), expected);
}

#[test]
#[ignore]
fn personal_top_three_from_a_list_of_scores() {
let high_scores = HighScores::new(array![10, 30, 90, 30, 100, 20, 10, 0, 30, 40, 40, 70, 70]);
let expected = array![100, 90, 70].span();
assert_eq!(high_scores.personal_top_three(), expected);
}

#[test]
#[ignore]
fn personal_top_highest_to_lowest() {
let high_scores = HighScores::new(array![20, 10, 30]);
let expected = array![30, 20, 10].span();
assert_eq!(high_scores.personal_top_three(), expected);
}

#[test]
#[ignore]
fn personal_top_when_there_is_a_tie() {
let high_scores = HighScores::new(array![40, 20, 40, 30]);
let expected = array![40, 40, 30].span();
assert_eq!(high_scores.personal_top_three(), expected);
}

#[test]
#[ignore]
fn personal_top_when_there_are_less_than_3() {
let high_scores = HighScores::new(array![30, 70]);
let expected = array![70, 30].span();
assert_eq!(high_scores.personal_top_three(), expected);
}

#[test]
#[ignore]
fn personal_top_when_there_is_only_one() {
let high_scores = HighScores::new(array![40]);
let expected = array![40].span();
assert_eq!(high_scores.personal_top_three(), expected);
}

#[test]
#[ignore]
fn latest_score_after_personal_top_scores() {
let high_scores = HighScores::new(array![70, 50, 20, 30]);
let expected = Option::Some(30);
high_scores.personal_top_three();
assert_eq!(high_scores.latest(), expected);
}

#[test]
#[ignore]
fn scores_after_personal_top_scores() {
let expected: Array<u32> = array![30, 50, 20, 70];
let high_scores = HighScores::new(expected.clone());
high_scores.personal_top_three();
assert_eq!(high_scores.scores(), expected.span());
}

#[test]
#[ignore]
fn latest_score_after_personal_best() {
let high_scores = HighScores::new(array![20, 70, 15, 25, 30]);
let expected = Option::Some(30);
high_scores.personal_best().unwrap();
assert_eq!(high_scores.latest(), expected);
}

#[test]
#[ignore]
fn scores_after_personal_best() {
let expected: Array<u32> = array![20, 70, 15, 25, 30];
let high_scores = HighScores::new(expected.clone());
high_scores.personal_best().unwrap();
assert_eq!(high_scores.scores(), expected.span());
}

0 comments on commit 8f9565b

Please sign in to comment.