Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add practice exercise Zebra Puzzle #788

Merged
merged 11 commits into from
Apr 23, 2024
Merged

Conversation

rajatnai49
Copy link
Contributor

issue number: #779

@github-actions github-actions bot closed this Jan 29, 2024
@vaeng vaeng reopened this Jan 29, 2024
Copy link
Contributor

@vaeng vaeng left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a programmatic way of solving this puzzle.

You can define enums, functions and other more complicated testing scenarios to achieve this goal.

@rajatnai49
Copy link
Contributor Author

Okay I will work on that👍

@vaeng
Copy link
Contributor

vaeng commented Feb 1, 2024

Thanks for the update!

Do you think we could have the details of the story in the test file? So the result is not always the same and students cannot just answer "Norwegian"?

So that we have a custom test case, where we give another story? I will ask the other maintainers if they did that. What do you think @siebenschlaefer ?

It is possible a bigger rewrite and I think you should get more than the standard 12 reputation for that, if you want to continue.

@siebenschlaefer
Copy link
Contributor

Hej @rajatnai49,

I'm a little bit torn. The example solution still contains a lot of "human reasoning", e.g. why the first house is yellow is deduced from facts 6, 15, and 2 and then hard-coded (houses[0].color = YELLOW;)
I would prefer an example solution that does that as little as possible, let your program find the answer.

For example, you could use five nested loops (for the nationality, the color, ...), try all permutations, and skip a particular permutation as soon as possible when it doesn't match the facts. Or you could try to implement a completely different approach, that's up to you.
If you need inspiration you could look at the solutions of this exercise in other tracks or at rosettacode.
And if you need help, let me know, either here or on our Discord server.

And two small things:

  • Why are drinks_water() and owns_zebra() member functions of a class? Unless that class has some state I'd suggest defining them as free functions instead.
  • For constants use const or constexpr variables instead of macros. That's a broadly accepted best practice.

@rajatnai49
Copy link
Contributor Author

Sorry @siebenschlaefer for disappointment😞, and I know that all permutation solution but I think it will be good if we just first remove that and after that try every possible cases for perticular conditions, and it is much efficient than trying all permutation
But, I understood your point that you want code to do every work, so fine I will work
And also I will take note of those practices this time around!!

@siebenschlaefer siebenschlaefer mentioned this pull request Feb 3, 2024
11 tasks
@siebenschlaefer
Copy link
Contributor

Sorry @siebenschlaefer for disappointment😞

No worries, please! As vaeng wrote earlier this is not an easy exercise. And maybe we maintainer should have communicated more clearly what kind of example solutions we're looking for.

And if you need any help, let us know.

@vaeng vaeng added x:module/practice-exercise Work on Practice Exercises x:rep/large Large amount of reputation labels Feb 12, 2024
@siebenschlaefer
Copy link
Contributor

@rajatnai49 Are you still working on this? Is there anything we can do to help you?

@rajatnai49
Copy link
Contributor Author

Hey, @siebenschlaefer sorry for delay but we are organizing the Hackathon in the university so I didn't have much time to work on the issue so I will give you update in next week for sure!!

@rajatnai49
Copy link
Contributor Author

@siebenschlaefer I added the solution as you explained earlier, review that and if any other requirements are there then let me know !

@siebenschlaefer
Copy link
Contributor

siebenschlaefer commented Mar 5, 2024

Much better, this is now an algorithmic solution.


But it takes a really long time to run because it tries all potential solutions and finds the correct solution after more than 171 million permutations. On my computer it runs in a a little bit more than one minute. Students wouldn't be able to submit this example solution because it would not complete the tests within the allowed time (I think the test runner has a limit of 20 or 30 seconds.)
We need to bring that time down because we want an example solution that could be successfully submitted.

There are five nested loops (for the cigarettes, drinks, pets, nationalities, and colors). Currently the innermost loop assigns values to the members of the houses and checks whether that's a valid solution.

Instead you could assign assign the values to the houses as soon as possible, i.e. the .cigarette in the outer loop, the .drink in the first inner loop, the .pet in the second inner loop, the .nationality in the third inner loop, and the .color in the innermost loop. That alone cuts the runtime in half (at least on my computer).

Instead of checking the complete solution in the innermost loop you could check each of the 14 clauses as soon as possible and continue if they don't hold. For example the outermost loop creates permutations of the cigarette brands, the first inner loop creates permutations of the drinks. You could check clauses 8 ("Milk is drunk in the middle house") and 12 ("The Lucky Strike smoker drinks orange juice.") right there, in the first inner loop. That would allow you to skip generating all the permutations of pets, nationalities, and colors when you know that the will never create a valid solution because houses[2].drink is not Milk or because the Lucky Strike smoker does not drink OJ. If you do that for all clauses (check each clause in the outermost possible loop) you can reduce the runtime drastically, on my computer it runs in under one second.


And some smaller nitpicks:

Four of the five std::map instances are not used at all. I'd suggest deleting them. That makes the code easier to read and to maintain.

As far as I can see the return statement in line 159 is the only one that is not indented by a multiple of four spaces.


And finally a more general question where @vaeng might also want to weigh in:

Currently the tests require two separate and free functions that answer the two questions. But is that really the best structure for this problem? It basically forces the students to write some helper function that solves the problem and gets called by the two "public" functions, and it will force the test runner to solve the puzzle twice (unless the students implement some caching) without a clear benefit.

How about a single function that returns both answers at the same time? That's how the Go track does it.

vaeng
vaeng previously requested changes Mar 5, 2024
exercises/practice/zebra-puzzle/.meta/config.json Outdated Show resolved Hide resolved
@vaeng
Copy link
Contributor

vaeng commented Mar 5, 2024

How about a single function that returns both answers at the same time? That's how the Go track does it.

We could also go the class way and generate the puzzle solution on construction and have the answers in variables ... Might be ugly :D

@exercism exercism deleted a comment from github-actions bot Mar 5, 2024
@siebenschlaefer
Copy link
Contributor

We could also go the class way and generate the puzzle solution on construction and have the answers in variables ... Might be ugly :D

Right, that's another option. (although personally I'm not a fan)

@rajatnai49
Copy link
Contributor Author

@siebenschlaefer totally understood ☺️, working on that. and both single function or class construction approaches are great because there is a only one particular answer for the zebra puzzle, so solving puzzle two time, like in the python track will not be add any value, So let me know on which path we are going.

@vaeng
Copy link
Contributor

vaeng commented Mar 6, 2024

So let me know on which path we are going.

a single function that returns both answers at the same time.

@rajatnai49 rajatnai49 requested a review from vaeng March 12, 2024 13:33
@vaeng vaeng dismissed their stale review March 12, 2024 13:42

problem has been solved, new review needed with all the changes.

@rajatnai49
Copy link
Contributor Author

@vaeng @siebenschlaefer
can you please give me the status/review of this PR, anything required?

Copy link
Contributor

@siebenschlaefer siebenschlaefer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good!

There are a few smaller things that I'd like to improve before merging.

The only potential problem that I can see is that the tests use SECTION which does not map directly to the two tests in the problem specification. The test name "drinks water" and UUID does not reflect that combined nature of the tests. I'd rather have two separate and independent tests.

@vaeng what do you think?

config.json Outdated
"uuid": "2badf89b-efc4-46f1-af8d-b7d2440e61c0",
"practices": [],
"prerequisites": [],
"difficulty": 5
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I would give this a higher difficulty, maybe 7 or 8.
@vaeng What do you think?

@@ -0,0 +1,244 @@
#include <algorithm>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please #include the associated .h file in your .cpp file, in this case #include "zebra_puzzle.h".
That helps keeping these two files in sync, and you don't need to re-define any types that are already defined in the .h file.

{
string drinksWater;
string ownsZebra;
} Solution;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you '#includethe.h` file you won't need to repeat this definition (see above).

{
std::string drinksWater;
std::string ownsZebra;
} Solution;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typedef struct { ... } type_name; is a C idiom.
In C++ you can write struct type_name { ... };, just like you did with House in the .cpp file.

# is regenerated, comments can be added via a `comment` key.

[16efb4e4-8ad7-4d5e-ba96-e5537b66fd42]
description = "solve the zebra puzzle and answer who drinks water and who owns zebra"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file should describe which tests from the language-independent problem specification are implemented in this track, the UUID and description should match those from the JSON file.
The zebra_puzze_tests.cpp implements both tests ("resident who drinks water" and "resident who owns zebra") so this TOML file should reflect that.


#include <string>

using namespace std;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While even some C++ programmers like to have this using namespace std; in their .cpp files most C++ experts recommend not to do that. I'd suggest removing that line, we don't want to promote bad practices.

@@ -0,0 +1,21 @@
#include "zebra_puzzle.h"

#include <string>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not necessary, <string> is already included in the .h file.

{
std::string drinksWater;
std::string ownsZebra;
} Solution;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

*If* you want to define that type for our students, do that in the .h file where it is required.
(and without the typedef idiom, see above.)

Solution solve()
{
// IMPLEMENT YOUR LOGIC HERE
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want to provide a function stub for the students as a hint which function they must implement, and what it returns, declare it in the .h file, like in the other exercises that do this.

@vaeng
Copy link
Contributor

vaeng commented Apr 18, 2024

@rajatnai49
The exercise is going to be featured in two weeks. Can you cover siebenschläfer's suggestions in the next days, so we can have it online soon?

Copy link
Contributor

@siebenschlaefer siebenschlaefer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. Thank you very much!

@vaeng
Copy link
Contributor

vaeng commented Apr 19, 2024

Clang is not happy with the formatting

@vaeng vaeng merged commit b726835 into exercism:main Apr 23, 2024
8 checks passed
@vaeng
Copy link
Contributor

vaeng commented Apr 23, 2024

This is it. Thaks a lot for all the work in this exercise @siebenschlaefer and especially @rajatnai49!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
x:module/practice-exercise Work on Practice Exercises x:rep/large Large amount of reputation
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants