Skip to content

Idea for new Exercise with Random Numbers #616

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

Closed
sleininger opened this issue Feb 28, 2017 · 11 comments
Closed

Idea for new Exercise with Random Numbers #616

sleininger opened this issue Feb 28, 2017 · 11 comments

Comments

@sleininger
Copy link

Hello,

I'm very new to this project, and have a lot more exploring to do. But I love what exercism does, and plan on using it to better myself in the future. I am also appreciative that you are welcoming to new contributors.

I am a CS student and consider myself to be pretty new to development. I was looking through the available exercises in languages I'm familiar with, and didn't notice any that used random numbers, which was something valuable I learned about in my intro classes. We made programs to simulate rolling dice, and I'll attach one I made in C++.

It asks the user for the number of dice they'd like to roll, number of faces each dice should have, and how many times they'd like to roll. This would give someone experience with nested for loops and with generating random numbers, and is a very simple program for a beginner.

#include <iostream>
#include <cstdlib>
using namespace std;

/****************************************************
Function: rollDice
Parameters: integer number of faces for the dice
Returns: Random roll between 1 and number of faces
*****************************************************/
int rollDice(int numFaces) {
   int roll;

   roll = rand() % numFaces + 1;
   return roll;
}

int main()
{
   int numRolls, numFaces, numDice, roll;

   srand(time(NULL));

   cout << "How many dice would you like to roll?" << endl;
   cin >> numDice;
   cout << "How many sides would you like these dice to have?" << endl;
   cin >> numFaces;

   cout << "How many times would you like to roll the dice?" << endl;
   cin >> numRolls;

   for (int count = 1; count <= numDice; count++)
   {
      cout << "Dice " << count << ": " << endl;
      for(int i = 1; i <= numRolls; i++) {
         roll = rollDice(numFaces);
         cout << "Roll " << i << ": " << roll << endl;
      }
      cout << "\n";
   }

   return 0;
}

So in the ReadMe it would ask the student to create a program the simulates rolling dice. It should get the number of dice, number of faces, and number of rolls from the user. Then it should display the randomized results for each dice for the user.

I hope this is the correct place for suggestions like this. Thank you!

@rbasso
Copy link
Contributor

rbasso commented Feb 28, 2017

I guess we just have robot-name, that mixes random string generation and persistent states, and simple-cipher, which demands the use of a randomly generated key if none is provided.

@ErikSchierboom
Copy link
Member

Although dealing with randomness is a great subject, I don't necessarily think it makes for a great exercise. Part of the problem of randomness is that it often is quite hard to write tests for it. It is not uncommon to have tests fail, well, randomly! Perhaps you could further expand on how you would want the tests to look like?

@masters3d
Copy link
Contributor

masters3d commented Mar 21, 2017

I've like a new random problem to replace robot name. It would be great if it included a way to create different kinds of serial numbers like book SKUs or different length or formats.

@sleininger
Copy link
Author

That’s a good point about randomness being difficult to test for, @ErikSchierboom. In my mind I assumed that the tests would just check that the number generated was within the range that the programmer wanted it to be. So if they created a regular 6-sided dice, then the test would make sure the roll was between 1 and 6. But I do see how creating a whole exercise just for that may seem like a waste of time.

I remember how confused I was when I first learned about generating random numbers, which is why I think it has the potential to be a helpful topic for new programmers. A way to make it a more in depth exercise may be to ask them to create a dice where the smallest side is not 1, so it can help show how numbers can be within a certain range. Something like this for a second type of dice:

int rollDiceRange(int min, int max) {
   int roll;

   roll = rand() % max + min;
   return roll
}

Another option would be to have each side of the dice be multiples of a specific number, so have them create a dice where each side will have a value that is a multiple of 10. It would look something like this:

int rollDiceDiv(int max, int div) {
   int roll;

   roll = (rand() % max + 1) * div;
   return roll;
}

This may make the exercise more worthwhile and give the person learning some more tools to use in the future.

I wrote some Unit tests that would test each of the 3 dice roll functions in these comments, and I tried to model them after the ones for other exercises on the website. I haven’t used Boost Test before so I apologize if these are sloppy or could be written better.

Here they are:

#define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp>
#include dice.hpp 

using namespace std;

BOOST_AUTO_TEST_CASE(test_dice_range) {
	//Test a roll with a normal 6-sided dice
	BOOST_TEST(1 <= dice::rollDice(6) <= 6);

	//Test with more sides
	BOOST_TEST(1 <= dice::rollDice(20) <= 20);

	//Test a coin flip
	BOOST_TEST(1 <= dice::rollDice(2) <= 2);

	//Maybe test a bunch of rolls on one dice to see if it is consistent
	for(int i = 0; i < 100; i++) {
		BOOST_TEST(1 <= dice::rollDice(10) <= 10);
	}
}

