From 3280230d1bb20a2192e8a73193d7b2c1da92b36a Mon Sep 17 00:00:00 2001 From: Peter Tseng Date: Mon, 9 Sep 2019 11:54:00 +0000 Subject: [PATCH 1/2] two-bucket: return Option Preparing for cases where reaching the goal is impossible. --- exercises/two-bucket/example.rs | 10 +++++----- exercises/two-bucket/src/lib.rs | 4 ++-- exercises/two-bucket/tests/two-bucket.rs | 24 ++++++++++++------------ 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/exercises/two-bucket/example.rs b/exercises/two-bucket/example.rs index 750834143..5979577e0 100644 --- a/exercises/two-bucket/example.rs +++ b/exercises/two-bucket/example.rs @@ -31,7 +31,7 @@ pub struct BucketStats { } /// Solve the bucket problem -pub fn solve(capacity_1: u8, capacity_2: u8, goal: u8, start_bucket: &Bucket) -> BucketStats { +pub fn solve(capacity_1: u8, capacity_2: u8, goal: u8, start_bucket: &Bucket) -> Option { let state = match *start_bucket { Bucket::One => (capacity_1, 0), Bucket::Two => (0, capacity_2), @@ -56,17 +56,17 @@ pub fn solve(capacity_1: u8, capacity_2: u8, goal: u8, start_bucket: &Bucket) -> let (bucket_1, bucket_2) = state; if bucket_1 == goal { - return BucketStats { + return Some(BucketStats { moves, goal_bucket: Bucket::One, other_bucket: bucket_2, - }; + }); } else if bucket_2 == goal { - return BucketStats { + return Some(BucketStats { moves, goal_bucket: Bucket::Two, other_bucket: bucket_1, - }; + }); } // Empty the first bucket diff --git a/exercises/two-bucket/src/lib.rs b/exercises/two-bucket/src/lib.rs index ffeb3a8c1..315adb999 100644 --- a/exercises/two-bucket/src/lib.rs +++ b/exercises/two-bucket/src/lib.rs @@ -17,9 +17,9 @@ pub struct BucketStats { } /// Solve the bucket problem -pub fn solve(capacity_1: u8, capacity_2: u8, goal: u8, start_bucket: &Bucket) -> BucketStats { +pub fn solve(capacity_1: u8, capacity_2: u8, goal: u8, start_bucket: &Bucket) -> Option { unimplemented!( - "Given one bucket of capacity {}, another of capacity {}, starting with {:?}, find pours to reach {}", + "Given one bucket of capacity {}, another of capacity {}, starting with {:?}, find pours to reach {}, or None if impossible", capacity_1, capacity_2, start_bucket, diff --git a/exercises/two-bucket/tests/two-bucket.rs b/exercises/two-bucket/tests/two-bucket.rs index bd1ec1c15..35519842f 100644 --- a/exercises/two-bucket/tests/two-bucket.rs +++ b/exercises/two-bucket/tests/two-bucket.rs @@ -4,11 +4,11 @@ use two_bucket::{solve, Bucket, BucketStats}; fn test_case_1() { assert_eq!( solve(3, 5, 1, &Bucket::One), - BucketStats { + Some(BucketStats { moves: 4, goal_bucket: Bucket::One, other_bucket: 5, - } + }) ); } @@ -17,11 +17,11 @@ fn test_case_1() { fn test_case_2() { assert_eq!( solve(3, 5, 1, &Bucket::Two), - BucketStats { + Some(BucketStats { moves: 8, goal_bucket: Bucket::Two, other_bucket: 3, - } + }) ); } @@ -30,11 +30,11 @@ fn test_case_2() { fn test_case_3() { assert_eq!( solve(7, 11, 2, &Bucket::One), - BucketStats { + Some(BucketStats { moves: 14, goal_bucket: Bucket::One, other_bucket: 11, - } + }) ); } @@ -43,11 +43,11 @@ fn test_case_3() { fn test_case_4() { assert_eq!( solve(7, 11, 2, &Bucket::Two), - BucketStats { + Some(BucketStats { moves: 18, goal_bucket: Bucket::Two, other_bucket: 7, - } + }) ); } @@ -56,11 +56,11 @@ fn test_case_4() { fn goal_equal_to_start_bucket() { assert_eq!( solve(1, 3, 3, &Bucket::Two), - BucketStats { + Some(BucketStats { moves: 1, goal_bucket: Bucket::Two, other_bucket: 0, - } + }) ); } @@ -69,10 +69,10 @@ fn goal_equal_to_start_bucket() { fn goal_equal_to_other_bucket() { assert_eq!( solve(2, 3, 3, &Bucket::One), - BucketStats { + Some(BucketStats { moves: 2, goal_bucket: Bucket::Two, other_bucket: 2, - } + }) ); } From 0ebd2f2ada694be90869b2bec1bfd2fe66441cda Mon Sep 17 00:00:00 2001 From: Peter Tseng Date: Mon, 9 Sep 2019 12:00:38 +0000 Subject: [PATCH 2/2] two-bucket: test inability to reach the goal The student solution would need to employ one of several strategies: * (If searching the state space) Notice that there are no further states to be visited, and yet the solution has not been reached. * Notice that the goal is not divisible by the GCD of the bucket sizes, therefore can be rejected immediately. In case the student assumes that all non-coprime bucket counts will invalidate the goal, a counterexample to that is given as well (buckets not coprime but goal is still possible). There are ten implementing tracks: bash csharp fsharp go java javascript python ruby rust typescript Of these tracks, only two of them (Bash, Go) currently test the condition where it is not possible to reach the goal. Having this test serves as a reminder that it remains wise to handle the situation where a search has not found its goal. https://github.com/exercism/problem-specifications/pull/1580 --- exercises/two-bucket/example.rs | 5 ++++- exercises/two-bucket/tests/two-bucket.rs | 22 ++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/exercises/two-bucket/example.rs b/exercises/two-bucket/example.rs index 5979577e0..658e25285 100644 --- a/exercises/two-bucket/example.rs +++ b/exercises/two-bucket/example.rs @@ -48,7 +48,7 @@ pub fn solve(capacity_1: u8, capacity_2: u8, goal: u8, start_bucket: &Bucket) -> visited.insert((capacity_1, 0)); visited.insert((0, capacity_2)); - loop { + while !next_search.is_empty() { let mut current_search = next_search; next_search = VecDeque::new(); @@ -122,4 +122,7 @@ pub fn solve(capacity_1: u8, capacity_2: u8, goal: u8, start_bucket: &Bucket) -> moves += 1; } + + // We ran out of states to search but still didn't reach the goal. + None } diff --git a/exercises/two-bucket/tests/two-bucket.rs b/exercises/two-bucket/tests/two-bucket.rs index 35519842f..10a4aa095 100644 --- a/exercises/two-bucket/tests/two-bucket.rs +++ b/exercises/two-bucket/tests/two-bucket.rs @@ -76,3 +76,25 @@ fn goal_equal_to_other_bucket() { }) ); } + +#[test] +#[ignore] +fn not_possible_to_reach_the_goal() { + assert_eq!( + solve(6, 15, 5, &Bucket::One), + None + ); +} + +#[test] +#[ignore] +fn with_same_buckets_but_different_goal_then_it_is_possible() { + assert_eq!( + solve(6, 15, 9, &Bucket::One), + Some(BucketStats { + moves: 10, + goal_bucket: Bucket::Two, + other_bucket: 0, + }) + ); +}