Forfeit game - coding contest since 50 years
The game of forfeits is a simple game and one of the classic programming examples since 50 years. I found it in a ALGOL 60 exercise book from 1963: Count the numbers up to 100, but skip all numbers if one of the condition matches:
- number is divisible by seven
- number contains a seven
- sum of digits is divisible by seven
- sum of digits contains a seven
Develop a function which calculates all numbers based on the given conditions. The only one parameter for the function is the range and the function should return the Array of all valid numbers. Hint: You must not cheat while returning a sliced constant result array. The result of the function with max=100 is: [1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 15, 18, 19, 20, 22, 23, 24, 26, 29, 30, 31, 32, 33, 36, 38, 39, 40, 41, 44, 45, 46, 48, 50, 51, 53, 54, 55, 58, 60, 62, 64, 65, 66, 69, 80, 81, 82, 83, 85, 88, 90, 92, 93, 94, 96, 99, 100].
clean code variant
Matthia's has delivered a clean code variant for the problem and it will be used as the reference for this coding contest. It's not the fastest solution but offers a direct implementation of the problem. Matthias uses an extra function for every condition and put it together in the main loop.
for (var i=0, res=[]; i <= n; i++) {
if (!(containsSeven(i) || isMultipleOfSeven(i) ||
sumContainsSeven(i) || sumIsMultipleOfSeven(i))) {
res.push(i);
}
}
return res;
//all helper functions defined in local scope
function containsSeven (n) {
return String(n).indexOf("7") !== -1;
};
function isMultipleOfSeven (n) {
return n % 7 === 0;
};
function sumContainsSeven (n) {
return containsSeven(sumOfDigits(n));
};
function sumIsMultipleOfSeven (n) {
return isMultipleOfSeven(sumOfDigits(n));
} ;
function sumOfDigits (n) {
var sum = 0;
while (n > 0) {
sum += n % 10;
n = Math.floor(n / 10);
}
return sum;
};
}
If you are surprised about using the helper function before defining, read more about function defining the the In-depth guide to javascript functions.
Testing your solution
In the following textbox you can test your solution by pasting your javascript function body direct into it. It will be compared to Matthias' clean code version and a performance optimized short clean code version, which works without functions.
/* game of forfeits var number {n} numbers to check return list of number */ function mySolution(n) {
};
You can include my javascript test suite and add your solution. The benchmark will test your function only with n=100
.
- <script src="http://united-coders.com/sites/default/files/JSLitmus.js.txt" type="text/javascript"></script>
- <script src="http://united-coders.com/sites/default/files/forfeits.js.txt" type="text/javascript"></script>
- <script type="text/javascript">
- JSLitmus.test("clean code version", com.unitedCoders.forfeits.testClean);
- JSLitmus.test("short version", com.unitedCoders.forfeits.testShort);
- if (com.unitedCoders.forfeits.runTest(myFunction) {
- JSLitmus.test("your version", function() { myFunction(100); } );
- }
- </javascript>
more sultions
In older articles (javascript lotto generator or articles with code puzzles tag), here on united-coders.com we linked articles and got comments with solutions in other languages. Feel free to comment or use our tackbacks, but only javascript have the possibility to run directly in the browser.
I will post in the second article the original ALGOL 60 solutions and my 1:1 migration to the javascript variant to benchmark it with today's coding skills. If we got some interesting solutions the third article will comment, link and benchmark all.
Attachment | Size |
---|---|
forfeits.js.txt | 2.97 KB |
- Login to post comments
Comments
azendal (not verified) - Wed, 11/10/2010 - 06:33
azendal (not verified) - Wed, 11/10/2010 - 06:36
Christian Harms - Wed, 11/10/2010 - 19:33
Thanx - this variant optimize the big check and is better by factor 2 than the clean code variant.
Keeto (not verified) - Wed, 11/10/2010 - 14:20
Keeto (not verified) - Wed, 11/10/2010 - 14:30
Did I win the slowest award? :D
Christian Harms - Wed, 11/10/2010 - 19:44
Hmmm, regular expressions are not the best choice for a performance programming contest. But the usage of reduce to get the checksum is fine!
droope (not verified) - Wed, 11/10/2010 - 15:29
asda this is how i would have done it :)
almost the same
Matthew (not verified) - Wed, 11/10/2010 - 20:53
http://tinyurl.com/2fgmwrr
Christian Harms - Wed, 11/10/2010 - 23:35
Hi Matthew, your solutions works but is slower than the clean code version. Try to make smarter code. We have some articles here on the blog (JavaScript types, defining functions and type-safe functions) and I will give you some improvements/hints:
line 3,4,...: ALWAYS add "var " before defining (=first usage) a variable - if not the variable is defined in global scope. This means after calling calling checkDig "num" and "numArray" is available in every other function!
line 7-22: Use && or || to combine conditions!
line 23: Adding an item to a list - use the method Array.push instead of incrementing the index and assigning to the current position
line 34: Never just return true or false in the body of an IF-statement
line 34, 49: if both terms are with the same type, use type-hard comperator "===" instead "==" (see more on All about types)
line 76: never forget the second parameter for parseInt(str, radix) - see w3scools.
line 58, 69: both function have the same code except the last line - use a function to calculate the sumOfDigit.
sud (not verified) - Thu, 11/11/2010 - 03:16
Just for kicks, I implemented the solution in Java using the Decorator Pattern for good OO design.
https://github.com/sudr/sandbox/tree/master/forfeit/
MSM (not verified) - Thu, 11/11/2010 - 06:29
I can't seen to make it run in your test harness, but when I run it in the page shown at https://gist.github.com/672058 it appears to return the correct array.
MSM (not verified) - Thu, 11/11/2010 - 14:53
Same thing, only refactoring all the return statements. Meh.
Christian Harms - Thu, 11/11/2010 - 22:44
MSM, simply add the following line with your function in the textbox. Your function is called recursive and it have to defined in the local scope of the test function.
But it has the same runetime like the clean code variant.
MSM (not verified) - Fri, 11/12/2010 - 01:00
Significant differences between JS runtimes, it appears.
Looks like the Webkit JS runtime has some issues with recursion. I'm not up on the differences in JS interpreters, though I'm impressed with V8 in general here.
MSM (not verified) - Fri, 11/12/2010 - 23:29
I took the array concatenation out of my function and replaced it with a push. Unfortunately, that meant adding a variable assignment. Oh, well. Purity falls to practicality.
This resulted in speedups in all engines:
I think I'm done with this now.
Ian (not verified) - Thu, 11/11/2010 - 09:20
I'm not very sure if it really work but I think it can be improved.
http://tinyurl.com/236wvob
Peter Galiba (not verified) - Thu, 11/11/2010 - 11:18
Peter Galiba (not verified) - Thu, 11/11/2010 - 11:27
Peter Galiba (not verified) - Thu, 11/11/2010 - 12:04
Ian (not verified) - Thu, 11/11/2010 - 16:54
it should get better performance when n is much greater.
http://tinyurl.com/2f77fq5
Ian (not verified) - Thu, 11/11/2010 - 17:49
http://tinyurl.com/2wr669r
I think it should start from 0 not from 1 as is in the test.
azendal (not verified) - Thu, 11/11/2010 - 18:02
based on adrian's idea
http://tinyurl.com/29nzawf
Ian (not verified) - Thu, 11/11/2010 - 18:59
107.4k
http://tinyurl.com/278l9xu
Christian Harms - Thu, 11/11/2010 - 19:42
WOW, you got a better speedup than my own solution (not published yet) with this code. I will put all solutions together for better comparing next week.
Ian (not verified) - Thu, 11/11/2010 - 21:17
my best for firefox
http://tinyurl.com/2dzuyev
MSM (not verified) - Thu, 11/11/2010 - 21:23
I'm laughing, now that I have the tests running. My recursive solutions above are dreadfully slow. I'm sure the array concatenation is not helping performance either.
Ian (not verified) - Sat, 11/13/2010 - 23:44
different aproach, but almost the same speed
forfeit game - ALGOL 60 solution from 1964 | united-coders.c (not verified) - Mon, 11/15/2010 - 16:46
[...] found two ALGOL sniplets for the forfeit game contest. My ALGOL 60 programming book (happy birthday for the ALGOL programming language) offer for every [...]
Anonymous (not verified) - Tue, 11/16/2010 - 10:13
Despite some comments, RegExes are very fast. Just dont't compile them again and again on each iteration :-)
The trick with tis code is that it only sums up the digits on overflow
Matthias Reuter - Tue, 11/16/2010 - 10:57
I had a version limited to n < 1700, from which I derived a recursive variant. Unfortunately, the recursive variant is much slower in IE and FF, so here's a combined approach:
kyungw00k (not verified) - Fri, 11/26/2010 - 04:12
http://tinyurl.com/24y26aa
boxersb (not verified) - Fri, 11/26/2010 - 05:35
I thought that must using deep depth recursive mechanism.. But your results are not.. It's little strange.. :-)
boxersb (not verified) - Fri, 11/26/2010 - 05:42
Javascript algorithm performance : And the winner is ... | u (not verified) - Thu, 12/02/2010 - 11:46
[...] little programming contest was fun and we can learn something about best practices in javascript algorithm performance. I have [...]