Skip to content

Commit

Permalink
new all-your-base exercise (#204)
Browse files Browse the repository at this point in the history
  • Loading branch information
colinleach authored Nov 14, 2023
1 parent 2be7cb8 commit 0b21d77
Show file tree
Hide file tree
Showing 11 changed files with 420 additions and 0 deletions.
9 changes: 9 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
]
},
Expand Down
33 changes: 33 additions & 0 deletions exercises/practice/all-your-base/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -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
31 changes: 31 additions & 0 deletions exercises/practice/all-your-base/.meta/Example.cfc
Original file line number Diff line number Diff line change
@@ -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();
}

}
7 changes: 7 additions & 0 deletions exercises/practice/all-your-base/.meta/ExampleTest.cfc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
component extends="AllYourBaseTest" {

function beforeAll(){
SUT = createObject( 'Solution' );
}

}
15 changes: 15 additions & 0 deletions exercises/practice/all-your-base/.meta/config.json
Original file line number Diff line number Diff line change
@@ -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."
}
67 changes: 67 additions & 0 deletions exercises/practice/all-your-base/.meta/tests.toml
Original file line number Diff line number Diff line change
@@ -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"
10 changes: 10 additions & 0 deletions exercises/practice/all-your-base/AllYourBase.cfc
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/**
* Your implementation of the All Your Base exercise
*/
component {

function rebase(inputBase, digits, outputBase) {
// implement me here
}

}
100 changes: 100 additions & 0 deletions exercises/practice/all-your-base/AllYourBaseTest.cfc
Original file line number Diff line number Diff line change
@@ -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 );
});

});
}
}
103 changes: 103 additions & 0 deletions exercises/practice/all-your-base/TestRunner.cfc
Original file line number Diff line number Diff line change
@@ -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 );
}
}

}

Loading

0 comments on commit 0b21d77

Please sign in to comment.