From 9f91bb8bc2aac827d61b27dde65c41a014682e66 Mon Sep 17 00:00:00 2001 From: Colin Leach Date: Mon, 13 Nov 2023 14:04:37 -0700 Subject: [PATCH] new all-your-base exercise --- config.json | 9 ++ .../all-your-base/.docs/instructions.md | 33 ++++++ .../practice/all-your-base/.meta/Example.cfc | 31 ++++++ .../all-your-base/.meta/ExampleTest.cfc | 7 ++ .../practice/all-your-base/.meta/config.json | 15 +++ .../practice/all-your-base/.meta/tests.toml | 67 ++++++++++++ .../practice/all-your-base/AllYourBase.cfc | 10 ++ .../all-your-base/AllYourBaseTest.cfc | 100 +++++++++++++++++ .../practice/all-your-base/TestRunner.cfc | 103 ++++++++++++++++++ exercises/practice/all-your-base/box.json | 8 ++ exercises/practice/all-your-base/index.cfm | 37 +++++++ 11 files changed, 420 insertions(+) create mode 100644 exercises/practice/all-your-base/.docs/instructions.md create mode 100644 exercises/practice/all-your-base/.meta/Example.cfc create mode 100644 exercises/practice/all-your-base/.meta/ExampleTest.cfc create mode 100644 exercises/practice/all-your-base/.meta/config.json create mode 100644 exercises/practice/all-your-base/.meta/tests.toml create mode 100644 exercises/practice/all-your-base/AllYourBase.cfc create mode 100644 exercises/practice/all-your-base/AllYourBaseTest.cfc create mode 100644 exercises/practice/all-your-base/TestRunner.cfc create mode 100644 exercises/practice/all-your-base/box.json create mode 100644 exercises/practice/all-your-base/index.cfm diff --git a/config.json b/config.json index e86d5ee..21e3894 100644 --- a/config.json +++ b/config.json @@ -312,6 +312,15 @@ "prerequisites": [], "difficulty": 1, "topics": null + }, + { + "slug": "all-your-base", + "name": "All Your Base", + "uuid": "acb1791a-3e69-404a-8095-8961e231b0de", + "practices": [], + "prerequisites": [], + "difficulty": 1, + "topics": null } ] }, diff --git a/exercises/practice/all-your-base/.docs/instructions.md b/exercises/practice/all-your-base/.docs/instructions.md new file mode 100644 index 0000000..fc3b851 --- /dev/null +++ b/exercises/practice/all-your-base/.docs/instructions.md @@ -0,0 +1,33 @@ +# Instructions + +Convert a number, represented as a sequence of digits in one base, to any other base. + +Implement general base conversion. +Given a number in base **a**, represented as a sequence of digits, convert it to base **b**. + +## Note + +- Try to implement the conversion yourself. + Do not use something else to perform the conversion for you. + +## About [Positional Notation][positional-notation] + +In positional notation, a number in base **b** can be understood as a linear combination of powers of **b**. + +The number 42, _in base 10_, means: + +`(4 * 10^1) + (2 * 10^0)` + +The number 101010, _in base 2_, means: + +`(1 * 2^5) + (0 * 2^4) + (1 * 2^3) + (0 * 2^2) + (1 * 2^1) + (0 * 2^0)` + +The number 1120, _in base 3_, means: + +`(1 * 3^3) + (1 * 3^2) + (2 * 3^1) + (0 * 3^0)` + +I think you got the idea! + +_Yes. Those three numbers above are exactly the same. Congratulations!_ + +[positional-notation]: https://en.wikipedia.org/wiki/Positional_notation \ No newline at end of file diff --git a/exercises/practice/all-your-base/.meta/Example.cfc b/exercises/practice/all-your-base/.meta/Example.cfc new file mode 100644 index 0000000..90ff6a5 --- /dev/null +++ b/exercises/practice/all-your-base/.meta/Example.cfc @@ -0,0 +1,31 @@ +/** +* Here is an example solution for the All Your Base exercise +*/ +component { + + function rebase(inputBase, digits, outputBase) { + if ( inputBase < 2 || outputBase < 2 ) { + return -1; + } + + var number = 0; + for ( digit in digits ) { + if ( digit < 0 || digit >= inputBase ) { + return -1; + } + number = number * inputBase + digit; + } + + var result = []; + var digit; + while ( number >= outputBase ) { + digit = number % outputBase; + result.append(digit); + number = (number - digit) / outputBase; + } + result.append(number); + + return result.reverse(); + } + +} \ No newline at end of file diff --git a/exercises/practice/all-your-base/.meta/ExampleTest.cfc b/exercises/practice/all-your-base/.meta/ExampleTest.cfc new file mode 100644 index 0000000..a50310f --- /dev/null +++ b/exercises/practice/all-your-base/.meta/ExampleTest.cfc @@ -0,0 +1,7 @@ +component extends="AllYourBaseTest" { + + function beforeAll(){ + SUT = createObject( 'Solution' ); + } + +} \ No newline at end of file diff --git a/exercises/practice/all-your-base/.meta/config.json b/exercises/practice/all-your-base/.meta/config.json new file mode 100644 index 0000000..081261e --- /dev/null +++ b/exercises/practice/all-your-base/.meta/config.json @@ -0,0 +1,15 @@ +{ + "authors": ["colinleach"], + "files": { + "solution": [ + "AllYourBase.cfc" + ], + "test": [ + "AllYourBaseTest.cfc" + ], + "example": [ + ".meta/Example.cfc" + ] + }, + "blurb": "Convert a number, represented as a sequence of digits in one base, to any other base." +} diff --git a/exercises/practice/all-your-base/.meta/tests.toml b/exercises/practice/all-your-base/.meta/tests.toml new file mode 100644 index 0000000..2b04dd3 --- /dev/null +++ b/exercises/practice/all-your-base/.meta/tests.toml @@ -0,0 +1,67 @@ +# This is an auto-generated file. +# +# Regenerating this file via `configlet sync` will: +# - Recreate every `description` key/value pair +# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications +# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion) +# - Preserve any other key/value pair +# +# As user-added comments (using the # character) will be removed when this file +# is regenerated, comments can be added via a `comment` key. + +[1e9ae1dc-35bd-43ba-aa08-e4b94c20fa37] +description = "ability modifier -> ability modifier for score 3 is -4" + +[cc9bb24e-56b8-4e9e-989d-a0d1a29ebb9c] +description = "ability modifier -> ability modifier for score 4 is -3" + +[5b519fcd-6946-41ee-91fe-34b4f9808326] +description = "ability modifier -> ability modifier for score 5 is -3" + +[dc2913bd-6d7a-402e-b1e2-6d568b1cbe21] +description = "ability modifier -> ability modifier for score 6 is -2" + +[099440f5-0d66-4b1a-8a10-8f3a03cc499f] +description = "ability modifier -> ability modifier for score 7 is -2" + +[cfda6e5c-3489-42f0-b22b-4acb47084df0] +description = "ability modifier -> ability modifier for score 8 is -1" + +[c70f0507-fa7e-4228-8463-858bfbba1754] +description = "ability modifier -> ability modifier for score 9 is -1" + +[6f4e6c88-1cd9-46a0-92b8-db4a99b372f7] +description = "ability modifier -> ability modifier for score 10 is 0" + +[e00d9e5c-63c8-413f-879d-cd9be9697097] +description = "ability modifier -> ability modifier for score 11 is 0" + +[eea06f3c-8de0-45e7-9d9d-b8cab4179715] +description = "ability modifier -> ability modifier for score 12 is +1" + +[9c51f6be-db72-4af7-92ac-b293a02c0dcd] +description = "ability modifier -> ability modifier for score 13 is +1" + +[94053a5d-53b6-4efc-b669-a8b5098f7762] +description = "ability modifier -> ability modifier for score 14 is +2" + +[8c33e7ca-3f9f-4820-8ab3-65f2c9e2f0e2] +description = "ability modifier -> ability modifier for score 15 is +2" + +[c3ec871e-1791-44d0-b3cc-77e5fb4cd33d] +description = "ability modifier -> ability modifier for score 16 is +3" + +[3d053cee-2888-4616-b9fd-602a3b1efff4] +description = "ability modifier -> ability modifier for score 17 is +3" + +[bafd997a-e852-4e56-9f65-14b60261faee] +description = "ability modifier -> ability modifier for score 18 is +4" + +[4f28f19c-2e47-4453-a46a-c0d365259c14] +description = "random ability is within range" + +[385d7e72-864f-4e88-8279-81a7d75b04ad] +description = "random character is valid" + +[2ca77b9b-c099-46c3-a02c-0d0f68ffa0fe] +description = "each ability is only calculated once" diff --git a/exercises/practice/all-your-base/AllYourBase.cfc b/exercises/practice/all-your-base/AllYourBase.cfc new file mode 100644 index 0000000..f57e707 --- /dev/null +++ b/exercises/practice/all-your-base/AllYourBase.cfc @@ -0,0 +1,10 @@ +/** +* Your implementation of the All Your Base exercise +*/ +component { + + function rebase(inputBase, digits, outputBase) { + // implement me here + } + +} \ No newline at end of file diff --git a/exercises/practice/all-your-base/AllYourBaseTest.cfc b/exercises/practice/all-your-base/AllYourBaseTest.cfc new file mode 100644 index 0000000..1794364 --- /dev/null +++ b/exercises/practice/all-your-base/AllYourBaseTest.cfc @@ -0,0 +1,100 @@ +component extends="testbox.system.BaseSpec" { + + function beforeAll(){ + SUT = createObject( 'AllYourBase' ); + WriteDump(SUT); + } + + function run(){ + + describe( "My AllYourBase class", function(){ + + it( 'single bit one to decimal', function(){ + expect( SUT.rebase( inputBase=2, digits=[1], outputBase=10 ) ).toBe( [1] ); + }); + + it( 'binary to single decimal', function(){ + expect( SUT.rebase( inputBase=2, digits=[1, 0, 1], outputBase=10 ) ).toBe( [5] ); + }); + + it( 'single decimal to binary', function(){ + expect( SUT.rebase( inputBase=10, digits=[5], outputBase=2 ) ).toBe( [1, 0, 1] ); + }); + + it( 'binary to multiple decimal', function(){ + expect( SUT.rebase( inputBase=2, digits=[1, 0, 1, 0, 1, 0], outputBase=10 ) ).toBe( [4, 2] ); + }); + + it( 'decimal to binary', function(){ + expect( SUT.rebase( inputBase=10, digits=[4, 2], outputBase=2 ) ).toBe( [1, 0, 1, 0, 1, 0] ); + }); + + it( 'trinary to hexadecimal', function(){ + expect( SUT.rebase( inputBase=3, digits=[1, 1, 2, 0], outputBase=16 ) ).toBe( [2, 10] ); + }); + + it( 'hexadecimal to trinary', function(){ + expect( SUT.rebase( inputBase=16, digits=[2, 10], outputBase=3 ) ).toBe( [1, 1, 2, 0] ); + }); + + it( '15-bit integer', function(){ + expect( SUT.rebase( inputBase=97, digits=[3, 46, 60], outputBase=73 ) ).toBe( [6, 10, 45] ); + }); + + it( 'empty list', function(){ + expect( SUT.rebase( inputBase=2, digits=[], outputBase=10 ) ).toBe( [0] ); + }); + + it( 'single zero', function(){ + expect( SUT.rebase( inputBase=10, digits=[0], outputBase=2 ) ).toBe( [0] ); + }); + + it( 'multiple zeros', function(){ + expect( SUT.rebase( inputBase=10, digits=[0, 0, 0], outputBase=2 ) ).toBe( [0] ); + }); + + it( 'leading zeros', function(){ + expect( SUT.rebase( inputBase=7, digits=[0, 6, 0], outputBase=10 ) ).toBe( [4, 2] ); + }); + + // input base must be >= 2 + it( 'input base is one', function(){ + expect( SUT.rebase( inputBase=1, digits=[0], outputBase=10 ) ).toBe( -1 ); + }); + + it( 'input base is zero', function(){ + expect( SUT.rebase( inputBase=0, digits=[], outputBase=10 ) ).toBe( -1 ); + }); + + it( 'input base is negative', function(){ + expect( SUT.rebase( inputBase=-2, digits=[1], outputBase=10 ) ).toBe( -1 ); + }); + + // all digits must satisfy 0 <= d < input base + it( 'negative digit', function(){ + expect( SUT.rebase( inputBase=2, digits=[1, -1, 1, 0, 1, 0], outputBase=10 ) ).toBe( -1 ); + }); + + it( 'invalid positive digit', function(){ + expect( SUT.rebase( inputBase=2, digits=[1, 2, 1, 0, 1, 0], outputBase=10 ) ).toBe( -1 ); + }); + + it( 'output base is one', function(){ + expect( SUT.rebase( inputBase=2, digits=[1, 0, 1, 0, 1, 0], outputBase=1 ) ).toBe( -1 ); + }); + + it( 'output base is zero', function(){ + expect( SUT.rebase( inputBase=10, digits=[7], outputBase=0 ) ).toBe( -1 ); + }); + + it( 'output base is negative', function(){ + expect( SUT.rebase( inputBase=2, digits=[1], outputBase=-7 ) ).toBe( -1 ); + }); + + it( 'both bases are negative', function(){ + expect( SUT.rebase( inputBase=-2, digits=[1], outputBase=-7 ) ).toBe( -1 ); + }); + + }); + } +} diff --git a/exercises/practice/all-your-base/TestRunner.cfc b/exercises/practice/all-your-base/TestRunner.cfc new file mode 100644 index 0000000..762b153 --- /dev/null +++ b/exercises/practice/all-your-base/TestRunner.cfc @@ -0,0 +1,103 @@ +/** +* I am a CommandBox task runner which you can use to test your implementation of this exercise against the +* provided test suite. To use me, open the CommandBox CLI and run this: +* +* CommandBox> task run TestRunner +* +* To start up a test watcher that will automatically rerun the test suite every time you save a file change, run this: +* +* CommandBox> task run TestRunner --watcher +* +*/ +component { + + /** + * @solution Runs the tests against the solution + * @watcher Start up a file watch that re-runs the tests on file changes. Use Ctrl-C to stop + */ + function run( boolean solution=false, boolean watcher=false ) { + + ensureTestBox(); + + if( watcher ) { + + // Tabula rasa + command( 'cls' ).run(); + + // Start watcher + watch() + .paths( '*.cfc' ) + .inDirectory( getCWD() ) + .withDelay( 500 ) + .onChange( function() { + + // Clear the screen + command( 'cls' ) + .run(); + + // This is neccessary so changes to tests get picked up right away. + pagePoolClear(); + + runTests( solution ); + + } ) + .start(); + + } else { + runTests( solution ); + } + + } + + /** + * Make sure the TestBox framework is installed + */ + private function ensureTestBox() { + var excerciseRoot = getCWD(); + var testBoxRoot = excerciseRoot & '/testbox'; + + if( !directoryExists( testBoxRoot ) ) { + + print.boldYellowLine( 'Installing some missing dependencies for you!' ).toConsole(); + command( 'install' ) + .inWorkingDirectory( excerciseRoot ) + .run(); + } + + // Bootstrap TestBox framework + filesystemUtil.createMapping( '/testbox', testBoxRoot ); + } + + /** + * Invoke TestBox to run the test suite + */ + private function runTests( boolean solution=false ) { + + // Create TestBox and run the tests + testData = new testbox.system.TestBox() + .runRaw( directory = { + // Find all CFCs... + mapping = filesystemUtil.makePathRelative( getCWD() ), + // ... in this directory ... + recurse = false, + // ... whose name ends in "test" + filter = function( path ) { + return path.reFind( ( solution ? 'Solution' : '' ) & 'Test.cfc$' ); + } + } ) + .getMemento(); + + // Print out the results with ANSI formatting for the CLI + getInstance( 'CLIRenderer@testbox-commands' ) + .render( print, testData, true ); + + print.toConsole(); + + // Set proper exit code + if( testData.totalFail || testData.totalError ) { + setExitCode( 1 ); + } + } + +} + diff --git a/exercises/practice/all-your-base/box.json b/exercises/practice/all-your-base/box.json new file mode 100644 index 0000000..3b95f7e --- /dev/null +++ b/exercises/practice/all-your-base/box.json @@ -0,0 +1,8 @@ +{ + "dependencies":{ + "testbox":"^2.5.0+107" + }, + "installPaths":{ + "testbox":"testbox/" + } +} \ No newline at end of file diff --git a/exercises/practice/all-your-base/index.cfm b/exercises/practice/all-your-base/index.cfm new file mode 100644 index 0000000..1e35079 --- /dev/null +++ b/exercises/practice/all-your-base/index.cfm @@ -0,0 +1,37 @@ + + + + + // get a list of all CFCs in this folder whose name looks like "XXXTest.cfc" + // And turn it into compnent path relative to the web root + url.bundles = directoryList( + path=expandPath( '/' ), + filter='*Test.cfc' ) + .map( function( path ) { + return path + .replaceNoCase( expandPath( '/' ), '' ) + .left( -4 ) + } ) + .toList(); + + + + + + + + Oops, you don't have TestBox installed yet! Please run box install from the root of your excercise folder and refresh this page. +