BOOST_AUTO_TEST_CASE(test_dice_range_min) {
	//Test small range and small numbers
	BOOST_TEST(3 <= dice::rollDiceRange(3, 6) <= 6);

	//Test larger numbers
	BOOST_TEST(500 <= dice::rollDiceRange(500, 100000) <= 100000);

	//Test multiple rolls for consistency
	for(int i = 0; i < 100; i++) {
		BOOST_TEST(20 <= dice::rollDiceRange(20, 100) <= 100);
	}

}

BOOST_AUTO_TEST_CASE(test_dice_div) {
	//Divisible by ten
	BOOST_TEST(dice::rollDiceDiv(6, 10) % 10 == 0);

	//Divisible by larger number
	BOOST_TEST(dice::rollDiceDiv(10, 24) % 24 == 0);

	//Even number
	BOOST_TEST(dice::rollDiceDiv(15, 2) % 2 == 0);
}

Maybe there is a better object to use than a dice for this type of exercise, I just haven’t thought of one.

I thought about what you said about generating SKUs, @masters3d, but I feel like that would fall into the category of generating numbers within a certain range. Or maybe there could be an exercise that covers left and right zero padding and other formatting options. Like inserting a “.” every 5 characters, having output print to a specific side of the screen, etc. I think you are onto a good idea with that!

@masters3d
Copy link
Contributor

masters3d commented Apr 24, 2017

Cross post: #731 (comment)

Electronic dog collars:
The first time you boot them up, a random name is generated in the format of a dog name chosen from a list followed by three random digits, eg FIDO837 or SPOT811.

Every once in a while we need to reset a collar to its factory settings, which means that their name gets wiped. The next time you ask, it will respond with a new random name.

The names must be random: they should not follow a predictable sequence. Random names means a risk of collisions. Your solution should not allow the use of the same name twice when avoidable.

List of dog names: 'FIDO', 'SPOT', 'ROVER' etc..

(And something about preventing multiple dogs in the neighbourhood having the same name if you want that constraint as well.)

@masters3d
Copy link
Contributor

@sleininger Are you interested in putting together this problem? See #731

@sshine
Copy link
Contributor

sshine commented Nov 9, 2018

You can test random functions either by property-based testing or by mocking the generator.

For example, if the exercise is to create a Dungeons & Dragons character generator, and the dice rules for determining your character's stats are "Roll 4 six-sided dice, keep the three highest", then one property is that each character stat must be within the interval 3-18. Another property is that given three random dice d1, d2, d3 and a fourth die d4 such that d4 <= d1, d4 <= d2, d4 <= d3, stat([d1, d2, d3, d4]) == d1 + d2 + d3. (This requires separating the dice choosing from the dice rolling.) And to show that the lowest die was discarded by mocking instead, mock the generator with (1, 1, 1, 2), (1, 1, 2, 1), (1, 2, 1, 1) and (2, 1, 1, 1) and show that it always yields the sum 4 and not 3.

I don't know how to encode this in canonical data, though.

Perhaps by comments rather than specific unit test values.

I don't know if all tracks support mocking a generator or property-based testing libraries. But I don't think that should be a reason to leave out exercises with randomness for languages that handle this well.

As for learning goals, besides getting acquainted with your language's random generator (a huge joy), learning how to separate side effects from your core logic is something that most (imperative) languages rarely encourage you to do. So learning how to write testable, random code is a pretty cool goal, too.

Still, if there are no concrete suggestions, perhaps this issue should be closed?

@sleininger: I'm open to cooperate with you on creating a randomness exercise, but we need a theme. :)

@ErikSchierboom
Copy link
Member

if the exercise is to create a Dungeons & Dragons character generator

Love this idea! We can test for random numbers, but also have the system generate random names.

I don't know how to encode this in canonical data, though.

There is some precendence, see this test case.

we need a theme

I'd say go with the D&D generator. It's a really practical example that a lot of people will be familiar with I think.

@sshine
Copy link
Contributor

sshine commented Nov 9, 2018

D&D stat rolling may be a good idea for a first randomness exercise, since it's just random numbers and how to isolate and test side effects.

But I'd love if there were an exercise that involved generating mazes or dungeons. For mazes the testable property can be that the maze is connected a certain way (e.g. planar, or fully within a square). For dungeons the property can be that generator's size parameter is equal to the number of rooms in the dungeon.

I'll submit a PR to the D&D idea on Monday. :)

@ErikSchierboom
Copy link
Member

But I'd love if there were an exercise that involved generating mazes or dungeons.

Sounds interesting. May be a good follow up exercise from the D&D one.

emcoding pushed a commit that referenced this issue Nov 19, 2018
@ErikSchierboom
Copy link
Member

This issue can be closed as it resulted in the additional of the dnd-character exercise, which focuses on random number generation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants