From 1e6928c2d396ba0be4bf1ded4ce35e89abfe1898 Mon Sep 17 00:00:00 2001 From: Pablo Miralles Date: Mon, 20 Jul 2026 12:41:04 +0200 Subject: [PATCH] Add cars-assemble concept exercise for if and comparisons. Unlocks if-control-structures and comparison-operators after booleans and numbers, ported from csharp/cars-assemble. Co-authored-by: Cursor --- config.json | 16 ++ .../concept/cars-assemble/.docs/hints.md | 25 +++ .../cars-assemble/.docs/instructions.md | 58 +++++ .../cars-assemble/.docs/introduction.md | 93 ++++++++ .../cars-assemble/.docs/introduction.md.tpl | 5 + .../concept/cars-assemble/.meta/config.json | 20 ++ .../concept/cars-assemble/.meta/design.md | 40 ++++ .../concept/cars-assemble/.meta/exemplar.php | 37 +++ .../concept/cars-assemble/CarsAssemble.php | 19 ++ .../cars-assemble/CarsAssembleTest.php | 210 ++++++++++++++++++ 10 files changed, 523 insertions(+) create mode 100644 exercises/concept/cars-assemble/.docs/hints.md create mode 100644 exercises/concept/cars-assemble/.docs/instructions.md create mode 100644 exercises/concept/cars-assemble/.docs/introduction.md create mode 100644 exercises/concept/cars-assemble/.docs/introduction.md.tpl create mode 100644 exercises/concept/cars-assemble/.meta/config.json create mode 100644 exercises/concept/cars-assemble/.meta/design.md create mode 100644 exercises/concept/cars-assemble/.meta/exemplar.php create mode 100644 exercises/concept/cars-assemble/CarsAssemble.php create mode 100644 exercises/concept/cars-assemble/CarsAssembleTest.php diff --git a/config.json b/config.json index 670d309ad..4159afde5 100644 --- a/config.json +++ b/config.json @@ -82,6 +82,22 @@ ], "status": "beta" }, + { + "slug": "cars-assemble", + "name": "Cars, Assemble!", + "uuid": "4949e5b9-c685-4380-9d70-46ae970035cf", + "concepts": [ + "comparison-operators", + "if-control-structures" + ], + "prerequisites": [ + "booleans", + "integers", + "floating-point-numbers", + "arithmetic-operators" + ], + "status": "beta" + }, { "slug": "windowing-system", "name": "Windowing System", diff --git a/exercises/concept/cars-assemble/.docs/hints.md b/exercises/concept/cars-assemble/.docs/hints.md new file mode 100644 index 000000000..b85916185 --- /dev/null +++ b/exercises/concept/cars-assemble/.docs/hints.md @@ -0,0 +1,25 @@ +# Hints + +## General + +- Review the [comparison operators][comparison-operators] and [control structures][if-statement] documentation. + +## 1. Calculate the success rate + +- Determining the success rate can be done through a [conditional statement][if-statement]. +- Numbers can be compared using the built-in [comparison operators][comparison-operators]. + +## 2. Calculate the production rate per hour + +- Use the `CarsAssemble.successRate()` method you wrote earlier to determine the success rate. +- PHP allows multiplication between integers and floating-point numbers. + The result will be a floating-point number when either operand is a float. + +## 3. Calculate the number of working items produced per minute + +- Converting a floating-point number to an integer discards the fractional part (truncation toward zero). +- You can cast to an integer with `(int)` or use [`intval()`][intval]. + +[comparison-operators]: https://www.php.net/manual/en/language.operators.comparison.php +[if-statement]: https://www.php.net/manual/en/control-structures.if.php +[intval]: https://www.php.net/manual/en/function.intval.php diff --git a/exercises/concept/cars-assemble/.docs/instructions.md b/exercises/concept/cars-assemble/.docs/instructions.md new file mode 100644 index 000000000..616e004c0 --- /dev/null +++ b/exercises/concept/cars-assemble/.docs/instructions.md @@ -0,0 +1,58 @@ +# Instructions + +In this exercise you'll be writing code to analyze the production of an assembly line in a car factory. +The assembly line's speed can range from `0` (off) to `10` (maximum). + +At its lowest speed (`1`), `221` cars are produced each hour. +The production increases linearly with the speed. +So with the speed set to `4`, it should produce `4 * 221 = 884` cars per hour. +However, higher speeds increase the likelihood that faulty cars are produced, which then have to be discarded. + +You have three tasks. + +## 1. Calculate the success rate + +Implement the `CarsAssemble.successRate()` method to calculate the ratio of an item being created without error for a given speed. +The following table shows how speed influences the success rate: + +- `0`: 0% success rate. +- `1` to `4`: 100% success rate. +- `5` to `8`: 90% success rate. +- `9`: 80% success rate. +- `10`: 77% success rate. + +```php +successRate(10); +// => 0.77 +``` + +## 2. Calculate the production rate per hour + +Implement the `CarsAssemble.productionRatePerHour()` method to calculate the assembly line's production rate per hour, taking into account its success rate: + +```php +productionRatePerHour(6); +// => 1193.4 +``` + +Note that the value returned is a floating-point number. + +## 3. Calculate the number of working items produced per minute + +Implement the `CarsAssemble.workingItemsPerMinute()` method to calculate how many working cars are produced per minute: + +```php +workingItemsPerMinute(6); +// => 19 +``` + +Note that the value returned is an integer. diff --git a/exercises/concept/cars-assemble/.docs/introduction.md b/exercises/concept/cars-assemble/.docs/introduction.md new file mode 100644 index 000000000..c8712fd8d --- /dev/null +++ b/exercises/concept/cars-assemble/.docs/introduction.md @@ -0,0 +1,93 @@ +# Introduction + +## Comparison Operators + +PHP has ten built in comparison operators: + +| Name | Example | Result | +| ----- | ---------- | -------------------------------------------------- | +| Equal | `$a == $b` | true if `$a` is equal to `$b` after type juggling. | +| Identical | `$a === $b` | true if `$a` is equal to `$b` and the same type. | +| Not Equal | `$a != $b` | true if `$a` is not equal to `$b` after type juggling. | +| Not Equal | `$a <> $b` | true if `$a` is not equal to `$b` after type juggling. | +| Not identical | `$a !== $b` | true if `$a` is not equal to `$b` or not of the same type. | +| Less than | `$a < $b` | true if `$a` is strictly less than `$b`. | +| Greater than | `$a > $b` | true if `$a` is strictly greater than `$b`. | +| Less than or equal to | `$a <= $b` | true if `$a` is less than or equal to `$b`. | +| Greater than or equal to | `$a >= $b` | true if `$a` is greater than or equal to `$b`. | +| Spaceship | `$a <=> $b` | returns an integer less than, equal to, or greater than `0`i when `$a`. | + +PHP has distinct definitions that differentiate between `equal` and `identical`. +Sometimes `identical` is also referred to as `strictly equal`. + +```php + true, equal +1 === "1"; // => false, not identical + +// Comparisons between integers and floating point values +1 == 1.0; // => true +1 === 1.0; // => false + +// Comparisons between object instances +new stdClass() == new stdClass(); // => true, properties are equal +new stdClass() === new stdClass(); // => false, references are not identical +``` + +## If, Else, Elseif + +Conditional statements using `if`, `elseif`, and `else` are a fundamental parts of program control flow. +The `if` statement evaluates an expression, and if `true`, will execute the code branch. + +```php +`, `<=`, `>=`). +- Know the difference between equal (`==`) and identical (`===`) comparisons. +- Know how to conditionally execute code using `if` statements. +- Know how to chain conditions with `elseif` / multiple `if` statements. +- Know how to convert a floating-point number to an integer by truncation. + +## Out of scope + +- `switch` / `match` expressions. +- The ternary operator. +- Type declarations. +- `declare(strict_types=1)`. + +## Concepts + +- `comparison-operators`: know how to compare values; know the difference between equal and identical comparisons. +- `if-control-structures`: know how to conditionally execute code using `if` / `elseif` / `else`. + +## Prerequisites + +- `booleans`: know how to use boolean values and operators. +- `integers`: know how to work with whole numbers. +- `floating-point-numbers`: know how to work with floating-point numbers. +- `arithmetic-operators`: know how to multiply and divide numbers. + +## Analyzer + +This exercise could benefit from the following rules: + +- `actionable`: If the student did not reuse `successRate` inside `productionRatePerHour`, instruct them to do so. +- `informative`: If the solution repeatedly hard-codes `221`, suggest storing it in a constant. +- `informative`: If the solution uses `if`/`elseif` with early returns, note that trailing `else` branches may be redundant. diff --git a/exercises/concept/cars-assemble/.meta/exemplar.php b/exercises/concept/cars-assemble/.meta/exemplar.php new file mode 100644 index 000000000..9bd062f55 --- /dev/null +++ b/exercises/concept/cars-assemble/.meta/exemplar.php @@ -0,0 +1,37 @@ += 5) { + return 0.9; + } + + if ($speed >= 1) { + return 1.0; + } + + return 0.0; + } + + public function productionRatePerHour($speed) + { + return self::CARS_PER_HOUR * $speed * $this->successRate($speed); + } + + public function workingItemsPerMinute($speed) + { + return (int) ($this->productionRatePerHour($speed) / 60); + } +} diff --git a/exercises/concept/cars-assemble/CarsAssemble.php b/exercises/concept/cars-assemble/CarsAssemble.php new file mode 100644 index 000000000..cb5b5c182 --- /dev/null +++ b/exercises/concept/cars-assemble/CarsAssemble.php @@ -0,0 +1,19 @@ +successRate(0); + $this->assertEqualsWithDelta(0.0, $actual, 0.001); + } + + /** + * @task_id 1 + */ + #[TestDox('Success rate for speed 1')] + public function testSuccessRateForSpeedOne() + { + $assembly_line = new CarsAssemble(); + $actual = $assembly_line->successRate(1); + $this->assertEqualsWithDelta(1.0, $actual, 0.001); + } + + /** + * @task_id 1 + */ + #[TestDox('Success rate for speed 4')] + public function testSuccessRateForSpeedFour() + { + $assembly_line = new CarsAssemble(); + $actual = $assembly_line->successRate(4); + $this->assertEqualsWithDelta(1.0, $actual, 0.001); + } + + /** + * @task_id 1 + */ + #[TestDox('Success rate for speed 5')] + public function testSuccessRateForSpeedFive() + { + $assembly_line = new CarsAssemble(); + $actual = $assembly_line->successRate(5); + $this->assertEqualsWithDelta(0.9, $actual, 0.001); + } + + /** + * @task_id 1 + */ + #[TestDox('Success rate for speed 9')] + public function testSuccessRateForSpeedNine() + { + $assembly_line = new CarsAssemble(); + $actual = $assembly_line->successRate(9); + $this->assertEqualsWithDelta(0.8, $actual, 0.001); + } + + /** + * @task_id 1 + */ + #[TestDox('Success rate for speed 10')] + public function testSuccessRateForSpeedTen() + { + $assembly_line = new CarsAssemble(); + $actual = $assembly_line->successRate(10); + $this->assertEqualsWithDelta(0.77, $actual, 0.001); + } + + /** + * @task_id 2 + */ + #[TestDox('Production rate per hour for speed 0')] + public function testProductionRatePerHourForSpeedZero() + { + $assembly_line = new CarsAssemble(); + $actual = $assembly_line->productionRatePerHour(0); + $this->assertEqualsWithDelta(0.0, $actual, 0.1); + } + + /** + * @task_id 2 + */ + #[TestDox('Production rate per hour for speed 1')] + public function testProductionRatePerHourForSpeedOne() + { + $assembly_line = new CarsAssemble(); + $actual = $assembly_line->productionRatePerHour(1); + $this->assertEqualsWithDelta(221.0, $actual, 0.1); + } + + /** + * @task_id 2 + */ + #[TestDox('Production rate per hour for speed 4')] + public function testProductionRatePerHourForSpeedFour() + { + $assembly_line = new CarsAssemble(); + $actual = $assembly_line->productionRatePerHour(4); + $this->assertEqualsWithDelta(884.0, $actual, 0.1); + } + + /** + * @task_id 2 + */ + #[TestDox('Production rate per hour for speed 7')] + public function testProductionRatePerHourForSpeedSeven() + { + $assembly_line = new CarsAssemble(); + $actual = $assembly_line->productionRatePerHour(7); + $this->assertEqualsWithDelta(1392.3, $actual, 0.1); + } + + /** + * @task_id 2 + */ + #[TestDox('Production rate per hour for speed 9')] + public function testProductionRatePerHourForSpeedNine() + { + $assembly_line = new CarsAssemble(); + $actual = $assembly_line->productionRatePerHour(9); + $this->assertEqualsWithDelta(1591.2, $actual, 0.1); + } + + /** + * @task_id 2 + */ + #[TestDox('Production rate per hour for speed 10')] + public function testProductionRatePerHourForSpeedTen() + { + $assembly_line = new CarsAssemble(); + $actual = $assembly_line->productionRatePerHour(10); + $this->assertEqualsWithDelta(1701.7, $actual, 0.1); + } + + /** + * @task_id 3 + */ + #[TestDox('Working items per minute for speed 0')] + public function testWorkingItemsPerMinuteForSpeedZero() + { + $assembly_line = new CarsAssemble(); + $actual = $assembly_line->workingItemsPerMinute(0); + $this->assertSame(0, $actual); + } + + /** + * @task_id 3 + */ + #[TestDox('Working items per minute for speed 1')] + public function testWorkingItemsPerMinuteForSpeedOne() + { + $assembly_line = new CarsAssemble(); + $actual = $assembly_line->workingItemsPerMinute(1); + $this->assertSame(3, $actual); + } + + /** + * @task_id 3 + */ + #[TestDox('Working items per minute for speed 5')] + public function testWorkingItemsPerMinuteForSpeedFive() + { + $assembly_line = new CarsAssemble(); + $actual = $assembly_line->workingItemsPerMinute(5); + $this->assertSame(16, $actual); + } + + /** + * @task_id 3 + */ + #[TestDox('Working items per minute for speed 8')] + public function testWorkingItemsPerMinuteForSpeedEight() + { + $assembly_line = new CarsAssemble(); + $actual = $assembly_line->workingItemsPerMinute(8); + $this->assertSame(26, $actual); + } + + /** + * @task_id 3 + */ + #[TestDox('Working items per minute for speed 9')] + public function testWorkingItemsPerMinuteForSpeedNine() + { + $assembly_line = new CarsAssemble(); + $actual = $assembly_line->workingItemsPerMinute(9); + $this->assertSame(26, $actual); + } + + /** + * @task_id 3 + */ + #[TestDox('Working items per minute for speed 10')] + public function testWorkingItemsPerMinuteForSpeedTen() + { + $assembly_line = new CarsAssemble(); + $actual = $assembly_line->workingItemsPerMinute(10); + $this->assertSame(28, $actual); + } +}